天天看點

Qt編寫視訊監控系統77-Onvif元件支援非正常時間的裝置

作者:Qt自定義控件

一、前言

在經曆了大量的現場裝置測試,至少幾十種廠家、幾百種裝置,遇見過奇奇怪怪的問題,一個個想方設法解決,發現有個問題是在下發鑒權的時候,需要帶上裝置的時間,而不是發送端的時間,如果帶的不是裝置上的時間很可能鑒權失敗。這個問題親測十幾種市面上各種視訊監控系統用戶端,均未實作,通過抓包分析,用戶端軟體帶的鑒權資訊中是本地的時間,而不是裝置的時間,導緻根本無法通過onvif協定來加載該裝置,這種體驗非常的不友好,因為很可能裝置出廠時間是1970年或者2000年等,而且裝置上未必帶有電池,斷電後時間一長,時間很可能又恢複了,而設定日期時間指令也需要帶上鑒權資訊,但是好在擷取日期時間指令是不需要鑒權資訊的,于是通過這個為突破口,首次需要鑒權資訊的指令前,主動擷取下裝置的日期時間,然後和本地的時間比較,取出一個秒數內插補點(裝置上的時間和本地時間相差多少秒),如果裝置是2000年那這個內插補點就是一個很大的負數值,在準備鑒權資訊的時候,取本地時間加上這個內插補點即可,這樣相當于下發的時間就是裝置上的時間。如果裝置上的時間更新過,則需要重新擷取時間即可。

Onvif元件功能設計:

  • 搜尋裝置,擷取裝置的資訊比如廠家、型号等。
  • 可指定網卡搜尋,可能有多個網卡多個網段位址。
  • 可手動指定單個裝置位址搜尋,用于多點傳播搜尋不通但是網絡通的情況。
  • 可以選擇累加的方式統計搜尋到的裝置,在跨網段大量裝置的情況下尤其需要。
  • 擷取裝置的多個配置檔案資訊profile。
  • 擷取對應配置檔案的視訊流位址rtsp,以及分辨率等參數。
  • 雲台控制,上下左右移動,焦距放大縮小,相對和絕對移動。
  • 擷取預置位資訊,增删改查,觸發預置位。
  • 訂閱事件,接收裝置的各種消息尤其是報警事件比如IO口的報警。
  • 抓圖,擷取裝置目前的圖檔。
  • 擷取和裝置網絡配置資訊比如IP位址等。
  • 擷取和設定NTP時間同步以及設定裝置時間。
  • 擷取和設定視訊參數和圖檔參數(亮度、色彩、飽和度)。
  • 重新開機裝置。
  • 增加、删除、修改、查詢OSD資訊。

二、效果圖

Qt編寫視訊監控系統77-Onvif元件支援非正常時間的裝置

三、體驗位址

  1. 國内站點:https://gitee.com/feiyangqingyun
  2. 國際站點:https://github.com/feiyangqingyun
  3. 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 體驗位址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 檔案名:bin_video_system。

四、相關代碼

QString OnvifOther::getDateTime()
{
    QString result = writeData("GetSystemDateAndTime", "tt:Year|tt:Month|tt:Day|tt:Hour|tt:Minute|tt:Second|tt:TZ", "擷取裝置時間", true, true);
    QStringList list = result.split(OnvifResultSplit);
    if (list.count() != 7) {
        return result;
    }

    QString year = getResult(list.at(0));
    QString month = getResult(list.at(1));
    QString day = getResult(list.at(2));
    QString hour = getResult(list.at(3));
    QString min = getResult(list.at(4));
    QString sec = getResult(list.at(5));

    //計算時區并指派
    QString timezone = list.at(6);
    timezone = timezone.mid(6, timezone.length() - 6);
    device->timezone = timezone;

    //将日期根據時區進行運算
    QString str = QString("%1-%2-%3 %4:%5:%6").arg(year).arg(month).arg(day).arg(hour).arg(min).arg(sec);
    QDateTime dt = QDateTime::fromString(str, "yyyy-M-d h:m:s");
    if (!device->timezone.contains("GMT-08")) {
        dt = dt.addSecs(8 * 60 * 60);
    }

    //2023-05-22 新增時間內插補點計算(有些裝置需要用裝置的時間去鑒權)
    device->timeOffset = QDateTime::currentDateTime().secsTo(dt);

    //不足兩位補零
    list = dt.toString("yyyy-M-d-h-m-s").split("-");
    result = QString("%1-%2-%3 %4:%5:%6 %7").arg(list.at(0)).arg(list.at(1), 2, '0').arg(list.at(2), 2, '0')
             .arg(list.at(3), 2, '0').arg(list.at(4), 2, '0').arg(list.at(5), 2, '0').arg(timezone);
    return result;
}

