如何检测 ONVIF 图像设备?

WSDiscoveryClient 类实现了检测 支持 ONVIF 协议的摄像头设备。该类主要实现了 WSDiscovery 协议(精简版)检测设备部分。

wsdiscoveryclient.h

#ifndef WSDISCOVERYCLIENT_H
#define WSDISCOVERYCLIENT_H

#include <QObject>
#include <QtCore>
#include <QtNetwork>

struct WSDiscoveryClientData
{
    QString EPAddress;
    QString type;
    QString deviceIP;
    QString deviceServiceAddress;
    QStringList scopes;
    QString metadataVersion;
    QDateTime lastUpdate;
};

class WSDiscoveryClient : public QObject
{
    Q_OBJECT
public:
    explicit WSDiscoveryClient(QObject *parent = nullptr);
    ~WSDiscoveryClient();
    void probe();//检测
signals:
    void discoveryNVT(QString ip,QString serviceAddress);//检测到设备会发送该信号,连接即可。
private:
    void initSocket();
    void readPendingDatagrams();
    void processTheDatagram(const QNetworkDatagram &datagram);
private:
    QUdpSocket *m_socket;
    QHash<QString,WSDiscoveryClientData> m_data;
};

QDebug operator<<(QDebug dbg,const WSDiscoveryClientData &obj);
#endif // WSDISCOVERYCLIENT_H

wsdiscoveryclient.cpp

#include "wsdiscoveryclient.h"

#define WS_DISCOVERY_ADDRESS "239.255.255.250"
#define WS_DISCOVERY_PORT 3702

//Q_LOGGING_CATEGORY(WSDiscoveryClient, "WSDiscoveryClient.log",QtInfoMsg)

static QString ProbeXML(int device_type/*Device,NVT NetworkVideoTransmitter*/)
{
    QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
    QString device = device_type ? "tds:Device" : "tdn:NetworkVideoTransmitter";
    QString cmd(R"(<?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>%2</Types><Scopes /></Probe></Body></Envelope>)");
    return cmd.arg(uuid).arg(device).remove("
");
}

WSDiscoveryClient::WSDiscoveryClient(QObject *parent) : QObject(parent)
  ,m_socket(new QUdpSocket(this))
{
    connect(m_socket,&QUdpSocket::readyRead,this,&WSDiscoveryClient::readPendingDatagrams);
    connect(m_socket,QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),[=](QAbstractSocket::SocketError socketError){
        qDebug() << socketError;
    });
    connect(m_socket,QOverload<QAbstractSocket::SocketState>::of(&QAbstractSocket::stateChanged),[=](QAbstractSocket::SocketState socketState){
        qDebug() << socketState;
    });

    initSocket();
}

WSDiscoveryClient::~WSDiscoveryClient()
{
}

void WSDiscoveryClient::probe()
{
    for(auto &v:{/*1,*/0}){
        QByteArray data = ProbeXML(v).toUtf8();
        int ret = m_socket->writeDatagram(data.data(),data.length(),QHostAddress(WS_DISCOVERY_ADDRESS),WS_DISCOVERY_PORT);
        if(!ret){
            qWarning() << "Probe Send Failed";
        }
    }
}

void WSDiscoveryClient::initSocket()
{
    if(!m_socket->bind(QHostAddress(WS_DISCOVERY_ADDRESS),WS_DISCOVERY_PORT,QAbstractSocket::ShareAddress)){
        qCritical() << QString("Binding [address:%1] [port:%2] failure.").arg(WS_DISCOVERY_ADDRESS).arg(WS_DISCOVERY_PORT);
    }
}

void WSDiscoveryClient::readPendingDatagrams()
{
    //qDebug() << "readPendingDatagrams";
    while (m_socket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = m_socket->receiveDatagram();
        processTheDatagram(datagram);
    }
}

