Qt音視頻開發(fā)之通用監(jiān)控布局控件的實現
一、前言
自從做監(jiān)控系統(tǒng)以來,就一直有打算將這個功能獨立出來一個類,這樣的話很多系統(tǒng)用到此類布局切換,通用這個類就行,而且后期此布局會增加其他異形布局,甚至按照16:9之類的比例生成布局,之前此功能直接寫在功能界面UI類中,不方便拓展,好多個系統(tǒng)用到此功能,一旦增加了64布局、128通道布局等,都需要做對應更改,煩不勝煩,所以務必將此功能徹底剝離出來,為后面的256通道、異形布局、自定義布局打基礎。
通道切換在監(jiān)控系統(tǒng)中是最基礎的必備功能,一般都會提供1通道、4通道、6通道、8通道、9通道、16通道這幾個通道切換,可能做得比較好的還會提供24通道、32通道、64通道的,這個可能對電腦的配置就有一定要求了,一般來說,超過9個通道實時顯示視頻流,基本上會采用子碼流來顯示,如果都采用主碼流,電腦壓力非常巨大,CPU占用很高,內存也高,不過現在的電腦配置越來越高,基本上四千多塊錢的臺式機,配置已經非常好了,顯示個16個通道的實時視頻毫無壓力。
Qt中的布局非常好用,尤其是QGridLayout表格布局,可以指定行列放置控件,而且還可以設置每個控件占用幾行幾列,這樣就可以完美的實現各種通道布局了。比如6通道,可以設置通道1占用兩行兩列,其他通道各站一行一列即可,當切換布局的時候,其他隱藏即可。
主要功能:
- 將所有通道切換處理全部集中到一個類。
- 通用整數倍數布局切換函數,可方便拓展到100、255通道等。
- 通用異形布局切換函數,可以參考進行自定義異形布局。
- 通道布局切換發(fā)出信號通知。
- 可控每種布局切換菜單是否啟用。
- 支持自定義子菜單布局內容。
- 支持設置對應的菜單標識比如默認的通道字樣改成設備。
本控件開源,開源地址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等。
- 本地音視頻文件和網絡音視頻文件,自動識別文件長度、播放進度、音量大小、靜音狀態(tài)等。
- 文件可以指定播放位置、調節(jié)音量大小、設置靜音狀態(tài)等。
- 支持倍速播放文件,可選0.5倍、1.0倍、2.5倍、5.0倍等速度,相當于慢放和快放。
- 支持開始播放、停止播放、暫停播放、繼續(xù)播放。
- 支持抓拍截圖,可指定文件路徑,可選抓拍完成是否自動顯示預覽。
- 支持錄像存儲,手動開始錄像、停止錄像,部分內核支持暫停錄像后繼續(xù)錄像,跳過不需要錄像的部分。
- 支持無感知切換循環(huán)播放、自動重連等機制。
- 提供播放成功、播放完成、收到解碼圖片、收到抓拍圖片、視頻尺寸變化、錄像狀態(tài)變化等信號。
- 多線程處理,一個解碼一個線程,不卡主界面。
5.2 特色功能
- 同時支持多種解碼內核,包括qmedia內核(Qt4/Qt5/Qt6)、ffmpeg內核(ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5)、vlc內核(vlc2/vlc3)、mpv內核(mpv1/mp2)、??祍dk、easyplayer內核等。
- 非常完善的多重基類設計,新增一種解碼內核只需要實現極少的代碼量,就可以應用整套機制。
- 同時支持多種畫面顯示策略,自動調整(原始分辨率小于顯示控件尺寸則按照原始分辨率大小顯示,否則等比例縮放)、等比例縮放(永遠等比例縮放)、拉伸填充(永遠拉伸填充)。所有內核和所有視頻顯示模式下都支持三種畫面顯示策略。
- 同時支持多種視頻顯示模式,句柄模式(傳入控件句柄交給對方繪制控制)、繪制模式(回調拿到數據后轉成QImage用QPainter繪制)、GPU模式(回調拿到數據后轉成yuv用QOpenglWidget繪制)。
- 支持多種硬件加速類型,ffmpeg可選dxva2、d3d11va等,mpv可選auto、dxva2、d3d11va,vlc可選any、dxva2、d3d11va。不同的系統(tǒng)環(huán)境有不同的類型選擇,比如linux系統(tǒng)有vaapi、vdpau,macos系統(tǒng)有videotoolbox。
- 解碼線程和顯示窗體分離,可指定任意解碼內核掛載到任意顯示窗體,動態(tài)切換。
- 支持共享解碼線程,默認開啟并且自動處理,當識別到相同的視頻地址,共享一個解碼線程,在網絡視頻環(huán)境中可以大大節(jié)約網絡流量以及對方設備的推流壓力。國內頂尖視頻廠商均采用此策略。這樣只要拉一路視頻流就可以共享到幾十個幾百個通道展示。
- 自動識別視頻旋轉角度并繪制,比如手機上拍攝的視頻一般是旋轉了90度的,播放的時候要自動旋轉處理,不然默認是倒著的。
- 自動識別視頻流播放過程中分辨率的變化,在視頻控件上自動調整尺寸。比如攝像機可以在使用過程中動態(tài)配置分辨率,當分辨率改動后對應視頻控件也要做出同步反應。
- 音視頻文件無感知自動切換循環(huán)播放,不會出現切換期間黑屏等肉眼可見的切換痕跡。
- 視頻控件同時支持任意解碼內核、任意畫面顯示策略、任意視頻顯示模式。
- 視頻控件懸浮條同時支持句柄、繪制、GPU三種模式,非絕對坐標移來移去。
- 本地攝像頭設備支持指定設備名稱、分辨率、幀率進行播放。
- 錄像文件同時支持打開的視頻文件、本地攝像頭、網絡視頻流等。
- 瞬間響應打開和關閉,無論是打開不存在的視頻或者網絡流,探測設備是否存在,讀取中的超時等待,收到關閉指令立即中斷之前的操作并響應。
- 支持打開各種圖片文件,支持本地音視頻文件拖曳播放。
- 視頻控件懸浮條自帶開始和停止錄像切換、聲音靜音切換、抓拍截圖、關閉視頻等功能。
- 音頻組件支持聲音波形值數據解析,可以根據該值繪制波形曲線和柱狀聲音條,默認提供了聲音振幅信號。
- 各組件中極其詳細的打印信息提示,尤其是報錯信息提示,封裝的統(tǒng)一打印格式。針對現場復雜的設備環(huán)境測試極其方便有用,相當于精確定位到具體哪個通道哪個步驟出錯。
- 代碼框架和結構優(yōu)化到最優(yōu),性能強悍,持續(xù)迭代更新升級。
- 源碼支持Qt4、Qt5、Qt6,兼容所有版本。
5.3 視頻控件
- 可動態(tài)添加任意多個osd標簽信息,標簽信息包括名字、是否可見、字號大小、文本文字、文本顏色、標簽圖片、標簽坐標、標簽格式(文本、日期、時間、日期時間、圖片)、標簽位置(左上角、左下角、右上角、右下角、居中、自定義坐標)。
- 可動態(tài)添加任意多個圖形信息,這個非常有用,比如人工智能算法解析后的圖形區(qū)域信息直接發(fā)給視頻控件即可。圖形信息支持任意形狀,直接繪制在原始圖片上,采用絕對坐標。
- 圖形信息包括名字、邊框大小、邊框顏色、背景顏色、矩形區(qū)域、路徑集合、點坐標集合等。
- 每個圖形信息都可指定三種區(qū)域中的一種或者多種,指定了的都會繪制。
- 內置懸浮條控件,懸浮條位置支持頂部、底部、左側、右側。
- 懸浮條控件參數包括邊距、間距、背景透明度、背景顏色、文本顏色、按下顏色、位置、按鈕圖標代碼集合、按鈕名稱標識集合、按鈕提示信息集合。
- 懸浮條控件一排工具按鈕可自定義,通過結構體參數設置,圖標可選圖形字體還是自定義圖片。
- 懸浮條按鈕內部實現了錄像切換、抓拍截圖、靜音切換、關閉視頻等功能,也可以自行在源碼中增加自己對應的功能。
- 懸浮條按鈕對應實現了功能的按鈕,有對應圖標切換處理,比如錄像按鈕按下后會切換到正在錄像中的圖標,聲音按鈕切換后變成靜音圖標,再次切換還原。
- 懸浮條按鈕單擊后都用名稱唯一標識作為信號發(fā)出,可以自行關聯響應處理。
- 懸浮條空白區(qū)域可以顯示提示信息,默認顯示當前視頻分辨率大小,可以增加幀率、碼流大小等信息。
- 視頻控件參數包括邊框大小、邊框顏色、焦點顏色、背景顏色(默認透明)、文字顏色(默認全局文字顏色)、填充顏色(視頻外的空白處填充黑色)、背景文字、背景圖片(如果設置了圖片優(yōu)先取圖片)、是否拷貝圖片、縮放顯示模式(自動調整、等比例縮放、拉伸填充)、視頻顯示模式(句柄、繪制、GPU)、啟用懸浮條、懸浮條尺寸(橫向為高度、縱向為寬度)、懸浮條位置(頂部、底部、左側、右側)。
5.4 內核ffmpeg
- 支持各種音視頻文件、本地攝像頭設備,各種視頻流網絡流。
- 支持開始播放、暫停播放、繼續(xù)播放、停止播放、設置播放進度、倍速播放。
- 可設置音量、靜音切換、抓拍圖片、錄像存儲。
- 自動提取專輯信息比如標題、藝術家、專輯、專輯封面,自動顯示專輯封面。
- 完美支持音視頻同步和倍速播放。
- 解碼策略支持速度優(yōu)先、質量優(yōu)先、均衡處理、最快速度。
- 支持手機視頻旋轉角度顯示,比如一般手機拍攝的視頻是旋轉了90度的,解碼顯示的時候需要重新旋轉90度才是正的。
- 自動轉換yuv420格式,比如本地攝像頭是yuyv422格式,有些視頻文件是xx格式,統(tǒng)一將非yuv420格式轉換,然后再進行處理。
- 支持硬解碼dxva2、d3d11va等,性能極高尤其是大分辨率比如4K視頻。
- 視頻響應極低延遲0.2s左右,極速響應打開視頻流0.5s左右,專門做了優(yōu)化處理。
- 硬解碼和GPU繪制組合,極低CPU占用,比??荡笕A等客戶端更優(yōu)。
- 支持視頻流中的各種音頻格式,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編碼(現在越來越多的監(jiān)控攝像頭是H265視頻流格式)生成視頻文件,內部自動識別切換編碼格式。
- 自動識別視頻流動態(tài)分辨率改動,重新打開視頻流。
- 支持用戶信息中包含特殊字符(比如用戶信息中包含+#@等字符)的視頻流播放,內置解析轉義處理。
- 純qt+ffmpeg解碼,非sdl等第三方繪制播放依賴,gpu繪制采用qopenglwidget,音頻播放采用qaudiooutput。
- 同時支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5版本,全部做了兼容處理。如果需要支持xp需要選用ffmpeg3及以下。
以上就是Qt音視頻開發(fā)之通用監(jiān)控布局控件的實現的詳細內容,更多關于Qt監(jiān)控布局控件的資料請關注腳本之家其它相關文章!
相關文章
C++實現LeetCode(157.用Read4來讀取N個字符)
這篇文章主要介紹了C++實現LeetCode(157.用Read4來讀取N個字符),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-07-07

