天天看點

針對監控錄影機(海康、大華等)進行手動錄像的功能設計

 監控錄影機(海康、大華等)進行視訊播放時一般需要疊加顯示區域線、點、文字等資訊,并且這些資訊可能随時根據使用者需要或視訊内容手動或自動調整而發生變化。

這時如果使用者需要回放或公司需要檢視客戶那邊的效果,隻是使用錄屏軟體錄制或者使用手機等錄像裝置錄屏,效果很難理想的。

這就需要自己儲存一個錄像,包括視訊資訊和疊加資訊。

錄像功能設計思路:

之前文章:

海思h264解碼庫

可以使用海思解碼庫解碼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);
                }
            }
        }