一、前言
在經曆了大量的現場裝置測試,至少幾十種廠家、幾百種裝置,遇見過奇奇怪怪的問題,一個個想方設法解決,發現有個問題是在下發鑒權的時候,需要帶上裝置的時間,而不是發送端的時間,如果帶的不是裝置上的時間很可能鑒權失敗。這個問題親測十幾種市面上各種視訊監控系統用戶端,均未實作,通過抓包分析,用戶端軟體帶的鑒權資訊中是本地的時間,而不是裝置的時間,導緻根本無法通過onvif協定來加載該裝置,這種體驗非常的不友好,因為很可能裝置出廠時間是1970年或者2000年等,而且裝置上未必帶有電池,斷電後時間一長,時間很可能又恢複了,而設定日期時間指令也需要帶上鑒權資訊,但是好在擷取日期時間指令是不需要鑒權資訊的,于是通過這個為突破口,首次需要鑒權資訊的指令前,主動擷取下裝置的日期時間,然後和本地的時間比較,取出一個秒數內插補點(裝置上的時間和本地時間相差多少秒),如果裝置是2000年那這個內插補點就是一個很大的負數值,在準備鑒權資訊的時候,取本地時間加上這個內插補點即可,這樣相當于下發的時間就是裝置上的時間。如果裝置上的時間更新過,則需要重新擷取時間即可。
Onvif元件功能設計:
- 搜尋裝置,擷取裝置的資訊比如廠家、型号等。
- 可指定網卡搜尋,可能有多個網卡多個網段位址。
- 可手動指定單個裝置位址搜尋,用于多點傳播搜尋不通但是網絡通的情況。
- 可以選擇累加的方式統計搜尋到的裝置,在跨網段大量裝置的情況下尤其需要。
- 擷取裝置的多個配置檔案資訊profile。
- 擷取對應配置檔案的視訊流位址rtsp,以及分辨率等參數。
- 雲台控制,上下左右移動,焦距放大縮小,相對和絕對移動。
- 擷取預置位資訊,增删改查,觸發預置位。
- 訂閱事件,接收裝置的各種消息尤其是報警事件比如IO口的報警。
- 抓圖,擷取裝置目前的圖檔。
- 擷取和裝置網絡配置資訊比如IP位址等。
- 擷取和設定NTP時間同步以及設定裝置時間。
- 擷取和設定視訊參數和圖檔參數(亮度、色彩、飽和度)。
- 重新開機裝置。
- 增加、删除、修改、查詢OSD資訊。
二、效果圖
三、體驗位址
- 國内站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 體驗位址: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 軟體子產品
- 視訊監控子產品,各種停靠小窗體子子產品,包括裝置清單、圖文警情、視窗資訊、雲台控制、預置位、巡航設定、裝置控制、懸浮地圖、網頁浏覽等。
- 視訊回放子產品,包括本地回放、遠端回放、裝置播放、圖檔回放、視訊上傳等。
- 電子地圖子產品,包括圖檔地圖、線上地圖、離線地圖、路徑規劃等。
- 日志查詢子產品,包括本地日志、裝置日志等。
- 系統設定子產品,包括系統設定(基本設定、視訊參數、資料庫設定、地圖配置、序列槽配置等)、錄像機管理、錄影機管理、輪詢配置、錄像計劃、使用者管理等。
5.2 基礎功能
- 支援各種視訊流(rtsp、rtmp、http等)、視訊檔案(mp4、rmvb、avi等)、本地USB錄影機播放。
- 支援多畫面切換,包括1、4、6、8、9、13、16、25、36、64畫面切換。
- 支援全屏切換,多種切換方式包括滑鼠右鍵菜單、工具欄按鈕、快捷鍵(alt+enter全屏,esc退出全屏)。
- 支援視訊輪詢,包括1、4、9、16畫面輪詢,可設定輪詢分組(輪詢預案)、輪詢間隔、碼流類型等。
- 支援onvif協定,包括裝置搜尋、雲台控制、預置位、裝置控制(圖檔參數、校對時間、系統重新開機,抓拍圖檔等)。
- 支援權限管理,不同的使用者可以對應不同的子產品權限,比如删除日志、關閉系統等。
- 資料庫支援多種,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金倉等。
- 本地USB錄影機支援設定分辨率、幀率等參數。
- 所有停靠子產品都自動生成對應的菜單用來控制顯示和隐藏,在标題欄右鍵可以彈出。
- 支援顯示所有子產品、隐藏所有子產品、複位普通布局、複位全屏布局。
- 輕按兩下裝置彈出實時預覽視訊,支援圖檔地圖、線上地圖、離線地圖等。
- 錄影機節點拖曳到對應窗體播放視訊,同時支援拖曳本地檔案直接播放。
- 删除視訊支援滑鼠右鍵删除、懸浮條關閉删除、拖曳到視訊監控面闆外删除等多種方式。
- 圖檔地圖上裝置按鈕可自由拖動,自動儲存位置資訊。百度地圖上可以滑鼠單擊擷取經緯度資訊,用來更新裝置位置。
- 視訊監控面闆窗體中任意通道支援拖曳交換,瞬間響應。
- 封裝了百度地圖,視圖切換,運動軌迹,裝置點位,滑鼠按下擷取經緯度等。
- 輕按兩下節點、拖曳節點、拖曳窗體交換位置等操作,均自動更新儲存最後的播放位址,下次軟體打開自動應用。
- 右下角音量條控件,失去焦點自動隐藏,音量條帶靜音圖示。
- 支援視訊截圖,可指定單個或者對所有通道截圖,底部小工具欄也有截圖按鈕。
- 支援逾時自動隐藏滑鼠指針、自動全屏機制。
- 支援onvif雲台控制,可上下左右移動雲台錄影機,包括複位和焦距調整等。
- 支援onvif預置位,可以添加、删除、修改預置位,可以調用起始位。
- 支援onvif圖像參數設定,包括明亮度、對比度、飽和度、尖銳度等。
- 支援onvif其他操作,包括抓圖、網絡設定、校時、重新開機、事件訂閱等。
- 支援任意onvif錄影機,包括但不限于海康、大華、宇視、天地偉業、華為等。
- 可儲存視訊,可標明時存儲或者單檔案存儲,可選存儲間隔時間。
- 可設定視訊流通信方式tcp+udp,可設定視訊解碼是速度優先、品質優先、均衡等。
- 可設定軟體中文名稱、英文名稱、LOGO圖示等。
- 存儲的視訊檔案支援導出到指定目錄,支援批量上傳到伺服器。
- 完善的錄像計劃設定,支援每個通道7 * 24小時每半小時設定是否存儲錄像。
5.3 特色功能
- 主界面采用停靠窗體模式,各種元件以小子產品的形式加入,可自定義任意子產品加入。
- 停靠子產品可拖動任意位置嵌入和懸浮,支援最大化全屏,支援多螢幕。
- 雙重布局檔案存儲機制,正常模式、全屏模式都對應不同的布局方案,自動切換和儲存,比如全屏模式可以突出幾個子產品透明顯示在指定位置,更具科幻感現代化。
- 原創onvif協定機制,采用底層協定解析(udp廣播搜尋+http請求執行指令)更輕量易懂易學習拓展,不依賴任何第三方元件比如gsoap。
- 原創資料導入、導出、列印機制,跨平台不依賴任何元件,瞬間導出資料。
- 内置多個原創元件,宇宙超值超級牛逼,包括資料導入導出元件(導出到xls、pdf、列印)、資料庫元件(資料庫管理線程、自動清理資料線程、萬能分頁、資料請求等)、地圖元件、視訊監控元件、檔案多線程收發元件、onvif通信元件、通用浏覽器核心元件等。
- 自定義資訊框+錯誤框+詢問框+右下角提示框(包含多種格式)等。
- 精美換膚,高達17套皮膚樣式随意更換,所有樣式全部統一,包括菜單等。
- 視訊控件懸浮條可以自行增加多個按鈕,監控界面底部小工具欄也可自行增加按鈕。
- 輕按兩下錄影機節點自動播放視訊,輕按兩下節點自動依次添加視訊,會自動跳到下一個,輕按兩下父節點自動添加該節點下的所有視訊。可選主碼流、子碼流。
- 錄像機管理、錄影機管理,可添加删除修改導入導出列印資訊,立即應用新的裝置資訊生成樹狀清單,不需重新開機。
- 可選多種核心自由切換,ffmpeg、vlc、mpv等,均可在pro中設定。推薦用ffmpeg,跨平台最多,預設提供好了linux和mac平台上編譯好的庫。
- 支援硬解碼,可設定硬解碼類型(qsv、dxva2、d3d11va等)。
- 預設采用opengl繪制視訊,超低的CPU資源占用,支援yuyv和nv12兩種格式繪制,性能爆表。
- 标簽和圖形資訊支援三種繪制方式,繪制到遮罩層、繪制到圖檔、源頭繪制(對應資訊可以存儲到檔案)。
- 高度可定制化,使用者可以很友善的在此基礎上衍生自己的功能,比如增加自定義子產品,增加運作模式、機器人監控、無人機監控、挖掘機監控等。
- 支援xp、win7、win10、win11、linux、mac、各種國産系統(UOS、中标麒麟、銀河麒麟等)、嵌入式linux等系統。
- 注釋完整,項目結構清晰,超級詳細完整的使用開發手冊,精确到每個代碼檔案的功能說明,不斷持續疊代版本。