1. 程式人生 > 其它 >VList data structures in C#

VList data structures in C#

尊重原創版權: https://www.gewuweb.com/hot/8802.html

Qt編寫安防視訊監控系統48-視訊引數

## 一、前言

視訊引數之前在基本引數中,後面越來越多,直接獨立了出來,甚至還拆分出來了視訊引數1、視訊引數2,引數越來越多分組也越來越多的時候,你會發現分組名稱都不夠用或者不方便命名,不能直觀的表示該分組的含義,索性直接用1/2/3來定義,比如BaseConfig1、BaseConfig2、VideoConfig1、VideoConfig2等。用這種命名反而更舒服一些,道理是要講得通。

視訊引數更改立即應用也是近期才實現的,之前做的是需要重啟應用,後面發現除了打開了的視訊外,其他引數都可以做到立即應用的,於是將這些引數的設定統一封裝成了一個函式initCommonVideoWidget用於通用初始化視訊控制元件,因為在各個介面的視訊控制元件指標都可以拿到,於是在視訊引數發生變化後,發出訊號,接收訊號後統一呼叫該引數重新初始化視訊控制元件即可。當然需要先過濾下已經開啟視訊的控制元件,如果視訊控制元件正在播放則不設定,設定會造成問題,需要關閉狀態設定。

視訊引數說明

1. 儲存視訊:開啟以後開啟視訊自動儲存錄影檔案,目錄在可執行檔案video_normal。

2. 儲存間隔:錄影檔案按照什麼規則儲存視訊,0分鐘表示只儲存到單個檔案。

3. 視訊邊框:視訊控制元件的邊框的寬度,值越大邊框越粗。

4. 報警視訊:開啟以後儲存報警視訊到可執行檔案video_alarm,暫未啟用。

5.
硬體加速:當前視訊核心採用何種硬體加速方式,預設none表示不啟用硬體加速(常用的所有qsv、dxva2、d3d11va,建議用dxva2或者d3d11va,具體根據電腦是否支援定)。

6. 通訊方式:視訊流採用何種通訊方式,預設TCP(可靠連線,不會丟包,缺點就是慢),可選UDP(不可靠連線,容易丟包,但是速度快)。

7. 視訊回撥:是否採用回撥的形式來解碼顯示視訊,比如vlc核心和海康核心都支援回撥。

8. 碼流型別:雙擊攝像機以什麼碼流地址載入實時視訊,預設子碼流。

9. 解碼方式:ffmpeg核心有用,可選速度優先、質量優先、均衡處理。

10. 播放音訊:開啟以後可以播放聲音,有時候不需要聲音的時候可以關閉,節省資源。

11. 圖片拉伸:預設開啟,通道畫面按照填充拉伸顯示,不開啟的話則按照等比例縮放。

12. 開啟間隔:軟體啟動後挨個載入開啟視訊的間隔,預設1秒鐘。

13. 重連間隔:軟體多久檢測一次攝像機是否掉線,預設10秒鐘。

14. 事件訂閱:開啟以後會用onvif的機制訂閱報警事件。

15. 自動校時:開啟後攝像機上線立即同步本地時間到攝像機。

## 二、功能特點

(一)軟體模組

1. 視訊監控模組,各種停靠小窗體子模組,包括裝置列表、圖文警情、視窗資訊、雲臺控制、預置位、巡航設定、裝置控制、懸浮地圖、網頁瀏覽等。

2. 視訊回放模組,包括本地回放、遠端回放、裝置播放、圖片回放、視訊上傳等。

3. 電子地圖模組,包括圖片地圖、線上地圖、離線地圖、路徑規劃等。

4. 日誌查詢模組,包括本地日誌、裝置日誌等。

5. 系統設定模組,包括系統設定(基本設定、視訊引數、資料庫設定、地圖配置、串列埠配置等)、錄影機管理、攝像機管理、輪詢配置、使用者管理等。

(二)基礎功能

1. 支援各種視訊流(rtsp、rtmp、http等)、視訊檔案(mp4、rmvb、avi等)、本地USB攝像機播放。

2. 支援多畫面切換,包括1、4、6、8、9、13、16、25、36、64畫面切換。

3. 支援全屏切換,多種切換方式包括滑鼠右鍵選單、工具欄按鈕、快捷鍵(alt+enter全屏,esc退出全屏)。

4. 支援視訊輪詢,包括1、4、9、16畫面輪詢,可設定輪詢分組(輪詢預案)、輪詢間隔、碼流型別等。

6. 支援許可權管理,不同的使用者可以對應不同的模組許可權,比如刪除日誌、關閉系統等。

