Qt音視頻開發之通用監控佈局控件的實現
一、前言
自從做監控系統以來,就一直有打算將這個功能獨立出來一個類,這樣的話很多系統用到此類佈局切換,通用這個類就行,而且後期此佈局會增加其他異形佈局,甚至按照16:9之類的比例生成佈局,之前此功能直接寫在功能界面UI類中,不方便拓展,好多個系統用到此功能,一旦增加瞭64佈局、128通道佈局等,都需要做對應更改,煩不勝煩,所以務必將此功能徹底剝離出來,為後面的256通道、異形佈局、自定義佈局打基礎。
通道切換在監控系統中是最基礎的必備功能,一般都會提供1通道、4通道、6通道、8通道、9通道、16通道這幾個通道切換,可能做得比較好的還會提供24通道、32通道、64通道的,這個可能對電腦的配置就有一定要求瞭,一般來說,超過9個通道實時顯示視頻流,基本上會采用子碼流來顯示,如果都采用主碼流,電腦壓力非常巨大,CPU占用很高,內存也高,不過現在的電腦配置越來越高,基本上四千多塊錢的臺式機,配置已經非常好瞭,顯示個16個通道的實時視頻毫無壓力。
Qt中的佈局非常好用,尤其是QGridLayout表格佈局,可以指定行列放置控件,而且還可以設置每個控件占用幾行幾列,這樣就可以完美的實現各種通道佈局瞭。比如6通道,可以設置通道1占用兩行兩列,其他通道各站一行一列即可,當切換佈局的時候,其他隱藏即可。
主要功能:
- 將所有通道切換處理全部集中到一個類。
- 通用整數倍數佈局切換函數,可方便拓展到100、255通道等。
- 通用異形佈局切換函數,可以參考進行自定義異形佈局。
- 通道佈局切換發出信號通知。
- 可控每種佈局切換菜單是否啟用。
- 支持自定義子菜單佈局內容。
- 支持設置對應的菜單標識比如默認的通道字樣改成設備。
本控件開源,開源地址https://gitee.com/feiyangqingyun/QWidgetDemo/tree/master/video/videobox
二、效果圖
三、體驗地址
國內站點:https://gitee.com/feiyangqingyun
國際站點:https://github.com/feiyangqingyun
體驗地址:https://pan.baidu.com/s/1YOVD8nkoOSYwX9KgSauLeQ 提取碼:kcgz 文件名:bin_video_demo/bin_linux_video。
四、相關代碼
void VideoBox::change_video_normal(int index, int flag) { //首先隱藏所有通道 hide_video_all(); int size = 0; int row = 0; int column = 0; //行列數一致的比如 2*2 3*4 4*4 5*5 等可以直接套用通用的公式 //按照這個函數還可以非常容易的拓展出 10*10 16*16=256 通道界面 for (int i = 0; i < videoCount; ++i) { if (i >= index) { //添加到對應佈局並設置可見 gridLayout->addWidget(widgets.at(i), row, column); widgets.at(i)->setVisible(true); size++; column++; if (column == flag) { row++; column = 0; } } if (size == (flag * flag)) { break; } } } void VideoBox::change_video_custom(int index, int type) { //從開始索引開始往後衍生多少個通道 QList<int> indexs; for (int i = index; i < (index + type); ++i) { indexs << i; } if (type == 6) { change_video_6(indexs); } else if (type == 8) { change_video_8(indexs); } else if (type == 13) { change_video_13(indexs); } } void VideoBox::change_video_6(const QList<int> &indexs) { //過濾防止索引越界 if (indexs.size() < 6) { return; } //首先隱藏所有通道 hide_video_all(); //挨個重新添加到佈局 gridLayout->addWidget(widgets.at(indexs.at(0)), 0, 0, 2, 2); gridLayout->addWidget(widgets.at(indexs.at(1)), 0, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(2)), 1, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(3)), 2, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(4)), 2, 1, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(5)), 2, 0, 1, 1); //設置通道控件可見 for (int i = indexs.first(); i <= indexs.last(); ++i) { widgets.at(i)->setVisible(true); } } void VideoBox::change_video_8(const QList<int> &indexs) { //過濾防止索引越界 if (indexs.size() < 8) { return; } //首先隱藏所有通道 hide_video_all(); //挨個重新添加到佈局 gridLayout->addWidget(widgets.at(indexs.at(0)), 0, 0, 3, 3); gridLayout->addWidget(widgets.at(indexs.at(1)), 0, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(2)), 1, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(3)), 2, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(4)), 3, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(5)), 3, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(6)), 3, 1, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(7)), 3, 0, 1, 1); //設置通道控件可見 for (int i = indexs.first(); i <= indexs.last(); ++i) { widgets.at(i)->setVisible(true); } } void VideoBox::change_video_13(const QList<int> &indexs) { //過濾防止索引越界 if (indexs.size() < 13) { return; } //首先隱藏所有通道 hide_video_all(); //挨個重新添加到佈局 gridLayout->addWidget(widgets.at(indexs.at(0)), 0, 0, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(1)), 0, 1, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(2)), 0, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(3)), 0, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(4)), 1, 0, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(5)), 2, 0, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(6)), 1, 1, 2, 2); gridLayout->addWidget(widgets.at(indexs.at(7)), 1, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(8)), 2, 3, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(9)), 3, 0, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(10)), 3, 1, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(11)), 3, 2, 1, 1); gridLayout->addWidget(widgets.at(indexs.at(12)), 3, 3, 1, 1); //設置通道控件可見 for (int i = indexs.first(); i <= indexs.last(); ++i) { widgets.at(i)->setVisible(true); } } void VideoBox::change_video_1(int index) { //首先隱藏所有通道 hide_video_all(); //添加通道到佈局 gridLayout->addWidget(widgets.at(index), 0, 0); //設置可見 widgets.at(index)->setVisible(true); } void VideoBox::change_video_4(int index) { change_video_normal(index, 2); } void VideoBox::change_video_6(int index) { change_video_custom(index, 6); } void VideoBox::change_video_8(int index) { change_video_custom(index, 8); } void VideoBox::change_video_9(int index) { change_video_normal(index, 3); } void VideoBox::change_video_13(int index) { change_video_custom(index, 13); } void VideoBox::change_video_16(int index) { change_video_normal(index, 4); } void VideoBox::change_video_25(int index) { change_video_normal(index, 5); } void VideoBox::change_video_36(int index) { change_video_normal(index, 6); } void VideoBox::change_video_64(int index) { change_video_normal(index, 8); }
五、功能特點
5.1 基礎功能
- 支持各種音頻視頻文件格式,比如mp3、wav、mp4、asf、rm、rmvb、mkv等。
- 支持本地攝像頭設備,可指定分辨率、幀率。
- 支持各種視頻流格式,比如rtp、rtsp、rtmp、http等。
- 本地音視頻文件和網絡音視頻文件,自動識別文件長度、播放進度、音量大小、靜音狀態等。
- 文件可以指定播放位置、調節音量大小、設置靜音狀態等。
- 支持倍速播放文件,可選0.5倍、1.0倍、2.5倍、5.0倍等速度,相當於慢放和快放。
- 支持開始播放、停止播放、暫停播放、繼續播放。
- 支持抓拍截圖,可指定文件路徑,可選抓拍完成是否自動顯示預覽。
- 支持錄像存儲,手動開始錄像、停止錄像,部分內核支持暫停錄像後繼續錄像,跳過不需要錄像的部分。
- 支持無感知切換循環播放、自動重連等機制。
- 提供播放成功、播放完成、收到解碼圖片、收到抓拍圖片、視頻尺寸變化、錄像狀態變化等信號。
- 多線程處理,一個解碼一個線程,不卡主界面。
5.2 特色功能
- 同時支持多種解碼內核,包括qmedia內核(Qt4/Qt5/Qt6)、ffmpeg內核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc內核(vlc2/vlc3)、mpv內核(mpv1/mp2)、海康sdk、easyplayer內核等。
- 非常完善的多重基類設計,新增一種解碼內核隻需要實現極少的代碼量,就可以應用整套機制。
- 同時支持多種畫面顯示策略,自動調整(原始分辨率小於顯示控件尺寸則按照原始分辨率大小顯示,否則等比例縮放)、等比例縮放(永遠等比例縮放)、拉伸填充(永遠拉伸填充)。所有內核和所有視頻顯示模式下都支持三種畫面顯示策略。
- 同時支持多種視頻顯示模式,句柄模式(傳入控件句柄交給對方繪制控制)、繪制模式(回調拿到數據後轉成QImage用QPainter繪制)、GPU模式(回調拿到數據後轉成yuv用QOpenglWidget繪制)。
- 支持多種硬件加速類型,ffmpeg可選dxva2、d3d11va等,mpv可選auto、dxva2、d3d11va,vlc可選any、dxva2、d3d11va。不同的系統環境有不同的類型選擇,比如linux系統有vaapi、vdpau,macos系統有videotoolbox。
- 解碼線程和顯示窗體分離,可指定任意解碼內核掛載到任意顯示窗體,動態切換。
- 支持共享解碼線程,默認開啟並且自動處理,當識別到相同的視頻地址,共享一個解碼線程,在網絡視頻環境中可以大大節約網絡流量以及對方設備的推流壓力。國內頂尖視頻廠商均采用此策略。這樣隻要拉一路視頻流就可以共享到幾十個幾百個通道展示。
- 自動識別視頻旋轉角度並繪制,比如手機上拍攝的視頻一般是旋轉瞭90度的,播放的時候要自動旋轉處理,不然默認是倒著的。
- 自動識別視頻流播放過程中分辨率的變化,在視頻控件上自動調整尺寸。比如攝像機可以在使用過程中動態配置分辨率,當分辨率改動後對應視頻控件也要做出同步反應。
- 音視頻文件無感知自動切換循環播放,不會出現切換期間黑屏等肉眼可見的切換痕跡。
- 視頻控件同時支持任意解碼內核、任意畫面顯示策略、任意視頻顯示模式。
- 視頻控件懸浮條同時支持句柄、繪制、GPU三種模式,非絕對坐標移來移去。
- 本地攝像頭設備支持指定設備名稱、分辨率、幀率進行播放。
- 錄像文件同時支持打開的視頻文件、本地攝像頭、網絡視頻流等。
- 瞬間響應打開和關閉,無論是打開不存在的視頻或者網絡流,探測設備是否存在,讀取中的超時等待,收到關閉指令立即中斷之前的操作並響應。
- 支持打開各種圖片文件,支持本地音視頻文件拖曳播放。
- 視頻控件懸浮條自帶開始和停止錄像切換、聲音靜音切換、抓拍截圖、關閉視頻等功能。
- 音頻組件支持聲音波形值數據解析,可以根據該值繪制波形曲線和柱狀聲音條,默認提供瞭聲音振幅信號。
- 各組件中極其詳細的打印信息提示,尤其是報錯信息提示,封裝的統一打印格式。針對現場復雜的設備環境測試極其方便有用,相當於精確定位到具體哪個通道哪個步驟出錯。
- 代碼框架和結構優化到最優,性能強悍,持續迭代更新升級。
- 源碼支持Qt4、Qt5、Qt6,兼容所有版本。
5.3 視頻控件
- 可動態添加任意多個osd標簽信息,標簽信息包括名字、是否可見、字號大小、文本文字、文本顏色、標簽圖片、標簽坐標、標簽格式(文本、日期、時間、日期時間、圖片)、標簽位置(左上角、左下角、右上角、右下角、居中、自定義坐標)。
- 可動態添加任意多個圖形信息,這個非常有用,比如人工智能算法解析後的圖形區域信息直接發給視頻控件即可。圖形信息支持任意形狀,直接繪制在原始圖片上,采用絕對坐標。
- 圖形信息包括名字、邊框大小、邊框顏色、背景顏色、矩形區域、路徑集合、點坐標集合等。
- 每個圖形信息都可指定三種區域中的一種或者多種,指定瞭的都會繪制。
- 內置懸浮條控件,懸浮條位置支持頂部、底部、左側、右側。
- 懸浮條控件參數包括邊距、間距、背景透明度、背景顏色、文本顏色、按下顏色、位置、按鈕圖標代碼集合、按鈕名稱標識集合、按鈕提示信息集合。
- 懸浮條控件一排工具按鈕可自定義,通過結構體參數設置,圖標可選圖形字體還是自定義圖片。
- 懸浮條按鈕內部實現瞭錄像切換、抓拍截圖、靜音切換、關閉視頻等功能,也可以自行在源碼中增加自己對應的功能。
- 懸浮條按鈕對應實現瞭功能的按鈕,有對應圖標切換處理,比如錄像按鈕按下後會切換到正在錄像中的圖標,聲音按鈕切換後變成靜音圖標,再次切換還原。
- 懸浮條按鈕單擊後都用名稱唯一標識作為信號發出,可以自行關聯響應處理。
- 懸浮條空白區域可以顯示提示信息,默認顯示當前視頻分辨率大小,可以增加幀率、碼流大小等信息。
- 視頻控件參數包括邊框大小、邊框顏色、焦點顏色、背景顏色(默認透明)、文字顏色(默認全局文字顏色)、填充顏色(視頻外的空白處填充黑色)、背景文字、背景圖片(如果設置瞭圖片優先取圖片)、是否拷貝圖片、縮放顯示模式(自動調整、等比例縮放、拉伸填充)、視頻顯示模式(句柄、繪制、GPU)、啟用懸浮條、懸浮條尺寸(橫向為高度、縱向為寬度)、懸浮條位置(頂部、底部、左側、右側)。
5.4 內核ffmpeg
- 支持各種音視頻文件、本地攝像頭設備,各種視頻流網絡流。
- 支持開始播放、暫停播放、繼續播放、停止播放、設置播放進度、倍速播放。
- 可設置音量、靜音切換、抓拍圖片、錄像存儲。
- 自動提取專輯信息比如標題、藝術傢、專輯、專輯封面,自動顯示專輯封面。
- 完美支持音視頻同步和倍速播放。
- 解碼策略支持速度優先、質量優先、均衡處理、最快速度。
- 支持手機視頻旋轉角度顯示,比如一般手機拍攝的視頻是旋轉瞭90度的,解碼顯示的時候需要重新旋轉90度才是正的。
- 自動轉換yuv420格式,比如本地攝像頭是yuyv422格式,有些視頻文件是xx格式,統一將非yuv420格式轉換,然後再進行處理。
- 支持硬解碼dxva2、d3d11va等,性能極高尤其是大分辨率比如4K視頻。
- 視頻響應極低延遲0.2s左右,極速響應打開視頻流0.5s左右,專門做瞭優化處理。
- 硬解碼和GPU繪制組合,極低CPU占用,比海康大華等客戶端更優。
- 支持視頻流中的各種音頻格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推薦選擇AAC兼容性跨平臺性最好。
- 視頻存儲支持yuv、h264、mp4多種格式,音頻存儲支持pcm、wav、aac多種格式。默認視頻mp4格式、音頻aac格式。
- 支持分開存儲音頻視頻文件,也支持合並到一個mp4文件,默認策略是無論何種音視頻文件格式存儲,最終都轉成mp4及aac格式,然後合並成音視頻一起的mp4文件。
- 支持本地攝像頭實時視頻顯示帶音頻輸入輸出,音視頻錄制合並到一個mp4文件。
- 支持H264/H265編碼(現在越來越多的監控攝像頭是H265視頻流格式)生成視頻文件,內部自動識別切換編碼格式。
- 自動識別視頻流動態分辨率改動,重新打開視頻流。
- 支持用戶信息中包含特殊字符(比如用戶信息中包含+#@等字符)的視頻流播放,內置解析轉義處理。
- 純qt+ffmpeg解碼,非sdl等第三方繪制播放依賴,gpu繪制采用qopenglwidget,音頻播放采用qaudiooutput。
- 同時支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5版本,全部做瞭兼容處理。如果需要支持xp需要選用ffmpeg3及以下。
以上就是Qt音視頻開發之通用監控佈局控件的實現的詳細內容,更多關於Qt監控佈局控件的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- php如何利用ffmpeg獲取視頻第一幀為縮略圖
- Python辦公自動化之Excel(中)
- Python 如何利用ffmpeg 處理視頻素材
- python用moviepy對視頻進行簡單的處理
- Python多個MP4合成視頻的實現方法