Qt音視訊開發34-Onvif時間設定
阿新 • • 發佈:2020-10-14
一、前言
對裝置設定時間很有必要,這個是必備的功能,畢竟大部分的前端裝置比如攝像機本身不帶BIOS電池的,所以沒法儲存時間,要麼設定了NTP地址來同步時間,要麼其他裝置主動對他進行設定時間,如果時間不正確了,意味著本地畫面顯示的時間字串,本地儲存的視訊錄影檔案等,都可能是不正確的,所以一般的處理是NVR一旦連上攝像機裝置以後,立馬將攝像機的時間設定成NVR的時間,這樣就保持了一致。
用onvif進行時間設定主要由兩種,一種是通過設定NTP服務地址以後,主動呼叫NTP同步來進行,另外一種就是傳送日期時間的資料包給裝置,讓他自己解析處理,這裡要注意的是,資料包中的日期時間是UTC格式的,即倫敦時間,所以在使用的時候需要自己本地先轉換成UTC時間在傳送,Qt內建了轉換成UTC時間的方法 QDateTime::currentDateTime().toUTC()。
onvif主要的功能:
- 搜尋裝置,獲取裝置的資訊比如廠家、型號等。
- 獲取裝置的多個配置檔案資訊profile。
- 獲取對應配置檔案的視訊流地址rtsp,以及解析度等引數。
- 雲臺控制,上下左右移動,焦距放大縮小,相對和絕對移動。
- 獲取預置位資訊,觸發預置位。
- 訂閱事件,接收裝置的各種訊息尤其是報警事件比如IO口的報警。
- 抓圖,獲取裝置當前的圖片。
- 獲取、建立、刪除使用者資訊。
- 獲取和裝置網路配置資訊比如IP地址等。
- 獲取和設定NTP時間同步以及設定裝置時間。
- 獲取和設定視訊引數和圖片引數(亮度、色彩、飽和度)。
- 重啟裝置。
onvif的處理流程:
- 繫結組播IP(239.255.255.250)和埠(3702),傳送固定的xml格式的資料搜尋裝置。
- 接收到的xml格式的資料解析,得到裝置的Onvif地址。
- 對Onvif地址傳送對應的資料,收到資料取出對應的節點資料。
- 請求Onvif地址獲取Media地址和Ptz地址,Media地址用來獲取詳細的配置檔案,Ptz地址用來雲臺控制。
- ptz控制是對Ptz地址傳送對應的資料即可。
- 設定了使用者認證的需要組織使用者token資訊一塊傳送,每次都需要作鑑權處理。
- 接收到的資料不是標準的xml資料,沒法按照正常的節點解析來處理,只能用QXmlQuery來做。
- 每個廠家裝置返回的資料未必完全一致,基本上都不一致,需要進行模糊查詢節點值。
- 特意採用底層協議解析,因為soap太臃腫函式名稱太另類,特意做的輕量級的。
- 兩個必備工具,Onvif Device Manager 和 Onvif Device Test Tool。
二、功能特點
- 廣播搜尋裝置,支援IPC和NVR,依次返回,可選擇不同的網絡卡IP。
- 依次獲取Onvif地址、Media地址、Profile檔案、Rtsp地址。
- 可對指定的Profile獲取視訊流Rtsp地址,比如主碼流子碼流地址。
- 可對每個裝置設定Onvif使用者資訊,用於認證獲取詳細資訊。
- 可實時預覽攝像機影象。
- 支援雲臺控制,可上下左右調節雲臺,支援絕對移動和相對移動,可放到和縮小影象遠近。
- 支援Qt4和Qt5任意Qt版本,親測Qt4.7.0到Qt5.14.2。
- 支援任意編譯器,親測mingw、msvc、gcc、clang。
- 支援任意作業系統,親測xp、win7、win10、android、linux、嵌入式linux、樹莓派全志H3等。
- 支援任意Onvif攝像機和NVR,親測海康、大華、宇視、華為、海思晶片核心等,可定製開發。
- 支援對指定IP地址及onvif地址進行單播搜尋,比如跨網段情況下非常有用。
- 支援指定過濾條件過濾搜尋裝置。
- 支援搜尋間隔設定,保證所有裝置搜尋回來,在大量裝置現場很有用。
- 可對圖片引數(亮度、色彩度、飽和度)進行設定。
- 支援NTP校時和時間同步設定。
- 純Qt編寫,超級小巧輕量,總共約2000行程式碼,不依賴任何第三方的庫和元件,跨平臺。
- 封裝好了通用的資料傳送和接收解析的函式,可以非常方便的自行拓展其他Onvif處理。
- 工具上提供了收發資料文字框,顯示收發的資料,方便檢視和分析。
- 支援所有Onvif裝置,程式碼工整,介面友好,直接引入pri即可使用。
三、效果圖
四、相關站點
- 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
- 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
- 個人主頁:https://blog.csdn.net/feiyangqingyun
- 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
- 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652
五、核心程式碼
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(ResultSplit);
if (list.count() != 7) {
return result;
}
QString year = list.at(0).split(":").last();
QString month = list.at(1).split(":").last();
QString day = list.at(2).split(":").last();
QString hour = list.at(3).split(":").last();
QString min = list.at(4).split(":").last();
QString sec = list.at(5).split(":").last();
//計算時區並賦值
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);
}
//不足兩位補零
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 OnvifOther::writeData(const QString &key, const QString &value, const QString &flag,
bool xmlns, bool value4, quint8 type)
{
if (device->deviceUrl.isEmpty()) {
return QString();
}
QString file = device->request->getSendData(key, true, xmlns);
QByteArray dataSend = file.toUtf8();
//最後引數表示超時時間 一般請求都是很快的 除非對方不線上則卡很久 需要設定下超時時間
QNetworkReply *reply = device->request->post(device->deviceUrl, dataSend, 3000);
emit sendData(dataSend, device->deviceUrl);
QStringList results;
QByteArray dataReceive;
bool ok = device->checkData(reply, dataReceive, flag);
if (ok) {
OnvifQuery query;
query.setData(dataReceive);
if (type == 0) {
if (!value.isEmpty()) {
//可能有多個關鍵字需要獲取
QStringList list = value.split("|");
foreach (QString str, list) {
QString result = value4 ? query.getValue4(str) : query.getValue3(str);
if (result != "-1") {
results << QString("%1:%2").arg(str).arg(result);
}
}
}
} else if (type == 1) {
results = query.getVideoSource();
}
}
return results.join(ResultSplit);
}