Qt音視訊開發28-Onvif資訊獲取

2020-10-07 13:00:32

一、前言

嚴格意義上來說,Onvif處理這塊算不上音視訊開發的內容,為何重新整理放在音視訊開發這個類別,主要是為了方便統一管理,而且在視訊監控處理這塊,通過onvif來拿到音視訊流這是必經的階段,也算是搭邊的東西。上一篇文章寫的是onvif裝置搜尋,搜到這些裝置以後,第一件事情就是要對裝置資訊獲取一下,比如獲取視訊流地址,設定套件資訊、碼流資訊、解析度大小等,這些資訊的獲取根據具體的需要去獲取,也沒有必要全部獲取,畢竟很可能大部分的資訊用不到,按需編碼永遠都是第一原則,第二原則才是考慮拓展性和穩定性,如果基本的需求都實現不了,那就不是一個真正的軟體,考慮再多的拓展性和穩定性都是白搭,說的嚴重一點就是:所有程式語言都是垃圾,能解決實際需求並變現才是王道!

onvif裝置資訊的獲取需要注意的是,現在市場上絕大部分的攝像機都有密碼驗證的限定,先不管他預設是admin還是12345,起碼有使用者驗證的機制擺在那,這樣相對來說安全很多,不然誰也可以通過onvif協定拿到對應的資訊,就沒有安全性可言,記得幾年前海康爆出了漏洞,導致很多監控攝像頭被洩露,為此海康現在的攝像頭預設onvif是關閉的,開啟以後密碼要求各種組合,哎,想要更安全就必須犧牲便捷性,這個也不知道誰能想出一個完美兼顧的方法。

onvif主要的功能:

  1. 搜尋裝置,獲取裝置的資訊比如廠家、型號等。
  2. 獲取裝置的多個組態檔資訊profile。
  3. 獲取對應組態檔的視訊流地址rtsp,以及解析度等引數。
  4. 雲臺控制,上下左右移動,焦距放大縮小,相對和絕對移動。
  5. 獲取預置位資訊,觸發預置位。
  6. 訂閱事件,接收裝置的各種訊息尤其是報警事件比如IO口的報警。
  7. 抓圖,獲取裝置當前的圖片。
  8. 獲取、建立、刪除使用者資訊。
  9. 獲取和裝置網路設定資訊比如IP地址等。
  10. 獲取和設定NTP時間同步以及設定裝置時間。
  11. 獲取和設定視訊引數和圖片引數(亮度、色彩、飽和度)。
  12. 重新啟動裝置。

onvif的處理流程:

  1. 繫結組播IP(239.255.255.250)和埠(3702),傳送固定的xml格式的資料搜尋裝置。
  2. 接收到的xml格式的資料解析,得到裝置的Onvif地址。
  3. 對Onvif地址傳送對應的資料,收到資料取出對應的節點資料。
  4. 請求Onvif地址獲取Media地址和Ptz地址,Media地址用來獲取詳細的組態檔,Ptz地址用來雲臺控制。
  5. ptz控制是對Ptz地址傳送對應的資料即可。
  6. 設定了使用者認證的需要組織使用者token資訊一塊傳送,每次都需要作鑑權處理。
  7. 接收到的資料不是標準的xml資料,沒法按照正常的節點解析來處理,只能用QXmlQuery來做。
  8. 每個廠家裝置返回的資料未必完全一致,基本上都不一致,需要進行模糊查詢節點值。
  9. 特意採用底層協定解析,因為soap太臃腫函數名稱太另類,特意做的輕量級的。
  10. 兩個必備工具,Onvif Device Manager 和 Onvif Device Test Tool。

