针对监控摄像机(海康、大华等)进行手动录像的功能设计
监控摄像机(海康、大华等)进行视频播放时一般需要叠加显示区域线、点、文字等信息,并且这些信息可能随时根据用户需要或视频内容手动或自动调整而发生变化。
这时如果用户需要回放或公司需要查看客户那边的效果,只是使用录屏软件录制或者使用手机等录像设备录屏,效果很难理想的。
这就需要自己保存一个录像,包括视频信息和叠加信息。
录像功能设计思路:
之前文章:
可以使用海思解码库解码h264帧,所以自定义录像文件里可以保存h264帧数据。
自定义文件设计为:
前4字节存储int型的h264帧数组长度,然后存储叠加信息,比如自定义500字节,可以留一些保留字节,然后存储h264帧数据
这样每一帧的数据长度为4+500+h264帧数组长度
需要注意:如果长时间录像,要考虑视频文件的分段问题。1080p流一般一小时2G+容量,
太大可能导致播放时获取视频长度时间过久,另外可能有磁盘单文件容量限制。
为什么不保存解码后的yuv甚至叠加信息画好后的bmp数据?
一帧yuv的数据大小1920*1080*1.5,约3M一帧1080p的
一帧bmp的数据大小1920*1080*3,约6M一帧1080p的
25帧每秒时需要75-150M每秒的磁盘写入速度,机械硬盘基本没这速度。
如果压缩为jpg或png,视频图像如果压缩率过低则容量减少有限,压缩率过高则大屏播放录像时效果很差
简单示例代码如下:
path是视频保存文件夹,具体逻辑控制代码不在下面显示代码范畴。
if (!File.Exists(path)) { FileStream fstemp = File.Create(path); fstemp.Close(); fstemp.Dispose(); } if (File.Exists(path)) { fs = new FileStream(path, FileMode.Append); bRecord = true; iRecordFrameCnt = 0; } else { MessageBox.Show("初始化视频文件失败!" + path); }
pStreamData为h264帧的指针,因为C++的接口一般传指针过来,需要先复制到数组内
frameLen为h264帧数据的长度。
磁盘不足参考:C# 获取磁盘剩余空间
private void RecordFrames(IntPtr pStreamData, uint frameLen) { string AppPath = Application.StartupPath.ToString(); string volume = AppPath.Substring(0, AppPath.IndexOf(':')); long freespace = GetHardDiskSpace(volume); if (freespace < 50) { VideoStop(); log.ErrorFormat("磁盘空间不足50MB,停止录像!");return; } iRecordFrameCnt++; try { byte[] buffer = new byte[frameLen]; Marshal.Copy(pStreamData, buffer, 0, (int)frameLen); byte[] intdata = BitConverter.GetBytes((int)frameLen); fs.Write(intdata, 0, intdata.Length); fs.Write(buffer, 0, (int)frameLen); byte[] datas = Recorddata(); //保存叠加数据,自由保存,不做说明 fs.Write(datas, 0, datas.Length); if (iRecordFrameCnt == videoLength) //按照设定时长分割录像文件,太长则录像文件过大(1小时2.5G),不好播放 { fs.Close(); fs.Dispose(); fs = null; if (xjNowNode != null && bPlay) { string path = Application.StartupPath; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } path = path + "\\" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".dat"; if (!File.Exists(path)) { FileStream fstemp = File.Create(path); fstemp.Close(); fstemp.Dispose(); } if (File.Exists(path)) { fs = new FileStream(path, FileMode.Append); iRecordFrameCnt = 0; } } } } catch (System.Exception ex) { if (ex.Message.IndexOf("磁盘空间不足") >= 0) { VideoStop(); log.ErrorFormat("磁盘空间不足,停止录像!"); } else { log.ErrorFormat("录像出错:" + ex.Message); } } }