最近業餘時間主要研究音視訊開發這塊,前面的文章寫了好多種視訊監控核心,一旦將這些核心搞定以後,視訊監控的相關功能水到渠成。做視訊監控系統,繞不過onvif這玩意,這玩意主要就是為了統一一個大概的標準,能夠對各個廠家的監控裝置進行常用的一些操作,比如搜尋、獲取資訊、雲臺控制、事件訂閱、抓拍圖片等,如果沒有這個規範,那麼各個廠家都各自為政,需要用私有的sdk去處理,這樣就很麻煩很慘了,幾十個廠家就需要幾十個sdk,對於程式設計師來說簡直是災難,想想就很恐怖的事情,哪個程式設計師不想多活幾年!
onvif裝置搜尋是最基本的功能,想要對裝置進行進一步的處理,必須先搜尋到裝置,預設onvif搜尋只能搜尋到同一個網段的裝置,要跨網段的話,需要手動指定裝置的IP地址或者onvif地址進行搜尋,這兩者在封裝的onvif類中都考慮到了,經歷過各種複雜的現場情況的考驗,也可以算是本系統的一個小特色吧。
近期又重新把獨創的方法實現的onvif工具重新重構了下,各個類之間非常清晰明瞭,增強了相容性和完整性,在之前的基礎上還增加了很多基礎的處理比如視訊引數和圖片引數的獲取,設定時間等,同時還增加了可以指定過濾條件對搜尋的裝置進行過濾,這個非常有用,很多時候現場各種型別的各個廠家的攝像機非常多,一般來說一個型別的攝像機對應的onvif地址基本一致,埠也是一致,這樣可以指定格式進行過濾,只顯示過濾後的裝置。還增加了搜尋間隔,經過現場無數次的測試各種廠家,發現搜尋命令可能要發好幾種好幾次,以便所有裝置都能搜尋到,畢竟搜尋採用廣播的UDP,意味著可能丟包。
onvif主要的功能:
onvif的處理流程:
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>