这大概是我迄今为止碰到过最难编译的库了,其中遇到各种各样的问题,最终我是在windows下编译出了源文件,然后回到linux进行开发的。
最先要申明的一点:onvif开发并不需要编译安装gsoap,gsoap只是用来获取onvif源代码的工具!
一、准备"onvif"源码
需要gsoap来帮助我们将.wdsl生成为.h,最后再生成.c文件。因为wdsl互相之间有相关性,离线安装会出现更多的麻烦(修改更多的配置信息),所以整个过程都在联网状态
完成,虽然比较耗时,但是强烈建议。
gsoap项目地址(需要下载)
http://www.cs.fsu.edu/~engelen/soap.html
onvif项目的wdsl地址(不需要下载)
http://www.onvif.org/Documents/Specifications.aspx中的ONVIF WSDL and XML Schemas Specifications
二、生成源文件
1、准备生成环境(2.8.10以上版本应该不需要这个步骤,2.8.17r并不需要修改typemap.dat!)
解压gsoap到任意文件夹,修改gsoap目录下的typemap.dat文件,gsoap官网对onvif编译的说明如下:
How do I use gSOAP for the ONVIF specifications?
Use gSOAP 2.8.10 and up. In the typemap.dat file used by wsdl2h, add:
# ONVIF recommended prefixes
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
对typemap.dat文件添加如上代码段,并复制到编译目录gsoap\bin\win32(win32对应windows,linux386对应linux,macosx对应macosx)。
2、生成.h文件
进入编译目录gsoap\bin\win32,这时目录下会有3个文件wsdl2h.exe、soapcpp2.exe以及typemap.dat。
运行命令行
wsdl2h -P -x -c -s -t typemap.dat -o onvif.h <wsdl地址> <wsdl地址> ...
其中<wsdl地址>为http://www.onvif.org/Documents/Specifications.aspx中的ONVIF WSDL and XML Schemas Specifications列表下各个wsdl文件网络位置,用空格分隔
<wsdl地址>举例
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl
http://www.onvif.org/onvif/ver10/events/wsdl/event.wsdl
http://www.onvif.org/onvif/ver10/display.wsdl
http://www.onvif.org/onvif/ver10/deviceio.wsdl
http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl
http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl
http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl
http://www.onvif.org/onvif/ver10/receiver.wsdl
http://www.onvif.org/onvif/ver10/recording.wsdl
http://www.onvif.org/onvif/ver10/search.wsdl
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl
http://www.onvif.org/onvif/ver10/replay.wsdl
http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl
http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl
http://www.onvif.org/ver10/actionengine.wsdl
http://www.onvif.org/ver10/pacs/accesscontrol.wsdl
http://www.onvif.org/ver10/pacs/doorcontrol.wsdl
http://www.onvif.org/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl
http://www.onvif.org/onvif/ver10/schema/onvif.xsd
等待完毕,会生成onvif.h文件在编译目录,大小在3~4m左右,失败的话大小为0k。
如果需要身份验证,修改生成的onvif.h,增加
#import "wsse.h"
3、生成.c文件
进入编译目录gsoap\bin\win32,运行命令行
soapcpp2 -c -x -L -I ../../import -I ../../../gsoap/ onvif.h
使用gsoap版本gSOAP 2.8.17r stable (update) (20.7 MB)出现如下错误(这错误太低级了,完全没有宏保护)
wsa5.h(288): **ERROR**: remote method name clash: struct/class 'SOAP_ENV__Fault'
already declared at line 274
回到gsoap\import目录下修改wsa5.h,将重复申明的部分注释如下。
/*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
);*/
重新运行上面的生成命令,生成成功。在目录下会生成很多文件,有用的文件如下:
soapC.c
soapClient.c
soapServer.c(与soapClient.c根据需要选择其一)
onvif.h
soapH.h
soapStub.h
wsdd.nsmap
除此之外,还需要gsoap-2.8\gsoap目录下的两个文件:
stdsoap2.c
stdsoap2.h
这些文件放在自己的代码目录下即可
四、onvif源码的使用
1、注释soapServer.c中SOAP_ENV__Fault部分代码
编译发生以下错误:
soapServer.c:259: undefined reference to `SOAP_ENV__Fault'
这里有两个解决办法,①如下注释soapServer.c中与这函数有关的代码,②自己实现这个函数。
SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap)
{
soap_peek_element(soap);
/*if (!soap_match_tag(soap, soap->tag, "SOAP-ENV:Fault"))
return soap_serve_SOAP_ENV__Fault(soap);*/
if (!soap_match_tag(soap, soap->tag, "wsdd:Hello"))
return soap_serve___wsdd__Hello(soap);
if (!soap_match_tag(soap, soap->tag, "wsdd:Bye"))
return soap_serve___wsdd__Bye(soap);
if (!soap_match_tag(soap, soap->tag, "wsdd:Probe"))
return soap_serve___wsdd__Probe(soap);
if (!soap_match_tag(soap, soap->tag, "wsdd:ProbeMatches"))
return soap_serve___wsdd__ProbeMatches(soap);
if (!soap_match_tag(soap, soap->tag, "wsdd:Resolve"))
return soap_serve___wsdd__Resolve(soap);
if (!soap_match_tag(soap, soap->tag, "wsdd:ResolveMatches"))
return soap_serve___wsdd__ResolveMatches(soap);
return soap->error = SOAP_NO_METHOD;
}
/*SOAP_FMAC5 int SOAP_FMAC6 soap_serve_SOAP_ENV__Fault(struct soap *soap)
{ struct SOAP_ENV__Fault soap_tmp_SOAP_ENV__Fault;
soap_default_SOAP_ENV__Fault(soap, &soap_tmp_SOAP_ENV__Fault);
if (!soap_get_SOAP_ENV__Fault(soap, &soap_tmp_SOAP_ENV__Fault, "SOAP-ENV:Fault", NULL))
return soap->error;
if (soap_body_end_in(soap)
|| soap_envelope_end_in(soap)
|| soap_end_recv(soap))
return soap->error;
soap->error = SOAP_ENV__Fault(soap, soap_tmp_SOAP_ENV__Fault.faultcode, soap_tmp_SOAP_ENV__Fault.faultstring, soap_tmp_SOAP_ENV__Fault.faultactor, soap_tmp_SOAP_ENV__Fault.detail, soap_tmp_SOAP_ENV__Fault.SOAP_ENV__Code, soap_tmp_SOAP_ENV__Fault.SOAP_ENV__Reason, soap_tmp_SOAP_ENV__Fault.SOAP_ENV__Node, soap_tmp_SOAP_ENV__Fault.SOAP_ENV__Role, soap_tmp_SOAP_ENV__Fault.SOAP_ENV__Detail);
if (soap->error)
return soap->error;
return soap_closesock(soap);
}*/
2、重定义soapH.h中soap_default_xsd_duration、soap_out_xsd__duration、soap_in_xsd__duration申明
这里有两个解决办法,①如下修改soapH.h头文件,②项目包含gsoap/custom/duration.c文件(实现在此文件中)
//SOAP_FMAC1 void SOAP_FMAC2 soap_default_xsd__duration(struct soap*, LONG64 *);
#define soap_default_xsd__duration(soap, a) soap_default_unsignedLONG64(soap, (ULONG64 *)a)
//SOAP_FMAC1 int SOAP_FMAC2 soap_out_xsd__duration(struct soap*, const char*, int, const LONG64 *, const char*);
#define soap_out_xsd__duration(soap,a,b,c,d) soap_out_unsignedLONG64(soap, a, b, (ULONG64 *)c, d)
//SOAP_FMAC1 LONG64 * SOAP_FMAC2 soap_in_xsd__duration(struct soap*, const char*, LONG64 *, const char*);
#define soap_in_xsd__duration(soap, a, b, c) (LONG64*)soap_in_unsignedLONG64(soap,a,(ULONG64 *)b,c)
3、实现功能函数
这时候编译我们的代码后会发生如下的错误(错误会有很多,做好思想准备,这里只列出一小部分)
/alex/discovery/soapServer.c:489: undefined reference to `__tds__GetServices'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetServiceCapabilities':
/alex/discovery/soapServer.c:530: undefined reference to `__tds__GetServiceCapabilities'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetDeviceInformation':
/alex/discovery/soapServer.c:571: undefined reference to `__tds__GetDeviceInformation'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetSystemDateAndTime':
/alex/discovery/soapServer.c:612: undefined reference to `__tds__SetSystemDateAndTime'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetSystemDateAndTime':
/alex/discovery/soapServer.c:653: undefined reference to `__tds__GetSystemDateAndTime'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetSystemFactoryDefault':
/alex/discovery/soapServer.c:694: undefined reference to `__tds__SetSystemFactoryDefault'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__UpgradeSystemFirmware':
/alex/discovery/soapServer.c:735: undefined reference to `__tds__UpgradeSystemFirmware'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SystemReboot':
/alex/discovery/soapServer.c:776: undefined reference to `__tds__SystemReboot'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__RestoreSystem':
/alex/discovery/soapServer.c:817: undefined reference to `__tds__RestoreSystem'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetSystemBackup':
/alex/discovery/soapServer.c:858: undefined reference to `__tds__GetSystemBackup'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetSystemLog':
/alex/discovery/soapServer.c:899: undefined reference to `__tds__GetSystemLog'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetSystemSupportInformation':
/alex/discovery/soapServer.c:940: undefined reference to `__tds__GetSystemSupportInformation'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetScopes':
/alex/discovery/soapServer.c:981: undefined reference to `__tds__GetScopes'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetScopes':
/alex/discovery/soapServer.c:1022: undefined reference to `__tds__SetScopes'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__AddScopes':
/alex/discovery/soapServer.c:1063: undefined reference to `__tds__AddScopes'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__RemoveScopes':
/alex/discovery/soapServer.c:1104: undefined reference to `__tds__RemoveScopes'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetDiscoveryMode':
/alex/discovery/soapServer.c:1145: undefined reference to `__tds__GetDiscoveryMode'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetDiscoveryMode':
/alex/discovery/soapServer.c:1186: undefined reference to `__tds__SetDiscoveryMode'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetRemoteDiscoveryMode':
/alex/discovery/soapServer.c:1227: undefined reference to `__tds__GetRemoteDiscoveryMode'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetRemoteDiscoveryMode':
/alex/discovery/soapServer.c:1268: undefined reference to `__tds__SetRemoteDiscoveryMode'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetDPAddresses':
/alex/discovery/soapServer.c:1309: undefined reference to `__tds__GetDPAddresses'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetEndpointReference':
/alex/discovery/soapServer.c:1350: undefined reference to `__tds__GetEndpointReference'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetRemoteUser':
/alex/discovery/soapServer.c:1391: undefined reference to `__tds__GetRemoteUser'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetRemoteUser':
/alex/discovery/soapServer.c:1432: undefined reference to `__tds__SetRemoteUser'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetUsers':
/alex/discovery/soapServer.c:1473: undefined reference to `__tds__GetUsers'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__CreateUsers':
/alex/discovery/soapServer.c:1514: undefined reference to `__tds__CreateUsers'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__DeleteUsers':
/alex/discovery/soapServer.c:1555: undefined reference to `__tds__DeleteUsers'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetUser':
/alex/discovery/soapServer.c:1596: undefined reference to `__tds__SetUser'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetWsdlUrl':
/alex/discovery/soapServer.c:1637: undefined reference to `__tds__GetWsdlUrl'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__GetCapabilities':
/alex/discovery/soapServer.c:1678: undefined reference to `__tds__GetCapabilities'
/tmp/cc9R4RNL.o: In function `soap_serve___tds__SetDPAddresses':
/alex/discovery/soapServer.c:1719: undefined reference to `__tds__SetDPAddresses'
这里便是我们会调用的功能函数,需要自己实现,每个函数都需要实现,但是对于我们不需要的功能函数,只做最简单的定义即可
int __wsdd__ProbeMatches(struct soap* soap, struct wsdd__ProbeMatchesType *wsdd__ProbeMatches)
{
fprintf(stderr,"__wsdd__ProbeMatches\n");
}
int __wsdd__Resolve(struct soap* soap, struct wsdd__ResolveType *wsdd__Resolve)
{
fprintf(stderr,"__wsdd__Resolve\n");
}
int __wsdd__ResolveMatches(struct soap* soap, struct wsdd__ResolveMatchesType *wsdd__ResolveMatches)
{
fprintf(stderr,"__wsdd__ResolveMatches\n");
}
int __tdn__Hello(struct soap* soap, struct wsdd__HelloType tdn__Hello, struct wsdd__ResolveType *tdn__HelloResponse)
{
fprintf(stderr,"__ns1__Hello\n");
}
int __tdn__Bye(struct soap* soap, struct wsdd__ByeType tdn__Bye, struct wsdd__ResolveType *tdn__ByeResponse)
{
fprintf(stderr,"__ns1__Bye\n");
}
int __tdn__Probe(struct soap* soap, struct wsdd__ProbeType tdn__Probe, struct wsdd__ProbeMatchesType *tdn__ProbeResponse)
{
fprintf(stderr,"__ns2__Probe\n");
}
三、onvif开发手册
四、调试
1、打开onvif调试开关,以便让onvif打印一些可用的调试信息
在Makefile中添加调试宏定义如
CC = gcc -DDEBUG
2、打开调试宏后,程序运行目录会产生三个文件:
RECV.log:记录了onvif接收到的SOAP数据
SENT.log:记录了onvif发送出去的SOAP数据
TEST.log:记录了onvif的实时的工作状态