我本来是打算写一系列Linux c++ onvif客户端开发系列文章,详见:
- Linux c++ onvif客户端开发(1): 根据wsdl生成cpp源文件
- Linux c++ onvif客户端开发(2): 获取摄像头H264/H265 RTSP地址
- Linux c++ onvif客户端开发(3): 扫描设备
- Linux c++ onvif客户端开发(4): 扫描某个设备是否支持onvif
- Linux c++ onvif客户端开发(5):gsoap内存管理
- Linux c++ onvif客户端开发(6):获取设备信息
下载安装openssl
从Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions (slproweb.com)
下载已经编译好的openssl并安装,注意要下载完整版——包含头文件和库文件的版本。
这里给一个直接下载链接https://slproweb.com/download/Win64OpenSSL-3_2_1.exe
openssl是为了编译wsdl2h.exe。
下载gSOAP源码
gSoap分商业版和开源版本,开源版可以从gSOAP Toolkit download | SourceForge.net下载
gsoap工具可以根据wsdl接口文档生成onvif源码,必须用到wsdl2h和soapcpp2两个工具(执行文件)。
我这里下载的gsoap_2.8.132 版本,并解压备用。
编译支持https下载的wsdl2h.exe
gsoap_2.8.132\gsoap-2.8\gsoap\bin\win64目录下已经提供了编译好的两个可执行文件。
但是这个windows版本wsdl2h.exe默认不支持HTTPS下载。也就是说无法使用这样的命令生成接口头文件。
wsdl2h -c++11 -x -t gsoap/typemap.dat https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl
这一步还是很繁琐的。
VS2019迁移wsdl2h工程
使用VS2019 打开gsoap_2.8.132\gsoap-2.8\gsoap\VisualStudio2005\wsdl2h\wsdl2h.sln,并接受升级。
迁移完成之后会有个报告,只要没有错误也就可以继续操作了。
增加x64编译配置
新建release方案选择x64新平台,并从win32创建
完成之后关闭窗口
配置编译选项包含openssl
解决方案资源管理器中,在wsdl2h项目上右键,选中“属性”,弹出项目属性编辑对话框。
先添加头文件包含目录,在C/C++附加包含目录中直接填入
C:\Program Files\OpenSSL-Win64\include
确认C++代码库运行方式,为MT,不用修改
链接器附加库目录填写
C:\Program Files\OpenSSL-Win64\lib\VC\x64\MT
这里要和上面的C++运行库方式要匹配。这里知识点
/MT
:是multithread-static version
,是多线程静态版本的意思,项目会使用运行时库的多线程静态版本,编译器会将LIBCMT.lib
放入.obj
文件中,以便链接器使用LIBCMT.lib
解析外部符号;/MTd
:是定义了_DEBUG
和/MT
,是/MT
类型的debug
版本;/MD
:是multithread-dll version
,是多线程dll
版本的意思,项目会使用运行时库的多线程动态dll
版本,编译器会将MSVCRT.lib
放入.obj
文件中,在编译项目时,它会静态链接MSVCRT.lib
,但在实际运行过程中,它会链接使用MSVCR90.dll
;MDd
:是定义了_DEBUG
和/MD
,是/MD
类型的debug
版本;
附加依赖项增加
;libcrypto.lib;libssl.lib
可以自己打开去按行填写,也可以直接编辑,使用;分割即可。
预处理器增加WITH_OPENSSL
然后配置完毕,应用、保存。
复制httpda.h/httpda.c到wsdl2h源码目录
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\httpda.c
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\httpda.h
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\smdevp.c
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\smdevp.h
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\threads.c
gsoap_2.8.132\gsoap-2.8\gsoap\plugin\threads.h
将上面两个文件复制到
gsoap_2.8.132\gsoap-2.8\gsoap\VisualStudio2005\wsdl2h\wsdl2h
并加入项目源文件中
重新生成
即可编译成功gsoap_2.8.132\gsoap-2.8\gsoap\VisualStudio2005\wsdl2h\x64\Release\wsdl2h.exe
创建目录结构并复制文件
创建libonvif目录,下面再创建4个子目录,子目录的用处如下
├── gsoap 从gsoap-2.8源代码中复制的一些文件,只有typemap.data 做了修改,这些编译时候其实用不到,但是可以放在这里
│ ├── custom
│ ├── import
│ └── plugin
├── onvif 生成的文件,需要编译进来
├── onvif_head 生成的一个中间头文件,可以删除
├── soap 从gsoap中复制的一些文件,后续需要编译进来
复制gosap源码
将gsoap_2.8.132\gsoap-2.8\gsoap 目录下的以下目录和文件复制到新建目录下的libonvif/gsoap目录下
custom/
extras/
import/
plugin/
dom.cpp
stdsoap2.cpp
stdsoap2.h
typemap.dat
复制wsdl2h和soapcpp2到libonvif目录
gsoap_2.8.132\gsoap-2.8\gsoap\VisualStudio2005\wsdl2h\x64\Release\wsdl2h.exe
这个是自己编译的支持https下载的工具
gsoap_2.8.132\gsoap-2.8\gsoap\bin\win64\soapcpp2.exe 这是源码包自带的工具
修改gsoap/typemap.dat
xsd__duration = #import “custom/duration.h” | xsd__duration
生成接口文件onvif_head/onvif.h
使用的相关wsdl文件描述 按需求自己变更
- https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl 用于获取设备参数
- https://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl 用于发现设备
- https://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl 云台控制
- https://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl 获取264的视频流地址
- https://www.onvif.org/onvif/ver20/media/wsdl/media.wsdl 获取h265视频流地址
- http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl 光圈,对比度,饱和度
如下内容保存为libonvif/step1_gen.bat
@echo off
set WSDL2H_BIN=wsdl2h.exe
set DST=onvif_head\onvif.h
%WSDL2H_BIN% -c++11 -x -t gsoap\typemap.dat -o %DST% https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl https://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl https://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl https://www.onvif.org/onvif/ver10/deviceio.wsdl https://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl https://www.onvif.org/onvif/ver20/media/wsdl/media.wsdl
:: # https://www.onvif.org/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/receiver.wsdl \
:: # http://www.onvif.org/onvif/ver10/recording.wsdl \
:: # http://www.onvif.org/onvif/ver10/search.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/onvif/ver10/schema/onvif.xsd \
:: # 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/ver10/accessrules/wsdl/accessrules.wsdl \
:: # http://www.onvif.org/ver10/credential/wsdl/credential.wsdl \
:: # http://www.onvif.org/ver10/schedule/wsdl/schedule.wsdl \
:: # http://www.onvif.org/ver10/pacs/types.xsd
然后双击运行
生成源码
增加鉴权头文件
鉴权也就是设备使用用户名密码登录。
如果onvif.h不加入#import "wsse.h",使用soap_wsse_add_UsernameTokenDigest函数会导致编译出错,也就无法登录设备进行操作了。
但是默认生成的onvif.h中是没有#import "wsse.h"的。
手动在onvif_head/onvif.h 122行增加
#import "wsse.h" // 这里是新增的一行
#import "wsdd10.h" // wsdd10 = <http://schemas.xmlsoap.org/ws/2005/04/discovery>
#import "xop.h" // xop = <http://www.w3.org/2004/08/xop/include>
#import "wsa5.h" // wsa5 = <http://www.w3.org/2005/08/addressing>
SOAP_ENV__Fault重复定义
如果没有修改相关文件,生成代码的时候会出现如下错误。
wsa5.h(280): *WARNING*: Duplicate declaration of 'SOAP_ENV__Fault' (already declared at line 268)
wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278
之所有会出现这个错误,是因为onvif.h头文件中同时:
#import "wsdd10.h" // wsdd10.h中又#import "wsa.h"
#import "wsa5.h" // wsa.h和wsa5.h两个文件重复定义了int SOAP_ENV__Fault
解决方法:
修改libonvif\gsoap\import\wsa5.h文件,将int SOAP_ENV__Fault修改为不冲突的任何名字,例如int SOAP_ENV__Fault_xxx,再次使用soapcpp2工具编译就成功了。
生成
如下保存为libonvif\step2_gen_cpp.bat
@REM @echo off
set SOAPCPP_BIN=soapcpp2.exe
%SOAPCPP_BIN% -2 -c++11 -C -L -x -I gsoap;gsoap/import;gosap/custom -d onvif onvif_head/onvif.h
:: 拷贝其他还有会用的源码
copy gsoap\stdsoap2.cpp soap
copy gsoap\stdsoap2.h soap
copy gsoap\plugin\wsaapi.h soap
copy gsoap\plugin\wsaapi.c soap\wsaapi.cpp
copy gsoap\custom\duration.c soap\duration.cpp
copy gsoap\custom\duration.h soap
copy gsoap\custom\struct_timeval.h soap
copy gsoap\custom\struct_timeval.c soap\struct_timeval.cpp
@REM :: 用于授权验证的一些文件
copy gsoap\dom.cpp soap
copy gsoap\plugin\mecevp.h soap
copy gsoap\plugin\mecevp.c soap\mecevp.cpp
copy gsoap\plugin\smdevp.h soap
copy gsoap\plugin\smdevp.c soap\smdevp.cpp
copy gsoap\plugin\threads.h soap
copy gsoap\plugin\threads.c soap\threads.cpp
copy gsoap\plugin\wsseapi.h soap
copy gsoap\plugin\wsseapi.c soap\wsseapi.cpp
@REM :: 删除多余nsmap——都一样,冗余了
@REM mv onvif/wsdd.nsmap onvif/wsdd.nsmap.bak
@REM rm onvif/*.nsmap
@REM mv onvif/wsdd.nsmap.bak onvif/wsdd.nsmap
pause
双击运行
生成文件如下
Qt导入onvif库 及demo
创建libonvif\onvif.pri ,你自己的pro文件可以include 这个文件
INCLUDEPATH += $$PWD/onvif \
$$PWD/soap
INCLUDEPATH += "C:\Program Files\OpenSSL-Win64\include"
DEFINES += WITH_OPENSSL WITH_DOM
win32:QMAKE_CXXFLAGS += /bigobj
HEADERS += \
$$PWD/onvif.h \
$$PWD/onvif.h \
$$PWD/onvif/soapH.h \
$$PWD/onvif/soapStub.h \
$$PWD/onvif/wsdd.nsmap \
$$PWD/soap/duration.h \
$$PWD/soap/mecevp.h \
$$PWD/soap/smdevp.h \
$$PWD/soap/stdsoap2.h \
$$PWD/soap/struct_timeval.h \
$$PWD/soap/threads.h \
$$PWD/soap/wsaapi.h \
$$PWD/soap/wsseapi.h
SOURCES += \
$$PWD/namespace.cpp \
$$PWD/onvif.cpp \
$$PWD/onvif.cpp \
$$PWD/onvif/soapClient.cpp \
$$PWD/onvif/soapC.cpp \
$$PWD/soap/dom.cpp \
$$PWD/soap/duration.cpp \
$$PWD/soap/mecevp.cpp \
$$PWD/soap/smdevp.cpp \
$$PWD/soap/stdsoap2.cpp \
$$PWD/soap/struct_timeval.cpp \
$$PWD/soap/threads.cpp \
$$PWD/soap/wsaapi.cpp \
$$PWD/soap/wsseapi.cpp
LIBS += "C:\Program Files\OpenSSL-Win64\lib\VC\x64\MT\libssl.lib" \
"C:\Program Files\OpenSSL-Win64\lib\VC\x64\MT\libcrypto.lib"
Demo及其他的一些帮助文件可以在https://gitee.com/noevilme/bilibili/tree/master/onvif/WinOnvif/libonvif 看到
里面也有个Demo可以直接编译使用
使用的注意事项
# winsock库和winsock2库冲突, 需要在pro文件中增加
DEFINES += WIN32_LEAN_AND_MEAN
pri文件使用方式为
INCLUDEPATH += $$PWD/../libonvif
include ($$PWD/../libonvif/onvif.pri)