一、前言
最近業余時間主要研究音視頻開發這塊,前面的文章寫了好多種視頻監控內核,一旦將這些內核搞定以后,視頻監控的相關功能水到渠成,做視頻監控系統,繞不過onvif這玩意,這玩意主要就是為了統一一個大概的標準,能夠對各個廠家的監控設備進行常用的一些操作,比如搜索、獲取資訊、云臺控制、事件訂閱、抓拍圖片等,如果沒有這個規范,那么各個廠家都各自為政,需要用私有的sdk去處理,這樣就很麻煩很慘了,幾十個廠家就需要幾十個sdk,對于程式員來說簡直是災難,想想就很恐怖的事情,哪個程式員不想多活幾年!
onvif設備搜索是最基本的功能,想要對設備進行進一步的處理,必須先搜索到設備,默認onvif搜索只能搜索到同一個網段的設備,要跨網段的話,需要手動指定設備的IP地址或者onvif地址進行搜索,這兩者在封裝的onvif類中都考慮到了,經歷過各種復雜的現場情況的考驗,也可以算是本系統的一個小特色吧,
近期又重新把獨創的方法實作的onvif工具重新重構了下,各個類之間非常清晰明了,增強了兼容性和完整性,在之前的基礎上還增加了很多基礎的處理比如視頻引數和圖片引數的獲取,設定時間等,同時還增加了可以指定過濾條件對搜索的設備進行過濾,這個非常有用,很多時候現場各種型別的各個廠家的攝像機非常多,一般來說一個型別的攝像機對應的onvif地址基本一致,埠也是一致,這樣可以指定格式進行過濾,只顯示過濾后的設備,還增加了搜索間隔,經過現場無數次的測驗各種廠家,發現搜索命令可能要發好幾種好幾次,以便所有設備都能搜索到,畢竟搜索采用廣播的UDP,意味著可能丟包,
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
五、核心代碼
void OnvifSearch::sendData()
{
//依次發送資料,如果到了最后一個則停止
//根據onvif device test工具抓包分析,只要發送前面兩個就行,后面兩個是ONVIF Device Manager抓包的
//在收到結果的地方要對重復的進行過濾,因為部分設備兩種協議請求都會回傳
//可以自行調整 timerSearch->stop(); 的位置來提高速度,比如很多情況下只要發送一次就可以
writeData("239.255.255.250");
if (currentFile == ":/send/searchDevice1.xml") {
currentFile = ":/send/searchDevice2.xml";
} else if (currentFile == ":/send/searchDevice2.xml") {
currentFile = ":/send/searchDevice3.xml";
} else if (currentFile == ":/send/searchDevice3.xml") {
currentFile = ":/send/searchDevice4.xml";
} else if (currentFile == ":/send/searchDevice4.xml") {
timerSearch->stop();
}
}
void OnvifSearch::writeData(const QString &ip)
{
QByteArray data = OnvifHelper::getFile(currentFile);
if (!data.isEmpty()) {
data = QString(data).arg(OnvifHelper::getUuid()).toUtf8();
udpSocket->writeDatagram(data, QHostAddress(ip), 3702);
emit sendData(data);
}
}
void OnvifSearch::readData()
{
QByteArray data;
QHostAddress host;
quint16 port = 0;
while (udpSocket->hasPendingDatagrams()) {
data.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(data.data(), data.size(), &host, &port);
QString ip = host.toString();
checkData(data, ip);
emit receiveData(data);
//qDebug() << TIMEMS << ip << port << data.size();
}
}
void OnvifSearch::readData(const QString &file)
{
//從檔案讀取資料決議,主要方便用來測驗各種攝像機回傳的資料
QFile f(file);
if (f.open(QFile::ReadOnly)) {
QByteArray data = f.readAll();
data = data.replace("\\\"", "\"");
checkData(data, "");
}
}
void OnvifSearch::checkData(const QByteArray &data, const QString &ip)
{
OnvifQuery query;
query.setData(data);
QString discovery = query.getDiscovery();
QString addr_path = QString("//%1:ProbeMatches/%1:ProbeMatch/%1:XAddrs").arg(discovery);
QString scopes_path = QString("//%1:ProbeMatches/%1:ProbeMatch/%1:Scopes").arg(discovery);
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_service
//發現還有串資料的要過濾 http://192.168.10.152/onvif/device_service http://192.168.10.172/onvif/device_service http://[fe80::9a8b:aff:fe4a:ad]/onvif/device_service
QStringList list = addr.split(" ");
if (ip.isEmpty()) {
addr = list.first();
} else {
foreach (QString str, list) {
if (str.contains(ip)) {
addr = str;
break;
}
}
}
//已經存在的地址不用繼續
if (checkExist(addr)) {
return;
}
//按照過濾條件過濾設備 適用于設備很多而且設備型別很多的情況
if (searchFilter != "none") {
//默認80埠的不會帶 :80 前面有 http: https: 所以要取后面的 : 索引位置來判斷
if (searchFilter == ":80") {
if (addr.indexOf(":", 8) >= 8) {
return;
}
} else {
if (!addr.contains(searchFilter)) {
return;
}
}
}
//定義結構體存盤設備資訊
DeviceInfo deviceInfo;
deviceInfo.addr = addr;
deviceInfo.ip = OnvifHelper::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));
}
}
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns="http://www.w3.org/2003/05/soap-envelope">
<Header>
<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">uuid:%1</wsa:MessageID>
<wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>
<wsa:Action xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>
</Header>
<Body>
<Probe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery">
<Types>tds:Device</Types>
<Scopes />
</Probe>
</Body>
</Envelope>
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/158807.html
標籤:其他
上一篇:AD20.0.13初學
下一篇:C語言實作鏈表與檔案的存取
