针对监控摄像机(海康、大华等)录像 .h264 文件的流媒体播放设计
监控摄像机(海康、大华等)内部带的录像功能一般录制的是h264文件,这种文件格式简单的把每一帧h264字节数据保存到文件里。
实际使用中,可能需要对特定录像进行反复检测,以训练、改进视频检测算法的准确性。
这里介绍两种方法。
1.使用RTSP流
服务端1:live555 使用C++代码开发容易些,网上代码较多
服务端2:vlc播放器,参考 使用vlc播放器做rtsp服务器
客户端:使用vlc播放器播放rtsp视频
2.自定义格式
使用UDP数据包
服务端针对.h264视频一帧帧发送给客户端,使用UDP协议
h264帧,尤其是I帧大小可能超过64K,即udp数据包的上限,需要考虑分包
分包:可以在第一字节=0表示不分包,=1表示分包这是第一包,=2这是第二包
视频清晰度暂定为480P,此时P帧一般小于64k,而I帧部分大于64k。这样可以不考虑数据包会先发后到导致乱序
客户端:
示例代码如下
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 6000); udpsockVideo = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpsockVideo.ReceiveBufferSize = 1000000; try { //绑定网络地址 udpsockVideo.Bind(ipep); IPAddress ip = IPAddress.Any; //得到客户机IP IPEndPoint sender = new IPEndPoint(ip, 0); EndPoint Remote = (EndPoint)(sender); log.ErrorFormat("udp开始接收视频帧"); bool bIframe = false; unAvailableCnt = 0; while (!bAbortvehicleEventThread) { if (udpsockVideo == null) { log.ErrorFormat("udp接收视频帧 Socket 为空!"); return; } if (udpsockVideo.Available <= 0) { unAvailableCnt++; if (unAvailableCnt > 10000) { log.ErrorFormat("udp接收视频帧:连续未接收到视频"); //实际比10秒多 unAvailableCnt = 0; } //TimeSpan ts = new TimeSpan(100000);//0.1毫秒 1 ticks =十亿分之一秒 百万分之一毫秒 千分之一微秒,1纳秒 Thread.Sleep(1); //sleep 1时,由于精度问题,经常耗时10-16毫秒 continue; } byte[] data = new byte[100000]; int recv = udpsockVideo.Receive(data); if (recv < 4) { continue; } if (ip != IPAddress.Any && (Remote as IPEndPoint).Address.ToString().Trim() != ip.ToString().Trim()) { continue; } unAvailableCnt = 0; try { if (data[0] != 0x00) { bIframe = true; byte b = data[0]; if (b == 0x01) { iframe1 = new byte[recv - 2]; Array.Copy(data, 2, iframe1, 0, recv - 2); } log.ErrorFormat(b.ToString()); if (b == 0x02) { iframe2 = new byte[recv - 1]; Array.Copy(data, 1, iframe2, 0, recv - 1); log.ErrorFormat(iframe2.Length.ToString()); } if (iframe1.Length < 100000 && iframe2.Length < 100000) { int iframeLen = iframe1.Length + iframe2.Length; byte[] Iframe = new byte[iframeLen]; Array.Copy(iframe1, 0, Iframe, 0, iframe1.Length); Array.Copy(iframe2, 0, Iframe, iframe1.Length, iframe2.Length); DecH264Frame(Iframe); iframe1 = new byte[100000]; iframe2 = new byte[100000]; bIframe = false; } } else { if (bIframe) { bIframe = false; iframe1 = new byte[100000]; iframe2 = new byte[100000]; } byte[] frame = new byte[recv - 1]; Array.Copy(data, 1, frame, 0, recv - 1); DecH264Frame(frame); } } catch (System.Exception ex) { log.ErrorFormat("udp接收视频帧 解码/录像出错:" + ex.Message); } } } catch (Exception e) { log.ErrorFormat("udp接收视频帧出错。端口:{0},出错信息:{1}", 6000, e.Message); } finally { if (udpsockVideo != null) { udpsockVideo.Close(); } }