7. 資料庫支援多種,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金倉等。

8. 本地USB攝像機支援設定解析度、幀率等引數。

9. 所有停靠模組都自動生成對應的選單用來控制顯示和隱藏,在標題欄右鍵可以彈出。

10. 支援顯示所有模組、隱藏所有模組、復位普通佈局、復位全屏佈局。

11. 雙擊裝置彈出實時預覽視訊,支援圖片地圖、線上地圖、離線地圖等。

12. 攝像機節點拖曳到對應窗體播放視訊,同時支援拖曳本地檔案直接播放。

13. 刪除視訊支援滑鼠右鍵刪除、懸浮條關閉刪除、拖曳到視訊監控面板外刪除等多種方式。

14. 圖片地圖上裝置按鈕可自由拖動,自動儲存位置資訊。百度地圖上可以滑鼠單擊獲取經緯度資訊,用來更新裝置位置。

15. 視訊監控面板窗體中任意通道支援拖曳交換,瞬間響應。

16. 封裝了百度地圖,檢視切換,運動軌跡,裝置點位,滑鼠按下獲取經緯度等。

17. 雙擊節點、拖曳節點、拖曳窗體交換位置等操作,均自動更新儲存最後的播放地址,下次軟體開啟自動應用。

18. 右下角音量條控制元件,失去焦點自動隱藏,音量條帶靜音圖示。

19. 支援視訊截圖,可指定單個或者對所有通道截圖,底部小工具欄也有截圖按鈕。

20. 支援超時自動隱藏滑鼠指標、自動全屏機制。

21. 支援onvif雲臺控制,可上下左右移動雲臺攝像機,包括復位和焦距調整等。

22. 支援任意onvif攝像機,包括但不限於海康、大華、宇視、天地偉業、華為等。

23. 可儲存視訊,可選定時儲存或者單檔案儲存,可選儲存間隔時間。

24. 可設定視訊流通訊方式tcp+udp,可設定視訊解碼是速度優先、質量優先、均衡等。

25. 可設定軟體中文名稱、英文名稱、LOGO圖示等。

26. 儲存的視訊檔案支援匯出到指定目錄,支援批量上傳到伺服器。

(三)特色功能

1. 主介面採用停靠窗體模式,各種元件以小模組的形式加入,可自定義任意模組加入。

2. 停靠模組可拖動任意位置嵌入和懸浮,支援最大化全屏,支援多螢幕。

3. 雙重佈局檔案儲存機制,正常模式、全屏模式都對應不同的佈局方案,自動切換和儲存,比如全屏模式可以突出幾個模組透明顯示在指定位置,更具科幻感現代化。

4. 原創onvif協議機制,採用底層協議解析(udp廣播搜尋+http請求執行命令)更輕量易懂易學習拓展,不依賴任何第三方元件比如gsoap。

5. 原創資料匯入匯出機制,跨平臺不依賴任何元件,瞬間匯出資料。

6.
內建多個原創元件,宇宙超值超級牛逼,包括資料匯入匯出元件(匯出到xls、pdf、列印)、資料庫元件(資料庫管理執行緒、自動清理資料執行緒、萬能分頁、資料請求等)、地圖元件、視訊監控元件、檔案多執行緒收發元件、onvif通訊元件、通用瀏覽器核心元件等。

7. 自定義資訊框+錯誤框+詢問框+右下角提示框(包含多種格式)等。

8. 精美換膚,高達17套面板樣式隨意更換,所有樣式全部統一,包括選單等。

9. 視訊控制元件懸浮條可以自行增加多個按鈕,監控介面底部小工具欄也可自行增加按鈕。

10. 雙擊攝像機節點自動播放視訊,雙擊節點自動依次新增視訊,會自動跳到下一個,雙擊父節點自動新增該節點下的所有視訊。可選主碼流、子碼流。

11. 錄影機管理、攝像機管理,可新增刪除修改匯入匯出列印資訊,立即應用新的裝置資訊生成樹狀列表,不需重啟。

12.
可選多種核心自由切換,ffmpeg、vlc、mpv等,均可在pro中設定。推薦用ffmpeg,跨平臺最多,預設提供好了linux和mac平臺上編譯好的庫。

13. 支援硬解碼,可設定硬解碼型別(qsv、dxva2、d3d11va等)。

14. 預設採用opengl繪製視訊,超低的CPU資源佔用,支援yuyv和nv12兩種格式繪製,很牛逼。

15. 高度可定製化,使用者可以很方便的在此基礎上衍生自己的功能,比如增加自定義模組,增加執行模式、機器人監控、無人機監控、挖掘機監控等。

