監控攝像機(海康、大華等)進行視頻播放時一般需要疊加顯示區域線、點、文字等信息,並且這些信息可能隨時根據用戶需要或視頻內容手動或自動調整而發生變化。
這時如果用戶需要回放或公司需要查看客戶那邊的效果,只是使用錄屏軟件錄制或者使用手機等錄像設備錄屏,效果很難理想的。
這就需要自己保存一個錄像,包括視頻信息和疊加信息。
錄像功能設計思路:
之前文章:
可以使用海思解碼庫解碼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); } } }