Qt编写Onvif搜索及云台控制工具
一、前言
這個工具很早以前大概在2013年就想做了,后面雜七雜八的事情一再耽擱,記得當時最初用的是soap類來搜索和解析的,后面發現太大了,每次編譯都要等好久,光源碼文件加起來都快10MB了,而且函數名非常另類,大量的下劃線等,反正本人非常不適應,近期經過一個朋友(QQ:408815041)的前期探索,對整個處理流程熟悉以后,發現其實用純Qt也可以實現,核心就是udp搜索+post數據。
本程序框架的最大難點在找出對應的數據以及節點數據解析。找出對應的數據可以直接使用官方的ONVIF Device Test Tool,抓包即可。數據解析一開始采用xml的節點解析,發現根本行不通,因為返回的數據不是標準的xml數據,而是soap格式的數據,需要用QXmlQuery來解析。本程序只實現了設備信息的搜索和云臺控制,并未實現服務端,服務端一般是IPC或者NVR上來實現。
體驗地址:https://pan.baidu.com/s/1bbL2ZughZAgfIGrexyN-9g 提取碼:zkeh,下面的bin_onviftool.zip,如果是XP系統,請先執行目錄下的fixff.cmd。
二、Onvif介紹
ONVIF致力于通過全球性的開放接口標準來推進網絡視頻在安防市場的應用,這一接口標準將確保不同廠商生產的網絡視頻產品具有互通性。2008年11月,正式發布了ONVIF第一版規范——ONVIF核心規范1.0。隨著視頻監控的網絡化應用,產業鏈的分工將越來越細。有些廠商專門做攝像頭,有些廠商專門做DVS,有些廠商則可能專門做平臺等,然后通過集成商進行集成,提供給最終客戶。這種產業合作模式,已經迫切的需要行業提供越來越標準化的接口平臺。
ONVIF規范描述了網絡視頻的模型、接口、數據類型以及數據交互的模式。并復用了一些現有的標準,如WS系列標準等。ONVIF規范的目標是實現一個網絡視頻框架協議,使不同廠商所生產的網絡視頻產品(包括攝錄前端、錄像設備等)完全互通。
ONVIF規范中設備管理和控制部分所定義的接口均以Web Services的形式提供,設備作為服務提供者為服務端。ONVIF規范涵蓋了完全的XML及WSDL的定義。每一個支持ONVIF規范的終端設備均須提供與功能相應的Web Service。服務端與客戶端的數據交互采用SOAP協議。ONVIF中的其他部分比如音視頻流則通過RTP/RTSP進行。
三、處理流程
四、功能特點
五、效果圖
六、核心代碼
#include "qonvifsearch.h" #include "qonviffunction.h" #include "qonvifquery.h"//onvif協議固定的IP和端口 #define OnvifAddr QHostAddress("239.255.255.250") #define OnvifPort 3702QOnvifSearch::QOnvifSearch(QObject *parent) : QObject(parent) {isOk = false;//定時器排隊發送搜索命令,有好幾種timer = new QTimer(this);connect(timer, SIGNAL(timeout()), this, SLOT(sendData()));timer->setInterval(300);udpSocket = new QUdpSocket(this); #if (QT_VERSION >= QT_VERSION_CHECK(4,8,5))udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 1); #endifconnect(udpSocket, SIGNAL(readyRead()), this, SLOT(readData())); }QOnvifSearch::~QOnvifSearch() {if (timer->isActive()) {timer->stop();} }void QOnvifSearch::sendData() {QByteArray data = QOnvifFunction::getFile(currentFile);if(!data.isEmpty()) {data = QString(data).arg(QOnvifFunction::getUuid()).toUtf8();udpSocket->writeDatagram(data, OnvifAddr, OnvifPort);emit sendData(data);}//依次發送數據,如果到了最后一個則停止//根據onvif device test工具抓包分析,只要發送前面兩個就行,后面兩個是ONVIF Device Manager抓包的//在收到結果的地方要對重復的進行過濾,因為部分設備兩種協議請求都會返回if (currentFile == ":/send/search1.xml") {currentFile = ":/send/search2.xml";} else if (currentFile == ":/send/search2.xml") {currentFile = ":/send/search3.xml";} else if (currentFile == ":/send/search3.xml") {currentFile = ":/send/search4.xml";} else if (currentFile == ":/send/search4.xml") {timer->stop();} }void QOnvifSearch::readData() {QByteArray data;QHostAddress host;quint16 port = 0;while (udpSocket->hasPendingDatagrams()) {data.resize(udpSocket->pendingDatagramSize());udpSocket->readDatagram(data.data(), data.size(), &host, &port);emit receiveData(data);}QOnvifQuery query;query.setData(data);QString addr_path = QString("//%1:ProbeMatches/%1:ProbeMatch/%1:XAddrs").arg(query.getDiscovery());QString scopes_path = QString("//%1:ProbeMatches/%1:ProbeMatch/%1:Scopes").arg(query.getDiscovery());QString addr = query.getValue(addr_path);QString scopes = query.getValue(scopes_path);if(!addr.isEmpty()) {//過濾下IPV6地址 http://192.168.1.64/onvif/device_service http://[fe80::9a8b:aff:fe6e:867c]/onvif/device_serviceQStringList list = addr.split(" ");addr = list.first();//過濾掉重復的設備,發送搜索設備的命令有好幾種,某些設備支持多種命令,所以會返回多次foreach (DeviceInfo deviceInfo, deviceInfos) {if (deviceInfo.addr == addr) {return;}}//定義結構體存儲設備信息DeviceInfo deviceInfo;deviceInfo.addr = addr;deviceInfo.ip = QOnvifFunction::getIP(addr);//取出其他信息 onvif://www.onvif.org/type/NetworkVideoTransmitter onvif://www.onvif.org/name/NVR onvif://www.onvif.org/hardware/hisi onvif://www.onvif.org/location/shanghai//這里的信息是通過廣播搜索返回的無需密碼,這里還可以根據打印出來的 scopes 自行增加設備信息list = scopes.split(" ");foreach (QString str, list) {QStringList l = str.split("/");if (l.contains("name")) {deviceInfo.name = l.last();} else if (l.contains("location")) {deviceInfo.location = l.last();} else if (l.contains("hardware")) {deviceInfo.hardware = l.last();}}deviceInfos << deviceInfo;emit receiveDevice(deviceInfo);emit receiveInfo(QString("發現新設備-> %1").arg(addr));} }bool QOnvifSearch::search(const QString &ip) {deviceInfos.clear();if (!QOnvifFunction::isIP(ip)) {return false;}//如果還未成功則先綁定if (!isOk) {isOk = udpSocket->bind(QHostAddress(ip), 0, QUdpSocket::ShareAddress);//udpSocket->joinMulticastGroup(OnvifAddr);}if (isOk) {//之前是直接全部放在這里發送,發現部分設備要好幾次才能回來//改成定時器排隊發送多種廣播搜索數據,就沒有問題currentFile = ":/send/search1.xml";timer->stop();timer->start();} else {emit receiveError(QString("綁定組播失敗-> %1").arg(udpSocket->errorString()));}return isOk; }QList<QOnvifSearch::DeviceInfo> QOnvifSearch::getDeviceInfos() {return this->deviceInfos; }QStringList QOnvifSearch::getAddrs() {QStringList addrs;foreach (DeviceInfo deviceInfo, deviceInfos) {addrs << deviceInfo.addr;}return addrs; }總結
以上是生活随笔為你收集整理的Qt编写Onvif搜索及云台控制工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 世界坐标系,摄像机坐标系、图像坐标系关系
- 下一篇: python轮廓函数的使用