16. 支援xp、win7、win10、linux、mac、各種國產系統(UOS、中標麒麟、銀河麒麟等)、嵌入式linux等系統。

17. 註釋完整,專案結構清晰,超級詳細完整的使用開發手冊,精確到每個程式碼檔案的功能說明,不斷持續迭代版本。

## 三、體驗地址

1.
體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g
提取碼:01jf 檔名:bin_video_system.zip。

2. 國內站點:https://gitee.com/feiyangqingyun

3.
國際站點:https://github.com/feiyangqingyun

4.

5.
知乎主頁:https://www.zhihu.com/people/feiyangqingyun/

6.
線上文件:https://feiyangqingyun.gitee.io/qwidgetdemo/video_system/

## 四、效果圖

下圖純粹為了勞逸結合!

## 五、核心程式碼

void frmConfigSystem::initVideoConfig1()
{
    QStringList listImageFlag;
    listImageFlag << "速度優先" << "質量優先" << "均衡";
    ui->cboxImageFlag->addItems(listImageFlag);
    ui->cboxImageFlag->setCurrentIndex(AppConfig::ImageFlag);
    connect(ui->cboxImageFlag, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig1()));

    QStringList listHardware;
    listHardware << "none";
#ifdef videoffmpeg
    listHardware << "qsv" << "cuvid";
#elif videovlc
    listHardware << "auto" << "any";
#endif
    listHardware << "vaapi" << "vdpau" << "dxva2" << "d3d11va";

    ui->cboxHardware->addItems(listHardware);
    ui->cboxHardware->setCurrentIndex(ui->cboxHardware->findText(AppConfig::Hardware));
    connect(ui->cboxHardware, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig1()));

    QStringList listTransport;
    listTransport << "tcp" << "udp";
    ui->cboxTransport->addItems(listTransport);
    ui->cboxTransport->setCurrentIndex(ui->cboxTransport->findText(AppConfig::Transport));
    connect(ui->cboxTransport, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig1()));

    ui->btnCallback->setChecked(AppConfig::Callback);
    connect(ui->btnCallback, SIGNAL(checkedChanged(bool)), this, SLOT(saveVideoConfig1()));

    ui->btnFillImage->setChecked(AppConfig::FillImage);
    connect(ui->btnFillImage, SIGNAL(checkedChanged(bool)), this, SLOT(saveVideoConfig1()));

    ui->btnPlayAudio->setChecked(AppConfig::PlayAudio);
    connect(ui->btnPlayAudio, SIGNAL(checkedChanged(bool)), this, SLOT(saveVideoConfig1()));
}

