ONVIF-gSOAP
ONVIF(开放网络视频接口论坛)标准使用Web Services技术,而gSOAP是一个用于开发Web Services服务的工具包,它能够将C/C++代码与SOAP/WSDL绑定,从而方便地实现ONVIF规范的各个服务.
通俗来讲就是:ONVIF是一种安防接口规范,gSoap工具将ONVIF的接口规范生成对应代码。
ONVIF
Onvif接口文档:
网络接口规范 - ONVIF Mandarin
左边一列是解释文档、右边是接口文档(WSDL),右边的看不懂了再去看左边的。
总的接口文档 ONVIF Core Specification
测试工具下载(需要会员账号):onvif.org/member-tool…
gSOAP
官网源码下载(有商业版、开源版两个版本):Genivia - gSOAP C and C++ SOAP/XML Web Services Tools
开发者文档: www.genivia.com/dev.html
工具下载,编译指南:Genivia Product Downloads
步骤
- 获取ONVIF的WSDL文件。
- onvif接口转换为头文件:使用gSOAP的工具wsdl2h将WSDL文件转化为头文件(比如onvif.h)
- 根据头文件(接口)转换成对应的C/C++代码:使用gSOAP的工具soapcpp2,将头文件转换成C/C++代码。
- 在项目中使用生成的代码,并实现ONVIF客户端或服务器。
gSOAP编译
# sudo apt-get install flex bison
# apt install autoconf automake libtool
#将编译好的OpenSSL的include和lib拷贝到gsoap-2.8/
# ./configure --prefix=/user_code/build_env/gsoap-2.8/install/ --with-openssl=/user_code/build_env/gsoap-2.8/openssl
# make
# make install
得到gSoap代码生成工具
bin/soapcpp2
bin/wsdl2h
share/gsoap/WS/typemap.dat
确认gsoap是否支持ssl,存在libsoapssl.a, 说明已经支持ssl:
下载WSDL
支持鉴权
方法一:在生成onvif.h文件里加上#import "wsse.h" 方法二:在typemap.dat结尾出添加 #import "wsse.h",这样就不用每次生成onvif.h头文件都要修改一次了
# C语言需要打开xsd__dateTime注释, C++ 不需要
# xsd__dateTime = #import "custom/struct_timeval.h" | xsd__dateTime
wstop__TopicSetType = $ _XML __mixed;
wsnt__FilterType = $ wsnt__TopicExpressionType;
_wsnt__NotificationMessageHolderType_Message = $ _tt__Message* tt__Message;
[
#import "wsse.h" 支持鉴权
#import "duration.h" 可选
#import "wsa.h" 可选
#import "ds.h" 可选
]
WSDL转换为头文件
Usage: wsdl2h [-a] [-b] [-c|-c++|-c++11|-c++14|-c++17] [-D] [-d] [-e] [-F] [-f] [-g] [-h] [-I path] [-i] [-j] [-k] [-L] [-l] [-M] [-m] [-N name] [-n name] [-O1|-O2|-O3|-O4|-Ow2|-Ow3|-Ow4] [-P|-p] [-Q] [-q name] [-R] [-r proxyhost[:port[:uid:pwd]]] [-r:uid:pwd] [-Sname] [-s] [-T] [-t typemapfile] [-U] [-u] [-V] [-v] [-w] [-W] [-x] [-y] [-z#] [-_] [-o outfile.h] infile.wsdl infile.xsd http://www... ...
#../wsdl2h -s -t ../../share/gsoap/WS/typemap.dat -o onvif.h ../../specs-21.12/wsdl/ver10/device/wsdl/devicemgmt.wsdl ../../specs-21.12/wsdl/ver10/events/wsdl/event.wsdl ../../specs-21.12/wsdl/ver10/schema/onvif.xsd ../../specs-21.12/wsdl/ver10/schema/common.xsd ../../specs-21.12/wsdl/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl ../../specs-21.12/wsdl/ver10/media/wsdl/media.wsdl ../../specs-21.12/wsdl/ver20/media/wsdl/media.wsdl ../../specs-21.12/wsdl/ver20/imaging/wsdl/imaging.wsdl ../../specs-21.12/wsdl/ver20/ptz/wsdl/ptz.wsdl ../../specs-21.12/wsdl/ver10/deviceio.wsdl ../../specs-21.12/wsdl/ver20/analytics/wsdl/analytics.wsdl ../../specs-21.12/wsdl/ver20/analytics/rules.xsd ../../specs-21.12/wsdl/ver10/schema/metadatastream.xsd ../../specs-21.12/wsdl/ver10/recording.wsdl ../../specs-21.12/wsdl/ver10/replay.wsdl ../../specs-21.12/wsdl/ver10/search.wsdl ../../specs-21.12/wsdl/ver10/receiver.wsdl ../../specs-21.12/wsdl/ver10/display.wsdl ../../specs-21.12/wsdl/remotediscovery.wsdl
或者直接指定在线的地址
../wsdl2h -s -t ../../share/gsoap/WS/typemap.dat -o onvif.h https://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl https://www.onvif.org/ver10/events/wsdl/event.wsdl https://www.onvif.org/ver10/schema/onvif.xsd https://www.onvif.org/ver10/schema/common.xsd https://www.onvif.org/ver10/advancedsecurity/wsdl/advancedsecurity.wsdl https://www.onvif.org/ver10/media/wsdl/media.wsdl https://www.onvif.org/ver20/media/wsdl/media.wsdl https://www.onvif.org/ver20/imaging/wsdl/imaging.wsdl https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl https://www.onvif.org/ver10/deviceio.wsdl https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl https://www.onvif.org/ver20/analytics/rules.xsd https://www.onvif.org/ver10/schema/metadatastream.xsd https://www.onvif.org/ver10/recording.wsdl https://www.onvif.org/ver10/replay.wsdl https://www.onvif.org/ver10/search.wsdl https://www.onvif.org/ver10/receiver.wsdl https://www.onvif.org/ver10/display.wsdl https://www.onvif.org/ver10/actionengine.wsdl
-s:不生成STL代码。
-c:生成C代码,默认是c++
根据WSDL转换的头文件转换为可调用的cpp
Usage: soapcpp2 [-0|-1|-2] [-C|-S|-CS] [-A] [-a] [-b] [-c|-c++|-c++11|-c++14|-c++17] [-d path] [-Ec] [-Ed] [-Et] [-e] [-f N] [-g] [-h] [-i] [-I path:path:...] [-L] [-l] [-m] [-n] [-p name] [-Q name] [-q name] [-r] [-s] [-T] [-t] [-u] [-V] [-v] [-w] [-x] [-y] [-z#] [infile]
#../soapcpp2 -2 -x -j -L -I../../share/gsoap/ -I../../share/gsoap/custom/ -I../../share/gsoap/import/ ./onvif.h
-x : 不生成xml
-2 : 使用soap1.2版本
-c : 生成c语言代码
-j : 生成c++代码,类似智能指针管理内存,要去掉-c
-L :不生成soapClientLib.cpp soapServerLib.cpp文件
最终生成:
- soapC.cpp: 数据类型的序列化/反序列化实现
- soapXXXProxy.h: C++ 代理类的头文件(Onvif接口)
- soapProxy.cpp: C++ 代理类的实现文件(代替了
soapClient.cpp) - soapH.h: 主要头文件
- soapStub.h: 存根/定义头文件
- soapXXXServer.cpp: 服务器端实现(如果你有服务端代码)(代替了
soapServer.cpp) - *.nsmap或 *.xml: XML 命名空间映射文件(里面的内容都是相同的,只要一个就好)
错误:
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
原因是import/wsa.h和import/wsa5.h的定义SOAP_ENV__Fault类型冲突,修改wsa5.h的SOAP_ENV__Fault为SOAP_ENV__Fault_2 然后清理make clean & make distclean,重新生成soapcpp2工具。
错误:
** The gSOAP code generator for C and C++, soapcpp2 release 2.8.139
** Copyright (C) 2000-2025 Genivia Inc. All Rights Reserved.
** The soapcpp2 tool and its generated software are released under the GPL.
** ----------------------------------------------------------------------------
** A commercial use license is available from Genivia Inc., contact@genivia.com
** ----------------------------------------------------------------------------
Critical error: Cannot open file "custom/struct_timeval.h" to import: No such file or directory
Hint: use option -I<path> (for example -Igsoap:gsoap/import:gsoap/custom:.)
需要指定gsoap目录,加上-I../../share/gsoap/
生成成功后,得到(xml报文模板,软件用不到,用于参考报文格式):
wsdd.nsmap 全局xml命名空间, 对应每个库的接口
DeviceBinding.nsmap
soapH.h soap接口函数
soapC.cpp 公共函数
soapClient.cpp 业务逻辑函数
soapClientLib.cpp
soapServer.cpp 业务逻辑函数
soapServerLib.cpp
soapStub.h 业务逻辑函数
编译错误:
/bin/ld: CMakeFiles/OnvifTest.dir/gsoap_src/stdsoap2.c.o: in function `soap_init_REQUIRE_lib_v208139':
/user_code/QtCode/OnvifTest/gsoap_src/stdsoap2.c:12785: undefined reference to `namespaces'
namespaces定义在nsmap文件中,解决:
#include "wsdd.nsmap"
错误:
In file included from /user_code/QtCode/OnvifTest/gsoap_src/custom/duration.c:58:0:
/user_code/QtCode/OnvifTest/gsoap_src/custom/duration.c:61:81: error: expected ‘)’ before ‘*’ token
SOAP_FMAC3 void SOAP_FMAC4 soap_default_xsd__duration(struct soap *soap, LONG64 *a)
放开typemap.dat注释:
xsd__duration = #import "custom/duration.h" | xsd__duration
C++测试程序
不使用编好的gsoap库,直接使用gsoap源文件,需要将用到的*.c文件全部改成*.cpp。
cmake_minimum_required(VERSION 3.16)
project(OnvifTest VERSION 0.1 LANGUAGES CXX C)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#add_compile_definitions(WITH_OPENSSL)
#add_compile_definitions(WITH_DOM)
#add_compile_definitions(WITH_OPENSSL_DTLS)
add_definitions(
-DWITH_OPENSSL # 启用OpenSSL
-DWITH_DOM # 启用DOM
-DWITH_OPENSSL_DTLS
-DSOAP_DEBUG
)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
set(C_SOURCE
# 生成的c代码
gsoap_generated/soapC.cpp
gsoap_generated/soapwsddProxy.cpp
gsoap_generated/soapDeviceBindingProxy.cpp
# gSOAP核心
gsoap_src/stdsoap2.cpp
gsoap_src/dom.cpp
# 日期时间序列化插件(关键!)
gsoap_src/custom/struct_timeval.cpp
gsoap_src/custom/duration.cpp
# gSOAP插件(必须完整)
gsoap_src/plugin/mecevp.cpp # 消息加密(关键!解决 soap_mec_size)
gsoap_src/plugin/smdevp.cpp # 签名/加密
gsoap_src/plugin/httpda.cpp
gsoap_src/plugin/wsseapi.cpp # WS-Security
gsoap_src/plugin/wsaapi.cpp # WS-Addressing
gsoap_src/plugin/threads.cpp # 多线程
)
# 设置为C语言源文件
#set_source_files_properties(${C_SOURCE} PROPERTIES LANGUAGE C)
set(PROJECT_SOURCES
mainwindow.ui
main.cpp
mainwindow.h
mainwindow.cpp
gsoap_generated/soapStub.h
gsoap_generated/soapH.h
gsoap_src/stdsoap2.h
gsoap_src/stdsoap2.cpp
gsoap_src/import/wsa.h
gsoap_src/plugin/smdevp.h
gsoap_src/plugin/wsseapi.h
gsoap_src/plugin/threads.h
# 日期时间序列化插件(关键!)
${C_SOURCE}
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(OnvifTest
MANUAL_FINALIZATION
#${HEADER_FILES}
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET OnvifTest APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(OnvifTest SHARED
#${HEADER_FILES}
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(OnvifTest
${PROJECT_SOURCES}
)
endif()
endif()
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_generated)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_generated)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/import)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/plugin)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/custom)
target_link_libraries(OnvifTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(OnvifTest PUBLIC ${PROJECT_SOURCE_DIR}/gsoap_src/openssl/include/)
#库链接
target_link_directories(OnvifTest PUBLIC ${PROJECT_SOURCE_DIR}/gsoap_src/openssl/lib)
target_link_libraries(OnvifTest PRIVATE ssl crypto dl)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.OnvifTest)
endif()
set_target_properties(OnvifTest PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS OnvifTest
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(OnvifTest)
endif()
#include "mainwindow.h"
#include <QApplication>
#include "DeviceBinding.nsmap"
#include "soapStub.h"
#include "soapH.h"
#include "wsseapi.h"
#include "httpda.h"
#include "soapDeviceBindingProxy.h"
#include <iostream>
int main(int argc, char *argv[])
{
struct soap *soap = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
soap_init(soap);
soap_set_mode(soap, SOAP_C_UTFSTRING); //设置为utf-8编码
soap->accept_timeout = 20;
soap->connect_timeout= 20;
soap->transfer_timeout = 10;
soap->recv_timeout = 20;
soap->send_timeout = 20;
soap_set_namespaces(soap,namespaces);
soap->namespaces = namespaces;
soap_register_plugin(soap, http_da);
soap_register_plugin(soap, soap_wsse);
if (SOAP_OK != soap_wsse_add_Timestamp(soap, "Time", 60)) // 60 seconds lifetime
{
return 0;
}
int result = 0;
const char *device_url = NULL;
// === 1. 初始化 SSL/TLS (HTTPS 核心) ===
if (soap_ssl_client_context(soap,
SOAP_SSL_SKIP_HOST_CHECK,
NULL, NULL,
NULL, // 确保证书路径正确!
NULL, NULL
)) {
fprintf(stderr, "[错误] SSL 初始化失败\n");
return 0;
}
printf("[信息] SSL/TLS 就绪。\n");
if (argc > 1) {
device_url = argv[1];
} else {
// 使用你的设备真实地址替换下面这个
device_url = "https://10.214.55.155:443/onvif/device_service";
printf("提示: 使用默认地址,可通过命令行参数指定。\n");
}
soap_set_recv_logfile(soap, "/user_code/QtCode/OnvifTest/drecv.log");
soap_set_sent_logfile(soap, "/user_code/QtCode/OnvifTest/dsent.log");
printf("目标设备: %s\n", device_url);
if (soap_wsse_add_UsernameTokenDigest(soap, NULL, "admin", "Hycs@23589")) {
fprintf(stderr, "[错误] 无法添加WS-Security认证头\n");
//soap_stream_fault(soap, stderr);
return 0;
}
// === 3. 准备请求与响应结构 ===
// 这些类型定义在 soapStub.h 中
struct _tds__GetDeviceInformation req;
struct _tds__GetDeviceInformationResponse resp;
DeviceBindingProxy deviceProxy(soap);
// === 4. 执行 HTTPS 调用 ===
printf("正在发送 GetDeviceInformation 请求...\n");
result = deviceProxy.GetDeviceInformation(
device_url, // 端点地址
NULL, // SOAP动作,可为空(使用默认)
&req, resp
);
if(result == 401)
{
struct http_da_info info;
http_da_save(soap, &info, soap->authrealm, "admin", "Hycs@23589");
result = deviceProxy.GetDeviceInformation(
device_url, // 端点地址
NULL, // SOAP动作,可为空(使用默认)
&req, resp
);
http_da_release(soap, &info);
}
// === 5. 处理结果 ===
if (result == SOAP_OK) {
printf("\n>>> HTTPS ONVIF 调用成功! <<<\n");
printf("制造商: %s\n", resp.Manufacturer ? resp.Manufacturer : "(空)");
printf("型号 : %s\n", resp.Model ? resp.Model : "(空)");
printf("序列号: %s\n", resp.SerialNumber ? resp.SerialNumber : "(空)");
} else {
fprintf(stderr, "\n>>> 调用失败 (错误代码: %d) <<<\n", result);
// 分析常见错误
if (soap->error == 401) {
fprintf(stderr, "错误 401: 需要身份验证。ONVIF 需要设置 WS-Security 用户名/密码。\n");
// 如需认证,在此添加代码:
// soap_wsse_add_UsernameTokenDigest(soap, NULL, "admin", "12345");
} else if (soap->error) {
fprintf(stderr, "SOAP 错误: ");
//soap_stream_fault(soap, stderr);
}
// if (soap->ssl_verify_error) {
// fprintf(stderr, "SSL 证书验证错误 (代码: 0x%04X)\n", soap->ssl_verify_error);
// }
}
// === 6. 清理 ===
//soap_destroy(soap);
//soap_end(soap);
//soap_free(soap);
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
C测试程序
cmake_minimum_required(VERSION 3.16)
project(OnvifTest VERSION 0.1 LANGUAGES CXX C)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#add_compile_definitions(WITH_OPENSSL)
#add_compile_definitions(WITH_DOM)
#add_compile_definitions(WITH_OPENSSL_DTLS)
add_definitions(
-DWITH_OPENSSL # 启用OpenSSL
-DWITH_DOM # 启用DOM
-DWITH_OPENSSL_DTLS
-DSOAP_DEBUG
)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
set(C_SOURCE
# 生成的代码
gsoap_generated/soapC.c
gsoap_generated/soapClient.c
# gSOAP核心
#gsoap_src/stdsoap2.c
gsoap_src/dom.c # DOM支持(关键!解决 soap_dom_find)
# 日期时间序列化插件(关键!)
gsoap_src/custom/struct_timeval.c
gsoap_src/custom/duration.c
# gSOAP插件(必须完整)
#gsoap_src/plugin/plugin.c
gsoap_src/plugin/mecevp.c # 消息加密(关键!解决 soap_mec_size)
gsoap_src/plugin/smdevp.c # 签名/加密
gsoap_src/plugin/httpda.c
gsoap_src/plugin/wsseapi.c # WS-Security
gsoap_src/plugin/wsaapi.c # WS-Addressing
gsoap_src/plugin/threads.c # 多线程
)
# 设置为C语言源文件
set_source_files_properties(${C_SOURCE} PROPERTIES LANGUAGE C)
set(PROJECT_SOURCES
mainwindow.ui
main.cpp
mainwindow.h
mainwindow.cpp
gsoap_generated/soapStub.h
gsoap_generated/soapH.h
gsoap_src/stdsoap2.h
gsoap_src/stdsoap2.cpp
gsoap_src/import/wsa.h
gsoap_src/plugin/smdevp.h
gsoap_src/plugin/wsseapi.h
gsoap_src/plugin/threads.h
# 日期时间序列化插件(关键!)
${C_SOURCE}
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(OnvifTest
MANUAL_FINALIZATION
#${HEADER_FILES}
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET OnvifTest APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(OnvifTest SHARED
#${HEADER_FILES}
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(OnvifTest
${PROJECT_SOURCES}
)
endif()
endif()
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_generated)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_generated)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/import)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/plugin)
target_include_directories(OnvifTest PRIVATE ${PROJECT_SOURCE_DIR}/gsoap_src/custom)
target_link_libraries(OnvifTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(OnvifTest PUBLIC ${PROJECT_SOURCE_DIR}/gsoap_src/openssl/include/)
#库链接
target_link_directories(OnvifTest PUBLIC ${PROJECT_SOURCE_DIR}/gsoap_src/openssl/lib)
target_link_libraries(OnvifTest PRIVATE ssl crypto dl)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.OnvifTest)
endif()
set_target_properties(OnvifTest PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS OnvifTest
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(OnvifTest)
endif()
#include "mainwindow.h"
#include <QApplication>
#include "DeviceBinding.nsmap"
#include "soapStub.h"
#include "soapH.h"
#include "wsseapi.h"
#include "httpda.h"
#include <iostream>
int main(int argc, char *argv[])
{
struct soap *soap = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT);
soap_init(soap);
soap_set_mode(soap, SOAP_C_UTFSTRING); //设置为utf-8编码
soap->accept_timeout = 20;
soap->connect_timeout= 20;
soap->transfer_timeout = 10;
soap->recv_timeout = 20;
soap->send_timeout = 20;
soap_set_namespaces(soap,namespaces);
soap->namespaces = namespaces;
soap_register_plugin(soap, http_da);
soap_register_plugin(soap, soap_wsse);
if (SOAP_OK != soap_wsse_add_Timestamp(soap, "Time", 60)) // 60 seconds lifetime
{
return 0;
}
int result = 0;
const char *device_url = NULL;
// === 1. 初始化 SSL/TLS (HTTPS 核心) ===
if (soap_ssl_client_context(soap,
SOAP_SSL_SKIP_HOST_CHECK,
NULL, NULL,
NULL, // 确保证书路径正确!
NULL, NULL
)) {
fprintf(stderr, "[错误] SSL 初始化失败\n");
return 0;
}
printf("[信息] SSL/TLS 就绪。\n");
if (argc > 1) {
device_url = argv[1];
} else {
// 使用你的设备真实地址替换下面这个
device_url = "https://10.214.55.155:443/onvif/device_service";
printf("提示: 使用默认地址,可通过命令行参数指定。\n");
}
soap_set_recv_logfile(soap, "/user_code/QtCode/OnvifTest/drecv.log");
soap_set_sent_logfile(soap, "/user_code/QtCode/OnvifTest/dsent.log");
printf("目标设备: %s\n", device_url);
if (soap_wsse_add_UsernameTokenDigest(soap, NULL, "admin", "Hycs@23589")) {
fprintf(stderr, "[错误] 无法添加WS-Security认证头\n");
//soap_stream_fault(soap, stderr);
return 0;
}
// === 3. 准备请求与响应结构 ===
// 这些类型定义在 soapStub.h 中
struct _tds__GetDeviceInformation req;
struct _tds__GetDeviceInformationResponse resp;
// 初始化结构(重要!)
//soap_default__tds__GetDeviceInformationResponse(soap, &resp);
// === 4. 执行 HTTPS 调用 ===
// 函数名 `soap_call_tds__GetDeviceInformation` 请根据第一步的搜索结果调整
printf("正在发送 GetDeviceInformation 请求...\n");
result = soap_call___tds__GetDeviceInformation(
soap,
device_url, // 端点地址
NULL, // SOAP动作,可为空(使用默认)
&req, resp
);
if(result == 401)
{
struct http_da_info info;
http_da_save(soap, &info, soap->authrealm, "admin", "Hycs@23589");
result = soap_call___tds__GetDeviceInformation(
soap,
device_url, // 端点地址
NULL, // SOAP动作,可为空(使用默认)
&req, resp
);
http_da_release(soap, &info);
}
// === 5. 处理结果 ===
if (result == SOAP_OK) {
printf("\n>>> HTTPS ONVIF 调用成功! <<<\n");
printf("制造商: %s\n", resp.Manufacturer ? resp.Manufacturer : "(空)");
printf("型号 : %s\n", resp.Model ? resp.Model : "(空)");
printf("序列号: %s\n", resp.SerialNumber ? resp.SerialNumber : "(空)");
} else {
fprintf(stderr, "\n>>> 调用失败 (错误代码: %d) <<<\n", result);
// 分析常见错误
if (soap->error == 401) {
fprintf(stderr, "错误 401: 需要身份验证。ONVIF 需要设置 WS-Security 用户名/密码。\n");
// 如需认证,在此添加代码:
// soap_wsse_add_UsernameTokenDigest(soap, NULL, "admin", "12345");
} else if (soap->error) {
fprintf(stderr, "SOAP 错误: ");
//soap_stream_fault(soap, stderr);
}
// if (soap->ssl_verify_error) {
// fprintf(stderr, "SSL 证书验证错误 (代码: 0x%04X)\n", soap->ssl_verify_error);
// }
}
// === 6. 清理 ===
//soap_destroy(soap);
//soap_end(soap);
//soap_free(soap);
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}