使用gSOAP生成ONVIF框架,控制网络摄像机及云台

1. gSoap依赖库的编译

先下载和编译下面三个包,为避免与系统库冲突,这里将生成文件安装到指定的目录,如 /home/test/output,版本用最新的都可以

bison-3.6.4.tar.xz

官网:http://www.gnu.org/software/bison/ 

tar -xf bison-3.6.4.tar.xz 
cd bison-3.6.4
./configure --prefix=/home/test/output/
make && make install

flex-2.6.4.tar.gz

官网:http://flex.sourceforge.net/ 

tar -xf flex-2.6.4.tar.gz 
cd flex-2.6.4
./configure --prefix=/home/test/output/
make && make install

openssl-1.1.1g.tar.gz

官网:https://www.openssl.org/ 

tar -xf openssl-1.1.1g.tar.gz 
cd openssl-1.1.1g
./config --prefix=/home/test/output/
make && make install

这里openssl安装到了 /home/test/output/lib 下面

2. gSoap工具编译

gSOAP开源版下载网址:http://sourceforge.net/projects/gsoap2

1. unzip gsoap_2.8.104.zip 
2. ./configure --with-openssl=/home/test/output/lib --prefix=/home/test/output
3. make
4. make install

可见 /home/test/output/bin 下面生成了 wsdl2h 和 soapcpp2,

这里为与windows下保持一致,将这两个文件拷贝到 gsoap-2.8/gsoap/bin/linux 目录下

3. ONVIF框架代码生成

3.1 修改typemap.dat

由于需要使用time_duration,需要先修改 typemap.dat 文件218行,将注释取消

#       Uncomment the line below to use LONG64 int for xsd:duration instead of
#       mapping xsd:duration to string (in milliseconds precision).
#       Then rerun wsdl2h and also compile and link custom/duration.c.
#
 xsd__duration = #import "custom/duration.h" | xsd__duration

3.2 使用wsdl2h工具,根据WSDL产生头文件

创建一个目录samples/onvif,用于存放生成的ONVIF框架头文件。创建目录samples/onvif-out,用于存放生成的ONVIF框架源码。这里可先把需要的wsdl下载到本目录下

https://www.onvif.org/ver10/network/wsdl/remotediscovery.wsdl 
https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl 
https://www.onvif.org/ver10/media/wsdl/media.wsdl
https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl

cd gsoap-2.8/gsoap/bin/linux
wsdl2h -P -c++ -x -s -t ../../typemap.dat -o ../../samples/onvif/onvif.h remotediscovery.wsdl devicemgmt.wsdl media.wsdl ptz.wsdl

可通过wsdl2h.exe -help查看各个选项的含义。这里-cxx为产生c++代码;-s为不使用STL库,-t为typemap.dat的标识。

出现错误提示:Cannot open '../../../ver10/schema/onvif.xsd' to retrieve schema

手动生成相关目录,这里正好是在 gSoap-2.8/ver10/schema,

下载下面几个文件放到该目录中

https://www.onvif.org/ver10/schema/onvif.xsd
https://www.onvif.org/ver10/schema/common.xsd

然后重新运行上面的命令

Saving ../../samples/onvif/onvif.h


**  The gSOAP WSDL/WADL/XSD processor for C and C++, wsdl2h release 2.8.104
**  Copyright (C) 2000-2020 Robert van Engelen, Genivia Inc.
**  All Rights Reserved. This product is provided "as is", without any warranty.
**  The wsdl2h tool and its generated software are released under the GPL.
**  ----------------------------------------------------------------------------
**  A commercial use license is available from Genivia Inc., contact@genivia.com
**  ----------------------------------------------------------------------------

Reading type definitions from type map "../../typemap.dat"
Reading 'remotediscovery.wsdl'...
Done reading 'remotediscovery.wsdl'
Reading 'devicemgmt.wsdl'...
  Reading schema '../../../ver10/schema/onvif.xsd'...
    Connecting to 'http://docs.oasis-open.org/wsn/b-2.xsd' to retrieve schema... connected, receiving...
      Connecting to 'http://docs.oasis-open.org/wsrf/bf-2.xsd' to retrieve schema... connected, receiving...
      Done reading 'http://docs.oasis-open.org/wsrf/bf-2.xsd'
      Connecting to 'http://docs.oasis-open.org/wsn/t-1.xsd' to retrieve schema... connected, receiving...
      Done reading 'http://docs.oasis-open.org/wsn/t-1.xsd'
    Done reading 'http://docs.oasis-open.org/wsn/b-2.xsd'
    Reading schema '../../../ver10/schema/common.xsd'...
    Done reading '../../../ver10/schema/common.xsd'
  Done reading '../../../ver10/schema/onvif.xsd'