void frmConfigSystem::saveVideoConfig1()
{
    int imageFlag = ui->cboxImageFlag->currentIndex();
    if (AppConfig::ImageFlag != imageFlag) {
        AppConfig::ImageFlag = imageFlag;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    QString hardware = ui->cboxHardware->currentText();
    if (AppConfig::Hardware != hardware) {
        AppConfig::Hardware = hardware;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    QString transport = ui->cboxTransport->currentText();
    if (AppConfig::Transport != transport) {
        AppConfig::Transport = transport;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    bool callback = ui->btnCallback->getChecked();
    if (AppConfig::Callback != callback) {
        AppConfig::Callback = callback;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    bool fillImage = ui->btnFillImage->getChecked();
    if (AppConfig::FillImage != fillImage) {
        AppConfig::FillImage = fillImage;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    bool playAudio = ui->btnPlayAudio->getChecked();
    if (AppConfig::PlayAudio != playAudio) {
        AppConfig::PlayAudio = playAudio;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    AppConfig::writeConfig();
}

void frmConfigSystem::initVideoConfig2()
{
    QStringList listVideoRtsp;
    listVideoRtsp << "主碼流" << "子碼流";
    ui->cboxVideoRtsp->addItems(listVideoRtsp);
    ui->cboxVideoRtsp->setCurrentIndex(AppConfig::VideoRtsp);
    connect(ui->cboxVideoRtsp, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig2()));

    QStringList listVideoBorder;
    listVideoBorder << "1畫素" << "2畫素" << "3畫素" << "4畫素" << "5畫素";
    ui->cboxVideoBorder->addItems(listVideoBorder);
    QString videoBorder = QString("%1畫素").arg(AppConfig::VideoBorder);
    ui->cboxVideoBorder->setCurrentIndex(ui->cboxVideoBorder->findText(videoBorder));
    connect(ui->cboxVideoBorder, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig2()));
}

void frmConfigSystem::saveVideoConfig2()
{
    //自動更新碼流型別
    int videoRtsp = ui->cboxVideoRtsp->currentIndex();
    if (AppConfig::VideoRtsp != videoRtsp) {
        AppConfig::VideoRtsp = videoRtsp;
        DeviceHelper::initDeviceTree();
        DeviceHelper::initVideoIcon();
    }

    QString strVideoBorder = ui->cboxVideoBorder->currentText();
    int videoBorder = strVideoBorder.mid(0, strVideoBorder.length() - 2).toInt();
    if (AppConfig::VideoBorder != videoBorder) {
        AppConfig::VideoBorder = videoBorder;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    AppConfig::writeConfig();
}

void frmConfigSystem::initVideoConfig3()
{
    ui->btnSaveVideoNormal->setChecked(AppConfig::SaveVideoNormal);
    connect(ui->btnSaveVideoNormal, SIGNAL(checkedChanged(bool)), this, SLOT(saveVideoConfig3()));

    ui->btnSaveVideoAlarm->setChecked(AppConfig::SaveVideoAlarm);
    connect(ui->btnSaveVideoAlarm, SIGNAL(checkedChanged(bool)), this, SLOT(saveVideoConfig3()));

    QStringList listSaveVideoInterval;
    listSaveVideoInterval << "0分鐘" << "1分鐘" << "3分鐘" << "5分鐘" << "10分鐘" << "20分鐘" << "30分鐘" << "60分鐘";
    ui->cboxSaveVideoInterval->addItems(listSaveVideoInterval);
    QString saveVideoInterval = QString("%1分鐘").arg(AppConfig::SaveVideoInterval);
    ui->cboxSaveVideoInterval->setCurrentIndex(ui->cboxSaveVideoInterval->findText(saveVideoInterval));
    connect(ui->cboxSaveVideoInterval, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig3()));

    QStringList listOpenInterval;
    listOpenInterval << "0毫秒" << "100毫秒" << "300毫秒" << "500毫秒" << "1000毫秒" << "2000毫秒" << "3000毫秒" << "5000毫秒";
    ui->cboxOpenInterval->addItems(listOpenInterval);
    QString openInterval = QString("%1毫秒").arg(AppConfig::OpenInterval);
    ui->cboxOpenInterval->setCurrentIndex(ui->cboxOpenInterval->findText(openInterval));
    connect(ui->cboxOpenInterval, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig3()));

    QStringList listCheckInterval;
    listCheckInterval << "5秒鐘" << "8秒鐘" << "10秒鐘" << "30秒鐘" << "60秒鐘";
    ui->cboxCheckInterval->addItems(listCheckInterval);
    QString checkInterval = QString("%1秒鐘").arg(AppConfig::CheckInterval);
    ui->cboxCheckInterval->setCurrentIndex(ui->cboxCheckInterval->findText(checkInterval));
    connect(ui->cboxCheckInterval, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig3()));

    QStringList listCheckTime;
    listCheckTime << "500毫秒" << "1000毫秒" << "2000毫秒" << "3000毫秒" << "5000毫秒" << "8000毫秒" << "10000毫秒";
    ui->cboxCheckTime->addItems(listCheckTime);
    QString checkTime = QString("%1毫秒").arg(AppConfig::CheckTime);
    ui->cboxCheckTime->setCurrentIndex(ui->cboxCheckTime->findText(checkTime));
    connect(ui->cboxCheckTime, SIGNAL(currentIndexChanged(int)), this, SLOT(saveVideoConfig3()));
}

void frmConfigSystem::saveVideoConfig3()
{
    AppConfig::SaveVideoNormal = ui->btnSaveVideoNormal->getChecked();
    AppConfig::SaveVideoAlarm = ui->btnSaveVideoAlarm->getChecked();

    QString saveVideoInterval = ui->cboxSaveVideoInterval->currentText();
    AppConfig::SaveVideoInterval = saveVideoInterval.mid(0, saveVideoInterval.length() - 2).toInt();

    QString openInterval = ui->cboxOpenInterval->currentText();
    AppConfig::OpenInterval = openInterval.mid(0, openInterval.length() - 2).toInt();

    QString checkInterval = ui->cboxCheckInterval->currentText();
    AppConfig::CheckInterval = checkInterval.mid(0, checkInterval.length() - 2).toInt();

    QString strCheckTime = ui->cboxCheckTime->currentText();
    int checkTime = strCheckTime.mid(0, strCheckTime.length() - 2).toInt();
    if (AppConfig::CheckTime != checkTime) {
        AppConfig::CheckTime = checkTime;
        emit AppEvent::Instance()->slot_changeVideoConfig();
    }

    AppConfig::writeConfig();
}

更多內容參考: https://www.gewuweb.com/sitemap.html