Onvif协议做安防监控的人应该都很熟悉,它广泛被用在各种安防设备中,比如IPC、DVR、NVR。在局域网中发现IPC设备很多就是基于Onvif协议实现的。本文章给大家介绍QuickGBLink开源的一个工具,该工具能够通过Onvif的设备发现将局域网内的Onvif IPC枚举出来,显示到界面的列表中,并且可以获得每个IPC设备的RTSP流地址。该工具界面截图如下:
Onvif客户端向网段内固定的某个组播地址发送一段广播报文,这个组播地址就是:239.255.255.250,端口:3702。而Onvif服务端(响应消息的设备)在固定端口上监听,接收到广播消息之后向发送者回复消息,回复的消息里返回设备自己的IP、UUID、EP Address、Type等,最重要的会返回设备的Device Service Address,格如:http://192.168.0.64:8099/onvif/device_service ,此地址就是为设备提供Onvif服务的服务地址。
Onvif客户端向局域网内固定组播组发送的广播报文:
uuid:25862dc4-4866-42a9-87a7-edcf2918ab10
urn:schemas-xmlsoap-org:ws:2005:04:discovery
http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe
xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery">
Onvif服务端回复的报文,消息格式如下:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xs="http://www.w3.org/2000/10/XMLSchema"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-
secext-1.0.xsd"
xmlns:wsa5="http://www.w3.org/2005/08/addressing"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:ns1="http://www.w3.org/2005/05/xmlmime"
xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"
xmlns:ns7="http://docs.oasis-open.org/wsrf/r-2"
xmlns:ns2="http://docs.oasis-open.org/wsrf/bf-2"
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"
xmlns:ns10="http://www.onvif.org/ver10/replay/wsdl"
xmlns:ns11="http://www.onvif.org/ver10/search/wsdl"
xmlns:ns13="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding"
xmlns:ns14="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding"
xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl"
xmlns:ns15="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding"
xmlns:ns16="http://www.onvif.org/ver10/events/wsdl/EventBinding"
xmlns:tev="http://www.onvif.org/ver10/events/wsdl"
xmlns:ns17="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding"
xmlns:ns18="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding"
xmlns:ns19="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding"
xmlns:ns20="http://www.onvif.org/ver10/events/wsdl/PullPointBinding"
xmlns:ns21="http://www.onvif.org/ver10/events/wsdl/CreatePullPointBinding"
xmlns:ns22="http://www.onvif.org/ver10/events/wsdl/PausableSubscriptionManagerBind
ing"
xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"
xmlns:ns3="http://www.onvif.org/ver10/analyticsdevice/wsdl"
xmlns:ns4="http://www.onvif.org/ver10/deviceIO/wsdl"
xmlns:ns5="http://www.onvif.org/ver10/display/wsdl"
xmlns:ns8="http://www.onvif.org/ver10/receiver/wsdl"
xmlns:ns9="http://www.onvif.org/ver10/recording/wsdl"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl"
xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"
xmlns:trt="http://www.onvif.org/ver10/media/wsdl"
xmlns:trt2="http://www.onvif.org/ver20/media/wsdl"
xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tns1="http://www.onvif.org/ver10/topics"
xmlns:tnsn="http://www.eventextension.com/2011/event/topics">
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches
urn:uuid:b12dca78-5566-7788-99aa-001217d7bf0f
onvif://www.onvif.org/hardware/IPC-model onvif://www.onvif.org/location/country/china
onvif://www.onvif.org/name/NVT onvif://www.onvif.org/Profile/Streaming
http://192.168.0.64:8099/onvif/device_service
Onvif客户端解析上面收到的数据,得到IPC摄像头的「设备服务地址」,即XAddrs字段,
我们将Onvif设备发现功能的逻辑处理都放到一个类里面实现,这个类的声明如下:
class DEVICE_IPC_INFO
{
public:
char hardware_type[128]; //硬件型号
char DeviceName[64];
char DeviceID[128]; //uuid
char IP[32];
int Port;
DEVICE_IPC_INFO()
{
memset(DeviceName, 0, sizeof(DeviceName));
memset(DeviceID, 0, sizeof(DeviceID));
memset(IP, 0, sizeof(IP));
memset(hardware_type, 0, sizeof(hardware_type));
Port = 80;
}
DEVICE_IPC_INFO& operator =(const DEVICE_IPC_INFO& t)
{
strcpy(hardware_type, t.hardware_type);
strcpy(DeviceName, t.DeviceName);
strcpy(DeviceID, t.DeviceID);
strcpy(IP, t.IP);
Port = t.Port;
return *this;
}
};
typedef void (CALLBACK* DeviceDiscoverCB)(DEVICE_IPC_INFO * pInfo, LPVOID lpContext);
class COnvifDiscovery
{
public:
COnvifDiscovery(void);
~COnvifDiscovery(void);
void SetDiscoveryInfoCB(DeviceDiscoverCB lpCallback, LPVOID lpContext);
int StartFind(void);
void StopFind();
protected:
static DWORD WINAPI ReceivingThrd(void * pParam);
int probeSocketIni(int *fd);
void probeSocketEnd(int *fd);
int sendProbe(int fd,const char *uuid,const char *types);
int recvProbeMatches(int fd,int timeout);
void ReceivingLoop(void);
int m_fd;
BOOL m_bIsReceiving;
HANDLE m_hRcvThread;
DeviceDiscoverCB m_lpCallback;
LPVOID m_lpContext;
};
这个类的头文件和源文件是Discover.h、Discover.cpp。具体实现过程这里略过。
1. 获取RTSP地址需要填写IPC的登录用户名和密码。但是,连某些摄像机,填写了用户名和密码之后,登录摄像机获取流信息失败,如下图:
错误原因可能是填写的登录IPC的用户名或密码错误,导致用户权鉴失败;也有可能是这个用户是没有Onvif访问权限(需要在IPC上做配置,这个问题多出现在海康的摄像机系列)。
关于这个问题的解决办法,请看这篇文章:
https://blog.csdn.net/QuickGBLink/article/details/103442791
2. 成功获取流信息后,可以选择流索引(流编号)从而显示不同通道的流URL,一般的摄像机有双码流,所以有两个通道的流,但是有些摄像机有三个流的。用户可以切换选择不同的流编号(通道编号),然后拷贝这个流的RTSP地址,放到VLC播放器中或我们的RTSP客户端中播放。
该工具的代码下载地址:
Github:https://github.com/QuickGBLink88/OnvifDiscoveryTool