/*!
<?xml version="1.0" encoding="utf-8" ?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
    xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
    xmlns:env="http://www.w3.org/2003/05/soap-envelope"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
    xmlns:dndl="http://www.onvif.org/ver10/network/wsdl/DiscoveryLookupBinding"
    xmlns:dnrd="http://www.onvif.org/ver10/network/wsdl/RemoteDiscoveryBinding"
    xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"
    xmlns:dn="http://www.onvif.org/ver10/network/wsdl">
    <SOAP-ENV:Header>
        <wsa:MessageID>urn:uuid:c25a2a64-1dd1-11b2-8339-00040502673d</wsa:MessageID>
        <wsa:RelatesTo>uuid:28e58635-58a1-4d5e-b1f6-c2557fa253d2</wsa:RelatesTo>
        <wsa:To SOAP-ENV:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsa:Action>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body>
        <d:ProbeMatches><d:ProbeMatch>
        <wsa:EndpointReference>
            <wsa:Address>urn:uuid:c25a2a64-1dd1-11b2-8339-00040502673d</wsa:Address>
        </wsa:EndpointReference>
        <d:Types>dn:NetworkVideoTransmitter</d:Types>
        <d:Scopes>
            onvif://www.onvif.org/type/Network_Video_Transmitter
            onvif://www.onvif.org/type/video_encoder
            onvif://www.onvif.org/location/MyCamera
            onvif://www.onvif.org/hardware/RDC
            onvif://www.onvif.org/name/IPCAMERA
            onvif://www.onvif.org/type/audio_encoder
            onvif://www.onvif.org/Profile/Streaming
        </d:Scopes>
        <d:XAddrs>http://192.168.1.180/onvif/device_service</d:XAddrs>
        <d:MetadataVersion>1</d:MetadataVersion>
        </d:ProbeMatch></d:ProbeMatches>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
*/
void WSDiscoveryClient::processTheDatagram(const QNetworkDatagram &datagram)
{
    const QByteArray &request = datagram.data();

    auto GetElementValue = [&](const QByteArray &start,const QByteArray &end,const QByteArray &data)->QByteArray{
        int s_pos = data.lastIndexOf(start);
        int e_pos = data.lastIndexOf(end);
        if(s_pos != -1 && e_pos != -1){
            return data.mid(s_pos + start.length(),e_pos - s_pos - start.length());
        }
        return QByteArray();
    };

    const QByteArray body = GetElementValue("<SOAP-ENV:Body>","</SOAP-ENV:Body>",request);
    WSDiscoveryClientData discoveryClientData;
    discoveryClientData.EPAddress = GetElementValue("<wsa:Address>","</wsa:Address>",body);
    discoveryClientData.type = GetElementValue("<d:Types>","</d:Types>",body);
    QString scopes = GetElementValue("<d:Scopes>","</d:Scopes>",body);
    discoveryClientData.scopes = scopes.split(" ");
    discoveryClientData.deviceServiceAddress = GetElementValue("<d:XAddrs>","</d:XAddrs>",body);
    if(!discoveryClientData.deviceServiceAddress.isEmpty()){
        QString serviceAddress = discoveryClientData.deviceServiceAddress;
        serviceAddress.remove("http://");
        serviceAddress.remove("https://");
        int pos = serviceAddress.indexOf(R"(/)");
        if(pos != -1){
            discoveryClientData.deviceIP = serviceAddress.mid(0,pos);
            discoveryClientData.lastUpdate = QDateTime::currentDateTimeUtc();
        }
    }
    discoveryClientData.metadataVersion = GetElementValue("<d:MetadataVersion>","</d:MetadataVersion>",body);

    qDebug() << discoveryClientData;
}

QDebug operator<<(QDebug dbg, const WSDiscoveryClientData &obj)
{
    QDebugStateSaver saver(dbg);
    dbg << obj.EPAddress
        << obj.type
        << obj.deviceIP
        << obj.deviceServiceAddress
        << obj.scopes
        << obj.metadataVersion
        << obj.lastUpdate;
    return dbg;
}
原文地址:https://www.cnblogs.com/cheungxiongwei/p/14266001.html