一、前言
- CMU(Center Manager Unit),即中心管理单位
- PU(Prefocus Unit),即监控前端单元,负责在CMU的控制下使用摄像机采集视频流、使用麦克风采集音频流、使用控制口采集报警信息、对摄像机云台镜头进行控制
- CU(Client Unit),监控系统的监控客户端单元,负责将PU采集到的视频流、音频流、报警信息提交给监控用户,并根据用户要求操作PU设备,如云台、镜头等
二、传统视频监控系统的一个局域网应用场景
- PU设备上线后,向CMU注册,建立连接
- CMU与PU进行信令交互,请求能力集,获取配置
- CU上线,向CMU注册,建立连接
- CMU与CU进行信令交互,传输设备列表
- CU向PU请求码流
三、应用onvif规范后对应场景
- PU设备上线后,向CMU发送HELLO消息
- CMU需要搜寻设备时,向PU发送PROBE消息
- CMU与PU进行信令交互,请求能力集,获取配置
- CU上线,向CMU注册,建立连接
- CMU与CU进行信令交互,传输设备列表
- 在CMU的协调下,CU同PU建立连接传输码流
有了onvif之后
- PU与CMU的交互方式发生了改变,CMU不再与PU保持长连接
- 遵循ONVIF规范,信令以及消息内容有了统一的标准
四、设备发现main函数说明
#include <iostream>
#include "wsdd.nsmap"
#include "soapH.h"
using namespace std;
int main()
{
/*****声明变量***********/
structsoap *soap; //soap环境变量
structwsdd__ProbeType req; //客户端发送的Probe
struct__wsdd__ProbeMatches resp; //服务端回的Probematchs
structwsdd__ScopesType sScope; //Probe里面的范围
structSOAP_ENV__Header header; //SOAP的头
intresult = 0; //返回值
int count = 0; //获得的设信息备个数
/**获取uuid(windows下叫guid,linux下叫uuid),格式为urn:uuid:8-4-4-4-12,由系统随机产生**/
staticchar buf[64] = {0}; //用来保存uuid号
UUID uuid; /*声明guid为GUID结构体变量,包含4个变量,分别是
unsigned longData1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
*/
if (S_OK== CoCreateuuid(&guid)) //如果uuid生成成功,则将其转为字符串,保存在buf中
{
_snprintf(buf,sizeof(buf)
,"urn:uuid:%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X"
, uuid.Data1
, uuid.Data2
, uuid.Data3
, uuid.Data4[0], uuid.Data4[1]
, uuid.Data4[2], uuid.Data4[3], uuid.Data4[4],uuid.Data4[5]
, uuid.Data4[6], uuid.Data4[7]
);
}
soap = soap_new(); //初始化soap
if(soap==NULL)
{
return -1;
}
soap_set_namespaces(soap,namespaces); //设置命名空间
soap->recv_timeout = 5; //设置接收Probematchs时间,超过5秒钟没有数据就退出
soap_default_SOAP_ENV__Header(soap,&header); //将header设置为soap消息的头属性
/*****给头赋值******/
header.wsa__MessageID =buf;
header.wsa__To="urn:schemas-xmlsoap-org:ws:2005:04:discovery";
header.wsa__Action="http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
soap->header = &header;
/*设置所需寻找设备的类型和范围,二者至少设定一个,否则可能收到非ONVIF设备,出现异常*/
soap_default_wsdd__ScopesType(soap,&sScope);
sScope.__item ="onvif://www.onvif.org"; //设置所需设备的sScope
soap_default_wsdd__ProbeType(soap,&req);
req.Scopes = &sScope;
req.Types ="tdn:NetworkVideoTransmitter";
/*设置所需设备的类型,tdn为命名空间前缀,为wsdd.nsmap文件中{"tdn","http://www.onvif.org/ver10/network/wsdl"}的tdn,如果不是tdn,而是其它,如ns1这里也要随之改为ns1*/
//通过组播发送Probe探针,发送成功返回0,否则-1
result = soap_send___wsdd__Probe(soap,"soap.udp://239.255.255.250:3702", NULL, &req);
if(result==-1)
{
cout<<"soap error:"<<soap->error<<soap_faultcode(soap)
<<"---"<<soap_faultstring(soap)<<endl;
}else
{
do{
result = soap_recv___wsdd__ProbeMatches(soap,&resp);
//接收ProbeMatches,成功返回0,否则-1
if (result==-1)
{
cout<<"共发现"<<count<<"个设备"<<endl;
cout<<"soap error:"<<soap->error<<soap_faultcode(soap)
<<"---"<<soap_faultstring(soap)<<endl;
break;
}else
{
count++;
cout<<"========================================="<<endl;
cout<<"UUID:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch->
wsa__EndpointReference.Address<<endl;
cout<<"Type:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch->Types<<endl;
cout<<"Scopes:"<<""<< resp.wsdd__ProbeMatches->
ProbeMatch->Scopes->__item<<endl;
cout<<"DeviceService Address:"<<""<<resp.wsdd__ProbeMatches->
ProbeMatch->XAddrs<<endl;
cout<<"MetadataVersion:"<<""<<resp.wsdd__ProbeMatches->
ProbeMatch->MetadataVersion<<endl;
}
}while(1);
}
/********清除变量************/
soap_destroy(soap); // removedeserialized class instances (C++ only)
soap_end(soap); //clean up and remove deserialized data
soap_done(soap);
return result;
}
五、CMakeList说明
- soap目录编译成了libonvif_soap,依赖openssl,还需要定义两个宏WITH_OPENSSL和 WITH_DOM
- onvif目录编译成了libonvif,依赖libonvif_soap.
cmake_minimum_required(VERSION 3.0)
project(OnvifDemo)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-g -O0")
set(LIB_SOAP_SRC
soap/struct_timeval.cpp
soap/duration.cpp
soap/wsaapi.cpp
soap/dom.cpp
soap/wsseapi.cpp
soap/smdevp.cpp
soap/mecevp.cpp
soap/threads.cpp
soap/stdsoap2.cpp)
add_library(onvif_soap STATIC ${LIB_SOAP_SRC})
target_link_libraries(onvif_soap PUBLIC ssl crypto)
target_compile_definitions(onvif_soap PUBLIC WITH_OPENSSL WITH_DOM)
target_include_directories(onvif_soap PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/onvif)
add_library(onvif STATIC onvif/soapC.cpp onvif/soapClient.cpp)
target_include_directories(onvif PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(onvif PUBLIC onvif_soap)
add_executable(dev_scan tests/scan_device.cpp)
target_link_libraries(dev_scan PRIVATE onvif)
六、调试过程注意事项
1)出现如下语法错误
error C2143:语法错误 : 缺少“{”(在“:”的前面)
error C2059:语法错误 : “:”
error C2143:语法错误 : 缺少“{”(在“:”的前面)
需要将工程中的.c文件改成.cpp文件即可
2)无法解析的外部命令错误soap_check_faultsubcode
在stdsoap2.h中,声明的soap_check_faultsubcode(structsoap *soap)函数在soapC.cpp中未实现, 可在soapC.cpp中添加如下实现
SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultsubcode(struct soap *soap)
{
soap_fault(soap);
if(soap->version == 2)
{
if(soap->fault->SOAP_ENV__Code &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode)
returnsoap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode->SOAP_ENV__Value;
return NULL;
}
returnsoap->fault->faultcode;
}
3) 无法解析的外部命令错误soap_check_faultdetail
在stdsoap2.h中,声明的soap_check_faultdetail(struct soap *soap)函数在soapC.cpp中未实现,,可在soapC.cpp中添加如下实现
SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultdetail(struct soap *soap)
{
soap_fault(soap);
if(soap->version == 2 && soap->fault->SOAP_ENV__Detail)
returnsoap->fault->SOAP_ENV__Detail->__any;
if(soap->fault->detail)
return soap->fault->detail->__any;
returnNULL;
}
4) 出现无法解析的外部符号_soap_in_xsd__duration
无法解析的外部符号_soap_in_xsd__duration,该符号在函数_soap_getelement中被引用soapC.obj : error LNK2019: 无法解析的外部符号_soap_out_xsd__duration,该符号在函数_soap_putelement中被引用
soapC.obj: error LNK2019: 无法解析的外部符号_soap_default_xsd__duration,该符号在函数_soap_default__tse__FindMetadata中被引用
需要将custom文件夹下面的duration.h和duration.c导入工程中
5)如果是调用soap_call_XXXX_Probe()来实现设备发现时,不能发现所有onvif设备
该函数实现过程中只有一次接收过程,所以无法发现所有的设备的问题。如果使用该函数,还需要对函数的实现做以下更改:
函数的接收部分,将原来的XXXX:Response更改为YYYY:ProbeMatches
其中,
- XXXX是.nsmap文件中http://www.onvif.org/ver10/network/wsdl"所对应的命名空间前缀
- YYYY与后面YYYY:ProbeMatchesType中的前缀相同,都是Web Services Dynamic Discovery (WS-Discovery)所对应的命名空间前缀名
6)在VS中出现fatal error C1128: 节数超过对象文件格式限制:请使用/bigobj 进行编译的错误
这是由于源代码文件太大,需添加选项/bigobj,在项目属性-> C/C++ ->命令行的附加选项中添加/bigobj
七、总结
1)了解gsoap工具的使用方法、编程方法以及文件结构,参考Genivia - gSOAP 用户指南,可根据需要查找相关内容
2)学会利用抓包工具,用于分析客户端所发出的消息,以及服务端回复的消息
标签:__,uuid,soap,应用,cpp,SOAP,onvif From: https://blog.csdn.net/janet110617/article/details/141677910