Done reading 'devicemgmt.wsdl'
Reading 'media.wsdl'...
Done reading 'media.wsdl'
Reading 'ptz.wsdl'...
Done reading 'ptz.wsdl'

Warning: 2 service bindings found, but collected as one service (use option -Nname to produce a separate service for each binding)

To finalize code generation, execute:
> soapcpp2 ../../samples/onvif/onvif.h
Or to generate C++ proxy and service classes:
> soapcpp2 -j ../../samples/onvif/onvif.h

可见生成了 samples/onvif/onvif.h文件

这里不用Proxy模式

3.3 修改头文件 onvif.h,使得支持鉴权

#import "wsa5.h"        // wsa5 = <http://www.w3.org/2005/08/addressing>
增加下面一句
#import "wsse.h"

3.4 避免定义冲突

修改import\wsa5.h文件,将int SOAP_ENV__Fault修改为int SOAP_ENV__Fault_alex

//gsoap SOAP_ENV service method-action: Fault http://www.w3.org/2005/08/addressing/soap/fault
int SOAP_ENV__Fault_alex
(       _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.5 使用soapcpp2工具,生成框架

./soapcpp2 -2 -C -c++ -L -x -I ../../import -I ../../custom -I../../ -d ../../samples/onvif-out/ ../../samples/onvif/onvif.h

文件产生框架代码

**  The gSOAP code generator for C and C++, soapcpp2 release 2.8.104
**  Copyright (C) 2000-2020, Robert van Engelen, Genivia Inc.
**  All Rights Reserved. This product is provided "as is", without any warranty.
**  The soapcpp2 tool and its generated software are released under the GPL.
**  ----------------------------------------------------------------------------
**  A commercial use license is available from Genivia Inc., contact@genivia.com
**  ----------------------------------------------------------------------------


soap12.h(54): *WARNING*: option -1 or -2 overrides SOAP-ENV namespace


soap12.h(55): *WARNING*: option -1 or -2 overrides SOAP-ENC namespace

Using project directory path: ../../samples/onvif-out/
Saving ../../samples/onvif-out/soapStub.h annotated copy of the source interface header file
Saving ../../samples/onvif-out/soapH.h serialization functions to #include in projects
Using wsdd service name: wsdd
Using wsdd service style: document
Using wsdd service encoding: literal
Using wsdd schema import namespace: http://schemas.xmlsoap.org/ws/2005/04/discovery
Saving ../../samples/onvif-out/wsdd.nsmap namespace mapping table
Using tdn service name: RemoteDiscoveryBinding
Using tdn service style: document
Using tdn service encoding: literal
Using tdn schema namespace: http://www.onvif.org/ver10/network/wsdl
Saving ../../samples/onvif-out/RemoteDiscoveryBinding.nsmap namespace mapping table
Using tds service name: DeviceBinding
Using tds service style: document
Using tds service encoding: literal
Using tds schema namespace: http://www.onvif.org/ver10/device/wsdl
Saving ../../samples/onvif-out/DeviceBinding.nsmap namespace mapping table
Using tptz service name: PTZBinding
Using tptz service style: document
Using tptz service encoding: literal
Using tptz schema namespace: http://www.onvif.org/ver20/ptz/wsdl
Saving ../../samples/onvif-out/PTZBinding.nsmap namespace mapping table
Using trt service name: MediaBinding
Using trt service style: document
Using trt service encoding: literal
Using trt schema namespace: http://www.onvif.org/ver10/media/wsdl
Saving ../../samples/onvif-out/MediaBinding.nsmap namespace mapping table
Saving ../../samples/onvif-out/soapClient.cpp client call stub functions
Saving ../../samples/onvif-out/soapC.cpp serialization functions

Compilation successful (2 warnings)

警告可以不处理

在samples/onvif-out下生成如下文件:

-rw-r--r--. 1 root root     2528 7月  22 14:41 DeviceBinding.nsmap
-rw-r--r--. 1 root root     2528 7月  22 14:41 MediaBinding.nsmap
-rw-r--r--. 1 root root     2528 7月  22 14:41 PTZBinding.nsmap
-rw-r--r--. 1 root root     2528 7月  22 14:41 RemoteDiscoveryBinding.nsmap
-rw-r--r--. 1 root root 11609899 7月  22 14:41 soapC.cpp
-rw-r--r--. 1 root root   560372 7月  22 14:41 soapClient.cpp
-rw-r--r--. 1 root root  7097772 7月  22 14:41 soapH.h
-rw-r--r--. 1 root root  3623591 7月  22 14:41 soapStub.h
-rw-r--r--. 1 root root     2528 7月  22 14:41 wsdd.nsmap

这些nsmap文件都相同,可以只保留wsdd.nsmap

同时把需要的一些文件都复制到指定目录中

[root@localhost onvif-out]# cp ../../plugin/mecevp.h .
[root@localhost onvif-out]# cp ../../plugin/mecevp.c mecevp.cpp
[root@localhost onvif-out]# cp ../../plugin/wsaapi.h .
[root@localhost onvif-out]# cp ../../plugin/wsaapi.c wsaapi.cpp
[root@localhost onvif-out]# cp ../../plugin/wsseapi.h .
[root@localhost onvif-out]# cp ../../plugin/wsseapi.c wsseapi.cpp
[root@localhost onvif-out]# cp ../../custom/duration.h .
[root@localhost onvif-out]# cp ../../custom/duration.c duration.cpp
[root@localhost onvif-out]# cp ../../custom/struct_timeval.h .
[root@localhost onvif-out]# cp ../../custom/struct_timeval.c struct_timeval.cpp
[root@localhost onvif-out]# cp ../../dom.cpp .
[root@localhost onvif-out]# cp ../../plugin/threads.h .
[root@localhost onvif-out]# cp ../../plugin/threads.c threads.cpp
[root@localhost onvif-out]# cp ../../stdsoap2.h .
[root@localhost onvif-out]# cp ../../stdsoap2.cpp .
[root@localhost onvif-out]# cp ../../plugin/smdevp.h .
[root@localhost onvif-out]# cp ../../plugin/smdevp.c smdevp.cpp

为使用命名空间,在stdsoap2.cpp中包含wsdd.nsmap文件,避免后面每个工程中都要包含

#define GSOAP_LIB_VERSION 208104
#include "wsdd.nsmap"

4. C++的测试代码

这里不进行设备发现,假定摄像机的服务地址为固定地址,查找到第一个媒体流,然后调用指定的预置位

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "soapH.h"
#include "wsaapi.h"
#include "wsseapi.h"

#define SOAP_SOCK_TIMEOUT    (10)                                               // socket超时时间(单秒秒)

#define USERNAME    "admin"
#define PASSWORD    "admin"

/************************************************************************
**函数:setAuthInfo
**功能:设置认证信息
**参数:
        [in] soap     - soap环境变量
        [in] username - 用户名
        [in] password - 密码
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
static int setAuthInfo(struct soap *soap, const char *username, const char *password)
{
	int result = soap_wsse_add_UsernameTokenDigest( soap, NULL, username, password );
	if ( result != 0 )
		printf("[soap] add_UsernameTokenDigest error: %d, %s, %s\n", result, *soap_faultcode(soap), *soap_faultstring(soap) );
    return result;
}


void* soapMalloc( struct soap *soap, unsigned int n )
{
    if ( n > 0 )
	{
        void* p = soap_malloc( soap, n );
        assert(NULL != p);
        memset(p, 0x00 ,n);
		return p;
    }
    return NULL;
}

struct soap *soapNew( int timeout )
{
    struct soap *soap = NULL;                                                   // soap环境变量

    assert( NULL != (soap = soap_new()) );

    soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespaces
    soap->recv_timeout    = timeout;                                            // 设置超时(超过指定时间没有数据就退出)
    soap->send_timeout    = timeout;
    soap->connect_timeout = timeout;

#if defined(__linux__) || defined(__linux)                                      // 参考https://www.genivia.com/dev.html#client-c的修改:
    soap->socket_flags = MSG_NOSIGNAL;                                          // To prevent connection reset errors
#endif

    soap_set_mode(soap, SOAP_C_UTFSTRING);                                      // 设置为UTF-8编码,否则叠加中文OSD会乱码
    return soap;
}

void soapDelete(struct soap *soap)
{
	if ( soap == NULL )
		return;
    soap_destroy(soap);                                                         // remove deserialized class instances (C++ only)
    soap_end(soap);                                                             // Clean up deserialized data (except class instances) and temporary data
    soap_done(soap);                                                            // Reset, close communications, and remove callbacks
    soap_free(soap);                                                            // Reset and deallocate the context created with soap_new or soap_copy
}

/************************************************************************
**函数:isH264Profile
**功能:媒体文件是否264
**参数:
        [in] addr - media地址
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/

bool isH264Profile( const char* addr, const char* profile )
{
	int result = 0;
	struct soap *soap = NULL;
	
	assert( NULL != (soap = soapNew(SOAP_SOCK_TIMEOUT)));
	
	setAuthInfo( soap, USERNAME, PASSWORD );

	_trt__GetProfile req;
	_trt__GetProfileResponse resp;
	req.ProfileToken = (char*)profile;

	bool bRet = false;
	soap_call___trt__GetProfile( soap, addr, NULL, &req, resp );
	if ( soap->error )
	{
		soap_print_fault( soap, stderr );
	}
	else
	{
		if ( resp.Profile && resp.Profile->VideoEncoderConfiguration 
			&& resp.Profile->VideoEncoderConfiguration->Encoding == tt__VideoEncoding__H264 )
			bRet = true;
	}

	soapDelete(soap);
	return bRet;
}

/************************************************************************
**函数:getProfiles
**功能:获得Profiles
**参数:
        [in] addr - 设备媒体地址
		[out] chProfile -- h264Profile
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/

int getProfiles( const char* addr, char* chProfile )
{
	int result = 0;
	struct soap *soap = NULL;
	assert(NULL != (soap = soapNew(SOAP_SOCK_TIMEOUT)));

	setAuthInfo(soap, USERNAME, PASSWORD);

	_trt__GetProfiles req;
	_trt__GetProfilesResponse resp;

	soap_call___trt__GetProfiles( soap, addr, NULL, &req, resp );
	if ( soap->error )
	{
		soap_print_fault( soap, stderr );
	}
	else
	{
		int size = resp.__sizeProfiles;
		for ( int i = 0; i < size; ++i )
		{
			if ( resp.Profiles[i] && resp.Profiles[i]->token )
			{
				char* chToken = resp.Profiles[i]->token;
				printf( "%s\n", resp.Profiles[i]->token );
				if ( !isH264Profile( addr, chToken ) )
					continue;
				memcpy( chProfile, chToken, strlen( chToken ) );
				break;
			}
		}
	}

	soapDelete(soap);
	return 0;
}

/************************************************************************
**函数:gotoPreset
**功能:调用指定的预置位
**参数:
        [in] addr - 设备媒体地址
		[in] profile -- Profile
		[in] iPreset -- 要调用的预置位id
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int gotoPreset( const char *addr, const char* profile, int iPreset )
{
	int result = 0;
	struct soap *soap = NULL;
	assert(NULL != (soap = soapNew(SOAP_SOCK_TIMEOUT)));

	setAuthInfo(soap, USERNAME, PASSWORD);

	char chTmp[10];
	sprintf(chTmp, "%d", iPreset );

	_tptz__GotoPreset req;
	req.ProfileToken = (char*)profile;
	req.PresetToken = chTmp;

	_tptz__GotoPresetResponse resp;
	soap_call___tptz__GotoPreset( soap, addr, NULL, &req, resp );
	if ( soap->error )
	{
		soap_print_fault( soap, stderr );
	}
	else
	{
		printf("call preset %d is ok\n", iPreset );
	}
	soapDelete(soap);
	return 0;
}

/************************************************************************
**函数:getDeviceInfo
**功能:获取设备基本信息
**参数:
        [in] chIp - 设备IP地址
**返回:
        0表明成功,非0表明失败
**备注:
************************************************************************/
int getDeviceInfo( char* chIp )
{
	char DeviceXAddr[100] = { 0 };
	//http://192.168.3.101/onvif/device_service
	sprintf( DeviceXAddr, "http://%s/onvif/device_service", chIp );
	
    struct soap *soap = NULL;
 
	assert(NULL != ( soap = soapNew( SOAP_SOCK_TIMEOUT ) ) );
	//用户鉴权
	setAuthInfo(soap, USERNAME, PASSWORD);

	_tds__GetServices req;
	req.IncludeCapability = true;
	_tds__GetServicesResponse resp;

	soap_call___tds__GetServices( soap,  DeviceXAddr, NULL, &req, resp );
	if ( soap->error )
	{
		soap_print_fault( soap, stderr );
	}
	else
	{
		int size = resp.__sizeService;
		std::string m_szPtzAddr;
		std::string m_szProfile;
		for ( int i = 0; i < size; ++i )
		{
			if ( resp.Service[i] && resp.Service[i]->XAddr )
			{
				char* pTmp = resp.Service[i]->XAddr;
				printf( "%s\n", resp.Service[i]->XAddr );

				std::string addr = pTmp;

				if ( addr.find( "media" ) != std::string::npos
					|| addr.find( "Media" ) != std::string::npos )
				{					
					char chProfile[100] = {0};
					getProfiles( pTmp, chProfile );
					m_szProfile = std::string( chProfile );
				}
				else if ( addr.find( "ptz" ) != std::string::npos 
					|| addr.find( "PTZ" ) != std::string::npos )
				{
					m_szPtzAddr = addr;
				}
			}
		}
		gotoPreset( m_szPtzAddr.c_str(), m_szProfile.c_str(), 1 );
	}

    soapDelete(soap);
    return 0;
}

int main(int argc, char **argv)
{
	char* chIp = NULL;
	if ( argc > 1 )
		chIp = argv[1];
	getDeviceInfo( chIp );
    return 0;
}

MakeFile中关键信息:

g++ -Wall -DWITH_OPENSSL -DWITH_DOM -O0 -g3 -D_DEBUG -o debug/duration.o -c duration.cpp

g++  -o ./testOnvifD ./debug/main.o ./debug/dom.o ./debug/mecevp.o ./debug/smdevp.o ./debug/soapC.o ./debug/soapClient.o ./debug/stdsoap2.o ./debug/struct_timeval.o ./debug/threads.o ./debug/wsaapi.o ./debug/wsseapi.o ./debug/duration.o -L. -ldl -lssl -lcrypto   
Finished building: ./testOnvifD

运行结果如下:

# ./testOnvifD 192.168.77.112

http://192.168.77.112/onvif/device_service
http://192.168.77.112/onvif/Media
Profile_1
http://192.168.77.112/onvif/Events
http://192.168.77.112/onvif/PTZ
http://192.168.77.112/onvif/Imaging
http://192.168.77.112/onvif/DeviceIO
http://192.168.77.112/onvif/Analytics
http://192.168.77.112/onvif/Recording
http://192.168.77.112/onvif/SearchRecording
http://192.168.77.112/onvif/Replay
call preset 1 is ok

 

使用gSOAP生成ONVIF框架代码需要遵循以下步骤: 1. 下载gSOAP工具包并安装到本地。 2. 下载ONVIF设备WSDL文件,例如https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl。 3. 使用gSOAP工具包中的wsdl2h工具将WSDL文件转换为头文件。 4. 使用soapcpp2工具生成ONVIF框架代码,例如: ```bash soapcpp2 -Iimport devicemgmt.h ``` 5. 在生成的代码中实现ONVIF设备的具体操作。 以下是在Linux系统中使用gSOAP生成ONVIF框架代码的示例: 1. 下载gSOAP工具包并安装到本地: ```bash wget https://sourceforge.net/projects/gsoap2/files/gSOAP/gsoap_2.8.112.zip unzip gsoap_2.8.112.zip cd gsoap-2.8 ./configure make sudo make install ``` 2. 下载ONVIF设备WSDL文件: ```bash wget https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl ``` 3. 使用wsdl2h工具将WSDL文件转换为头文件: ```bash wsdl2h -o onvif.h devicemgmt.wsdl ``` 4. 使用soapcpp2工具生成ONVIF框架代码: ```bash soapcpp2 -Iimport onvif.h ``` 5. 在生成的代码中实现ONVIF设备的具体操作。 在生成的代码中,每个ONVIF操作都有对应的函数,例如`SOAP_FMAC5 int SOAP_FMAC6 __tds__GetDeviceInformation(struct soap* soap, _tds__GetDeviceInformation* tds__GetDeviceInformation, _tds__GetDeviceInformationResponse &tds__GetDeviceInformationResponse)`就是获取设备信息的函数。您可以在这些函数中实现具体的操作。 另外,生成的代码中还有一些辅助函数和结构体,您也可以根据需要使用它们。 例如,以下代码片段演示了如何创建一个SOAP客户端并调用`__tds__GetDeviceInformation`函数: ```c++ #include "soapDeviceBindingProxy.h" const char* endpoint = "http://192.168.1.100/onvif/device_service"; DeviceBindingProxy proxy; proxy.soap_endpoint = endpoint; _tds__GetDeviceInformation tds__GetDeviceInformation; _tds__GetDeviceInformationResponse tds__GetDeviceInformationResponse; proxy.__tds__GetDeviceInformation(&tds__GetDeviceInformation, tds__GetDeviceInformationResponse); // 处理返回值 ``` 在使用时,需要将`endpoint`替换为实际的设备IP地址和ONVIF服务端口号。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值