二、功能特點

  1. 廣播搜尋裝置,支援IPC和NVR,依次返回,可選擇不同的網路卡IP。
  2. 依次獲取Onvif地址、Media地址、Profile檔案、Rtsp地址。
  3. 可對指定的Profile獲取視訊流Rtsp地址,比如主碼流子碼流地址。
  4. 可對每個裝置設定Onvif使用者資訊,用於認證獲取詳細資訊。
  5. 可實時預覽攝像機影象。
  6. 支援雲臺控制,可上下左右調節雲臺,支援絕對移動和相對移動,可放到和縮小影象遠近。
  7. 支援Qt4和Qt5任意Qt版本,親測Qt4.7.0到Qt5.14.2。
  8. 支援任意編譯器,親測mingw、msvc、gcc、clang。
  9. 支援任意作業系統,親測xp、win7、win10、android、linux、嵌入式linux、樹莓派全志H3等。
  10. 支援任意Onvif攝像機和NVR,親測海康、大華、宇視、華為、海思晶片核心等,可客製化開發。
  11. 支援對指定IP地址及onvif地址進行單播搜尋,比如跨網段情況下非常有用。
  12. 支援指定過濾條件過濾搜尋裝置。
  13. 支援搜尋間隔設定,保證所有裝置搜尋回來,在大量裝置現場很有用。
  14. 可對圖片引數(亮度、色彩度、飽和度)進行設定。
  15. 支援NTP校時和時間同步設定。
  16. 純Qt編寫,超級小巧輕量,總共約2000行程式碼,不依賴任何第三方的庫和元件,跨平臺。
  17. 封裝好了通用的資料傳送和接收解析的函數,可以非常方便的自行拓展其他Onvif處理。
  18. 工具上提供了收發資料文字方塊,顯示收發的資料,方便檢視和分析。
  19. 支援所有Onvif裝置,程式碼工整,介面友好,直接引入pri即可使用。

三、效果圖

在這裡插入圖片描述

四、相關站點

  1. 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
  3. 個人主頁:https://blog.csdn.net/feiyangqingyun
  4. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
  5. 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心程式碼

OnvifDevice::DeviceInfo *OnvifBase::getDeviceInfo()
{
    if (device->deviceUrl.isEmpty()) {
        return 0;
    }

    QString file = device->request->getSendData("GetDeviceInformation");
    QByteArray dataSend = file.toUtf8();
    QNetworkReply *reply = device->request->auth(device->deviceUrl, dataSend);
    emit sendData(dataSend, device->deviceUrl);

    OnvifDevice::DeviceInfo *deviceInfo = 0;
    QByteArray dataReceive;
    bool ok = device->checkData(reply, dataReceive, "獲取裝置資訊");
    if (ok) {
        OnvifQuery query;
        query.setData(dataReceive);
        QString wsdl = query.getDeviceWsdl();
        QString name_path = QString("//%1:GetDeviceInformationResponse/%1:Manufacturer").arg(wsdl);
        QString mod_path = QString("//%1:GetDeviceInformationResponse/%1:Model").arg(wsdl);
        QString ver_path = QString("//%1:GetDeviceInformationResponse/%1:FirmwareVersion").arg(wsdl);
        QString ser_path = QString("//%1:GetDeviceInformationResponse/%1:SerialNumber").arg(wsdl);
        QString hard_path = QString("//%1:GetDeviceInformationResponse/%1:HardwareId").arg(wsdl);

        //先將廣播搜尋到的裝置資訊一起打包
        deviceInfo = new OnvifDevice::DeviceInfo;
        deviceInfo->addr = device->deviceInfo.value("addr");
        deviceInfo->ip = device->deviceInfo.value("ip");
        deviceInfo->name = device->deviceInfo.value("name");
        deviceInfo->location = device->deviceInfo.value("location");
        deviceInfo->hardware = device->deviceInfo.value("hardware");
        deviceInfo->manufacturer = query.getValue(name_path);
        deviceInfo->model = query.getValue(mod_path);
        deviceInfo->firmwareVersion = query.getValue(ver_path);
        deviceInfo->serialNumber = query.getValue(ser_path);
        deviceInfo->hardwareId = query.getValue(hard_path);
    }

    return deviceInfo;
}
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <wsse:UsernameToken>%1</wsse:UsernameToken>
    </wsse:Security>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <GetDeviceInformation xmlns="http://www.onvif.org/ver10/device/wsdl"/>
  </s:Body>
</s:Envelope>