ONVIF开发总结
日期:2014-09-25点击次数:8698
一、 Onvif 简介
不同厂商生产的网络视频产品都定义了自己的接口,因此不同厂商的设备使用方式区别很大,这就造成了系统集成过程中的软件研发困难。
为此,2008年5月,由安讯士联合博世及索尼公司三方宣布将携手共同成立一个国际开放型网络视频产品标准网络接口开发论坛,取名为ONVIF(Open Network Video Interface Forum,开放型网络视频接口论坛),并以公开、开放的原则共同制定开放性行业标准。
ONVIF规范描述了网络视频的模型、接口、数据类型以及数据交互的模式。并复用了一些现有的标准,如WS系列标准等。ONVIF规范的目标是实现一个网络视频框架协议,使不同厂商所生产的网络视频产品(包括摄录前端、录像设备等)完全互通。
ONVIF规范中设备管理和控制部分所定义的接口均以Web Services的形式提供。ONVIF规范涵盖了完全的XML及WSDL的定义。每一个支持ONVIF规范的终端设备均须提供与功能相应的Web Service。服务端与客户端的数据交互采用SOAP协议。ONVIF中的其他部分比如音视频流则通过RTP/RTSP进行。
二、 Onvif的优势
1、不同厂商所提供的产品,均可以通过一个统一的“语言”来进行交流。方便了系统的集成。
2、终端用户和集成用户不需要被某些设备的固有解决方案所束缚。大大降低了开发成本。
3、不断扩展的规范将由市场来导向,遵循规范的同时也满足主流的用户需求。使用ONVIf协议保证了程序的可靠性和稳定性。
三、 生成
1. 生成onvif.h头文件的方法
产生头文件有两种方法:链接网络生成和本地生成。
1) wsdl2h相关命令参数
-c 产生c语言的代码,否则产生C++
-s 不使用STL代码
-t 指定typemap.dat文件
-o 指定生成的头文件名
2) 链接网络生成
l 将wsdl2.exe和typemap.dat文件放入同一个文件夹
l 利用cmd或批处理执行以下dos命令:
wsdl2h.exe -c -s -t typemap.dat -o onvif.h
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl http://www.onvif.org/onvif/ver10/deviceio.wsdl http://www.onvif.org/onvif/ver10/display.wsdl http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl http://www.onvif.org/onvif/ver10/recording.wsdl http://www.onvif.org/onvif/ver10/replay.wsdl http://www.onvif.org/onvif/ver10/search.wsdl http://www.onvif.org/onvif/ver10/receiver.wsdl http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl
3) 下载到本地生成
l 从ONVIF官网上把相关的WSDL文档下载到本地,下载地址(详细参见备注说明),此外还需下载样式表,下载地址见WSDL文档中的schemaLocation。
l 将wsdl2.exe、typemap.dath、WSDL文档和样式表放入同一文件夹中。
l 将相关WSDL文档中的样式表引入路径(schemaLocation)修改为本地实际地址,如:event.wsdl中schemaLocation="http://www.w3.org/2005/08/addressing/ws-addr.xsd修改为schemaLocation = ws-addr.xsd
l 利用cmd或批处理执行以下命令
wsdl2h.exe -c -s -t typemap.dat -o onvif.h remotediscovery.wsdl devicemgmt.wsdl analytics.wsdl analyticsdevice.wsdl media.wsdl deviceio.wsdl display.wsdl event.wsdl imaging.wsdl recording.wsdl replay.wsdl search.wsdl receiver.wsdl ptz.wsdl
4) 备注说明
l wsdl2.exe位于gsoap_2.8.14\gsoap-2.8\gsoap\bin
l typemap.dat位于gsoap_2.8.14\gsoap-2.8\gsoap
l wsdl下载地址:http://www.onvif.org/Documents/Specifications.aspx
l 各url之间用空格隔开
l typemap.dat不需要修改
l 链接网络生成方法的优点是不用考虑以上文件对其它文件的依赖关系,不用修改引入路径。该方法的缺点跟网速有关,因此中途可能会中断,如果超过5分钟未生成,可重新执行命令, 通过代理上网则无法生成,不推荐用此方法。
l 下载本地生成方法的优缺点和链接网络方法生成相反,代理网推荐使用此方法。
2. 生成onvif源代码
1) 整理相关的文件
把刚生成的onvif.h与soapcpp2.exe、import和custom放入同一文件夹,其中:
soapcpp2.exe位于gsoap_2.8.14\gsoap-2.8\gsoap\bin
import位于gsoap_2.8.14\gsoap-2.8\gsoap
custom位于gsoap_2.8.14\gsoap-2.8\gsoap
2) 通过以下命令生成相关源文件
soapcpp2.exe -2 –c onvif.h -I import
3) Soapcpp2相关命令参数
-2 采用SOAP1.2,
-x 不产生xml文件(不推荐使用此命令,因为xml文档很有用)
-I 为引入路径
-C 只产生客户端代码(注意:C是大写)
4) 备注说明
l 需要在onvif.h中加入#import "wsse.h,用来做安全验证
l 需要将import目录下的wsa5.h以下部分注释掉,否则编译时会报soap_xxxx_SOAP_ENV__Fault()函数重复定义。
int SOAP_ENV__Fault
( _QName faultcode, // SOAP 1.1
char *faultstring, // SOAP 1.1
char *faultactor, // SOAP 1.1
struct SOAP_ENV__Detail *detail, // SOAP 1.1
struct SOAP_ENV__Code *SOAP_ENV__Code, // SOAP 1.2
struct SOAP_ENV__Reason *SOAP_ENV__Reason, // SOAP 1.2
char *SOAP_ENV__Node, // SOAP 1.2
char *SOAP_ENV__Role, // SOAP 1.2
struct SOAP_ENV__Detail *SOAP_ENV__Detail, // SOAP 1.2
void);
3. 利用gsoap2.8.8生成源代码和gsoap2.8.14生成的差异
利用gsoap2.8.8生成源代码方法跟gsoap2.8.14基本一致,但需注意以下区别:
1) typemap.dat文件中需要添加相关信息
tds = "http://www.onvif.org/ver10/device/wsdl"
tev = "http://www.onvif.org/ver10/events/wsdl"
tls = "http://www.onvif.org/ver10/display/wsdl"
tmd = "http://www.onvif.org/ver10/deviceIO/wsdl"
timg = "http://www.onvif.org/ver20/imaging/wsdl"
trt = "http://www.onvif.org/ver10/media/wsdl"
tptz = "http://www.onvif.org/ver20/ptz/wsdl"
trv = "http://www.onvif.org/ver10/receiver/wsdl"
trc = "http://www.onvif.org/ver10/recording/wsdl"
tse = "http://www.onvif.org/ver10/search/wsdl"
trp = "http://www.onvif.org/ver10/replay/wsdl"
tan = "http://www.onvif.org/ver20/analytics/wsdl"
tad = "http://www.onvif.org/ver10/analyticsdevice/wsdl"
tdn = "http://www.onvif.org/ver10/network/wsdl"
tt = "http://www.onvif.org/ver10/schema"
# OASIS recommended prefixes
wsnt = "http://docs.oasis-open.org/wsn/b-2"
wsntw = "http://docs.oasis-open.org/wsn/bw-2"
wsrfbf = "http://docs.oasis-open.org/wsrf/bf-2"
wsrfr = "http://docs.oasis-open.org/wsrf/r-2"
wsrfrw = "http://docs.oasis-open.org/wsrf/rw-2"
wstop = "http://docs.oasis-open.org/wsn/t-1"
# WS-Discovery 1.0 remapping
wsdd10__HelloType = | wsdd__HelloType
wsdd10__ByeType = | wsdd__ByeType
wsdd10__ProbeType = | wsdd__ProbeType
wsdd10__ProbeMatchesType = | wsdd__ProbeMatchesType
wsdd10__ProbeMatchType = | wsdd__ProbeMatchType
wsdd10__ResolveType = | wsdd__ResolveType
wsdd10__ResolveMatchesType = | wsdd__ResolveMatchesType
wsdd10__ResolveMatchType = | wsdd__ResolveMatchType
# SOAP-ENV mapping
SOAP_ENV__Envelope = struct SOAP_ENV__Envelope { struct SOAP_ENV__Header *SOAP_ENV__Header; _XML SOAP_ENV__Body; }; | struct SOAP_ENV__Envelope
SOAP_ENV__Header = | struct SOAP_ENV__Header
SOAP_ENV__Fault = | struct SOAP_ENV__Fault
SOAP_ENV__Detail = | struct SOAP_ENV__Detail
SOAP_ENV__Code = | struct SOAP_ENV__Code
SOAP_ENV__Subcode = | struct SOAP_ENV__Subcode
SOAP_ENV__Reason = | struct SOAP_ENV__Reason
如果typedef.map文件中存在以下两行,那么进行注释
_wsse2__Security == _wsse__Security
wsse2__Security == wsse__Security
2) wsa5.h中无 SOAP_ENV__Fault函数
由于wsa5.h中没有SOAP_ENV__Fault因此不会产生代码重复,因此不用注释。
调试代码
1. 新建一个项目
将上面生成的soapH.h、 soapStub.h、 wsdd.nsmap、soapC.c、 soapClient.c,还有位于gsoap-2.8\gsoap 的:stdsoap2.c、stdsoap2.h和位于\custom中的:duration.c放入工程中,然后编写main函数,调试代码。
2. 文件主要功能说明
wsdd.nsmap 名空间定义,服务器端与客户端都要包含它,里面有很多,都是
相同的,只需导入一个进入工程就行
stdsoap2.h Header _le of stdsoap2.cpp runtime library
stdsoap2.c Runtime C library with XML parser and run-time support routines soapStub.h soapH.h //Main header file to be included by all client and service sources
soapC.c //Serializers and deserializers for the specfied data structures
soapClient.c //Client stub routines for remote operations
soapStub.h A modi_ed and annotated header file produced from the input header file
很多已经声明的类型,在文件编译时提示类型无定义,将扩展名改为cpp进行编译即可。
函数说明
#include <iostream>
#include "wsdd.nsmap"
#include "soapH.h"
using namespace std;
int main()
{
/*****声明变量***********/
struct soap *soap; //soap环境变量
struct wsdd__ProbeType req; //客户端发送的Probe
struct __wsdd__ProbeMatches resp; //服务端回的Probematchs
struct wsdd__ScopesType sScope; //Probe里面的范围
struct SOAP_ENV__Header header; //SOAP的头
int result = 0; //返回值
int count = 0; //获得的设信息备个数
/**获取guid(windows下叫guid,linux下叫uuid),格式为urn:uuid:8-4-4-4-12,由系统随机产生**/
static char buf[64] = {0}; //用来保存guid号
GUID guid; /*声明guid为GUID结构体变量,包含4个变量,分别是
unsigned longData1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
*/
if (S_OK == CoCreateGuid(&guid)) //如果guid生成成功,则将其转为字符串,保存在buf中
{
_snprintf(buf,sizeof(buf)
, "urn:uuid:%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X"
, guid.Data1
, guid.Data2
, guid.Data3
, guid.Data4[0], guid.Data4[1]
, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]
, guid.Data4[6], guid.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<<"Device Service Address:"<<" "<<resp.wsdd__ProbeMatches->
ProbeMatch->XAddrs<<endl;
cout<<"MetadataVersion:"<<" "<<resp.wsdd__ProbeMatches->
ProbeMatch->MetadataVersion<<endl;
}
}while(1);
}
/********清除变量************/
soap_destroy(soap); // remove deserialized class instances (C++ only)
soap_end(soap); // clean up and remove deserialized data
soap_done(soap);
return result;
}
四、调试过程遇到的问题和注意事项
1. 出现如下语法错误:
error C2143: 语法错误 : 缺少“{”(在“:”的前面)
error C2059: 语法错误 : “:”
error C2143: 语法错误 : 缺少“{”(在“:”的前面)
需要将工程中的.c文件改成.cpp文件即可。
2. 无法解析的外部命令错误soap_check_faultsubcode
在stdsoap2.h中声明的soap_check_faultsubcode(struct soap *soap)函数在soapC.cpp中未实现, 可在soapC.cpp中添加如下实现:
SOAP_FMAC3 const char * SOAP_FMAC4 soap_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)
return soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode->SOAP_ENV__Value;
return NULL;
}
return soap->fault->faultcode;
}
3. 无法解析的外部命令错误soap_check_faultdetail
在stdsoap2.h中声明的soap_check_faultdetail(struct soap *soap)函数在soapC.cpp中未实现, 可在soapC.cpp中添加如下实现:
SOAP_FMAC3 const char * SOAP_FMAC4 soap_check_faultdetail(struct soap *soap)
{
soap_fault(soap);
if (soap->version == 2 && soap->fault->SOAP_ENV__Detail)
return soap->fault->SOAP_ENV__Detail->__any;
if (soap->fault->detail)
return soap->fault->detail->__any;
return NULL;
}
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. 在VS中出现fatal error C1128: 节数超过对象文件格式限制: 请使用/bigobj 进行编译的错误
这是由于源代码文件太大的原因,需添加选项/bigobj,在项目属性 -> C/C++ ->命令行的附加选项中添加/bigobj。
软件部 刘彦鑫