bool OnvifOther::setDateTime(const QDateTime &datetime, bool ntp)
{
    QStringList temp = datetime.toString("yyyy-M-d-h-m-s").split("-");
    QString wsdl = "http://www.onvif.org/ver10/device/wsdl";
    QString schema = "http://www.onvif.org/ver10/schema";

    QStringList list;
    list << QString("    <SetSystemDateAndTime xmlns=\"%1\">").arg(wsdl);
    list << QString("      <DateTimeType>%1</DateTimeType>").arg(ntp ? "NTP" : "Manual");
    list << QString("      <DaylightSavings>%1</DaylightSavings>").arg("false");
    list << QString("      <TimeZone>");
    list << QString("        <TZ xmlns=\"%1\">%2</TZ>").arg(schema).arg(ntp ? device->timezone : "CST-8");
    list << QString("      </TimeZone>");

    if (!ntp) {
        list << QString("      <UTCDateTime>");
        list << QString("        <Date xmlns=\"%1\">").arg(schema);
        list << QString("          <Year>%1</Year>").arg(temp.at(0));
        list << QString("          <Month>%1</Month>").arg(temp.at(1));
        list << QString("          <Day>%1</Day>").arg(temp.at(2));
        list << QString("        </Date>");
        list << QString("        <Time xmlns=\"%1\">").arg(schema);
        list << QString("          <Hour>%1</Hour>").arg(temp.at(3));
        list << QString("          <Minute>%1</Minute>").arg(temp.at(4));
        list << QString("          <Second>%1</Second>").arg(temp.at(5));
        list << QString("        </Time>");
        list << QString("      </UTCDateTime>");
    }

    list << QString("    </SetSystemDateAndTime>");

    QString result = writeData(list.join("\r\n"), "SetSystemDateAndTimeResponse", "設定裝置時間", false);
    return result.contains("SetSystemDateAndTimeResponse");
}

QString OnvifXml::getUserToken(const QString &userName, const QString &userPwd, qint64 timeOffset)
{
    //要轉成UTC格式的時間 "2019-08-10T03:31:37S"  "2020-10-11T09:24:44.988Z"
    //有些裝置需要按照裝置上的時間來鑒權(否則會失敗)
    QDateTime DateTime = QDateTime::currentDateTime().addSecs(timeOffset).toUTC();
    QByteArray Created = DateTime.toString("yyyy-MM-ddThh:mm:ss.zzzZ").toLatin1();

    //指定字元串進行密碼加密 LKqI6G/AikKCQrN0zqZFlg==
    QByteArray Nonce = Created.toBase64();
    QByteArray Nonce2 = QByteArray::fromBase64(Nonce);
    QByteArray Password = Nonce2 + Created + userPwd.toLatin1();
    Password = QCryptographicHash::hash(Password, QCryptographicHash::Sha1).toBase64();

    //固定字元串
    QString Enter = "\r\n        ";
    QString Type = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest";
    QString EncodingType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary";
    QString xmlns = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";

    QStringList list;
    if (wsseToken) {
        list << QString("%1<wsse:Username>%2</wsse:Username>").arg(Enter).arg(userName);
        list << QString("%1<wsse:Password Type=\"%2\">%3</wsse:Password>").arg(Enter).arg(Type).arg(QString(Password));
        list << QString("%1<wsse:Nonce>%2</wsse:Nonce>").arg(Enter).arg(QString(Nonce));
        list << QString("%1<wsu:Created>%2</wsu:Created>").arg(Enter).arg(QString(Created));
    } else {
        list << QString("%1<Username>%2</Username>").arg(Enter).arg(userName);
        list << QString("%1<Password Type=\"%2\">%3</Password>").arg(Enter).arg(Type).arg(QString(Password));
        list << QString("%1<Nonce EncodingType=\"%2\">%3</Nonce>").arg(Enter).arg(EncodingType).arg(QString(Nonce));
        list << QString("%1<Created xmlns=\"%2\">%3</Created>").arg(Enter).arg(xmlns).arg(QString(Created));
    }

    list << QString("%1").arg("\r\n      ");
    return list.join("");
}           

五、功能特點

5.1 軟體子產品

  1. 視訊監控子產品,各種停靠小窗體子子產品,包括裝置清單、圖文警情、視窗資訊、雲台控制、預置位、巡航設定、裝置控制、懸浮地圖、網頁浏覽等。
  2. 視訊回放子產品,包括本地回放、遠端回放、裝置播放、圖檔回放、視訊上傳等。
  3. 電子地圖子產品,包括圖檔地圖、線上地圖、離線地圖、路徑規劃等。
  4. 日志查詢子產品,包括本地日志、裝置日志等。
  5. 系統設定子產品,包括系統設定(基本設定、視訊參數、資料庫設定、地圖配置、序列槽配置等)、錄像機管理、錄影機管理、輪詢配置、錄像計劃、使用者管理等。

5.2 基礎功能

  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畫面輪詢,可設定輪詢分組(輪詢預案)、輪詢間隔、碼流類型等。
  5. 支援onvif協定,包括裝置搜尋、雲台控制、預置位、裝置控制(圖檔參數、校對時間、系統重新開機,抓拍圖檔等)。
  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. 支援onvif圖像參數設定,包括明亮度、對比度、飽和度、尖銳度等。
  24. 支援onvif其他操作,包括抓圖、網絡設定、校時、重新開機、事件訂閱等。
  25. 支援任意onvif錄影機,包括但不限于海康、大華、宇視、天地偉業、華為等。
  26. 可儲存視訊,可標明時存儲或者單檔案存儲,可選存儲間隔時間。
  27. 可設定視訊流通信方式tcp+udp,可設定視訊解碼是速度優先、品質優先、均衡等。
  28. 可設定軟體中文名稱、英文名稱、LOGO圖示等。
  29. 存儲的視訊檔案支援導出到指定目錄,支援批量上傳到伺服器。
  30. 完善的錄像計劃設定,支援每個通道7 * 24小時每半小時設定是否存儲錄像。

5.3 特色功能

  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. 高度可定制化,使用者可以很友善的在此基礎上衍生自己的功能,比如增加自定義子產品,增加運作模式、機器人監控、無人機監控、挖掘機監控等。
  17. 支援xp、win7、win10、win11、linux、mac、各種國産系統(UOS、中标麒麟、銀河麒麟等)、嵌入式linux等系統。
  18. 注釋完整,項目結構清晰,超級詳細完整的使用開發手冊,精确到每個代碼檔案的功能說明,不斷持續疊代版本。

繼續閱讀