当前位置: 首页 > news >正文

保姆级教程:用C语言和gSOAP从零实现一个ONVIF客户端(附完整源码)

从零构建ONVIF客户端:基于C语言与gSOAP的实战指南

在物联网和智能安防领域,ONVIF协议已经成为设备互联互通的事实标准。对于嵌入式开发者而言,掌握ONVIF客户端的开发技能意味着能够轻松对接市面上绝大多数网络摄像头和NVR设备。本文将带领你从零开始,使用C语言和gSOAP框架构建一个功能完整的ONVIF客户端,涵盖设备发现、能力获取、媒体配置和流地址获取等核心功能。

1. 环境准备与工具链搭建

1.1 基础开发环境配置

在开始ONVIF客户端开发前,需要准备以下基础环境:

  • Linux系统:推荐Ubuntu 18.04或更高版本
  • gSOAP工具包:版本2.8或更高
  • 开发工具链
    sudo apt-get install build-essential cmake openssl libssl-dev

1.2 gSOAP安装与配置

gSOAP是开发ONVIF客户端的核心工具,它能够将WSDL文件转换为可直接调用的C代码:

wget https://sourceforge.net/projects/gsoap2/files/gsoap-2.8/gsoap_2.8.100.zip unzip gsoap_2.8.100.zip cd gsoap-2.8 ./configure --prefix=/usr/local make sudo make install

提示:安装完成后,建议将gSOAP的bin目录加入PATH环境变量,方便后续使用wsdl2h和soapcpp2工具。

1.3 ONVIF框架代码生成

ONVIF官方提供了完整的WSDL描述文件,我们需要用gSOAP工具生成对应的C语言框架:

wsdl2h -c -o onvif.h \ 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 soapcpp2 -c -x -I/usr/local/share/gsoap/import onvif.h

生成的关键文件包括:

  • soapH.h:SOAP协议头文件
  • soapC.c:SOAP协议实现
  • soapClient.c:客户端调用接口
  • wsdd.nsmap:命名空间映射表

2. 项目结构与核心API解析

2.1 项目目录结构规划

合理的项目结构能显著提高代码可维护性:

onvif-client/ ├── CMakeLists.txt ├── include/ │ ├── onvif.h │ └── common.h ├── src/ │ ├── main.c │ ├── discovery.c │ └── media.c └── thirdparty/ ├── gsoap/ └── onvif-wsdl/

2.2 gSOAP核心API详解

gSOAP的核心是struct soap上下文,它管理着所有SOAP调用的状态和资源:

// 创建SOAP上下文 struct soap *soap = soap_new(); // 设置命名空间(关键步骤) soap_set_namespaces(soap, namespaces); // 典型调用模式 int result = soap_call___tds__GetDeviceInformation( soap, device_endpoint, NULL, &request, &response ); // 资源释放 soap_destroy(soap); soap_end(soap); soap_free(soap);

2.3 认证机制实现

ONVIF设备通常需要WS-Security认证,gSOAP提供了便捷的API:

int soap_wsse_add_UsernameTokenDigest( struct soap *soap, const char *id, const char *username, const char *password );

典型调用示例:

struct soap *soap = soap_new(); soap_wsse_add_UsernameTokenDigest(soap, NULL, "admin", "123456");

3. 设备发现与能力协商

3.1 WS-Discovery协议实现

ONVIF使用WS-Discovery协议进行设备发现,核心是多播探测:

#define SOAP_MCAST_ADDR "soap.udp://239.255.255.250:3702" struct wsdd__ProbeType probe; soap_default_wsdd__ProbeType(soap, &probe); probe.Types = "dn:NetworkVideoTransmitter"; int result = soap_send___wsdd__Probe(soap, SOAP_MCAST_ADDR, NULL, &probe);

设备响应处理:

struct __wsdd__ProbeMatches matches; while(SOAP_OK == soap_recv___wsdd__ProbeMatches(soap, &matches)) { if(matches.wsdd__ProbeMatches) { for(int i=0; i<matches.wsdd__ProbeMatches->__sizeProbeMatch; i++) { char *xaddr = matches.wsdd__ProbeMatches->ProbeMatch[i].XAddrs; printf("Found device at: %s\n", xaddr); } } }

3.2 设备能力获取

获取设备能力是后续所有操作的基础:

struct _tds__GetCapabilities capabilities_req; struct _tds__GetCapabilitiesResponse capabilities_resp; int result = soap_call___tds__GetCapabilities( soap, device_endpoint, NULL, &capabilities_req, &capabilities_resp ); if(SOAP_OK == result) { char *media_xaddr = capabilities_resp.Capabilities->Media->XAddr; printf("Media service at: %s\n", media_xaddr); }

关键能力信息包括:

  • 设备信息(Device)
  • 媒体服务(Media)
  • PTZ控制(PTZ)
  • 事件处理(Events)

4. 媒体流获取与实战技巧

4.1 媒体配置获取

每个ONVIF设备可能有多个媒体配置(Profile),需要先获取配置token:

struct _trt__GetProfiles profiles_req; struct _trt__GetProfilesResponse profiles_resp; int result = soap_call___trt__GetProfiles( soap, media_endpoint, NULL, &profiles_req, &profiles_resp ); if(SOAP_OK == result && profiles_resp.__sizeProfiles > 0) { char *profile_token = profiles_resp.Profiles[0]->token; }

4.2 RTSP流地址获取

获取RTSP流地址是客户端最核心的功能:

struct _trt__GetStreamUri stream_req; struct _trt__GetStreamUriResponse stream_resp; struct tt__StreamSetup stream_setup; struct tt__Transport transport; stream_setup.Stream = tt__StreamType__RTP_Unicast; stream_setup.Transport = &transport; transport.Protocol = tt__TransportProtocol__RTSP; stream_req.StreamSetup = &stream_setup; stream_req.ProfileToken = profile_token; int result = soap_call___trt__GetStreamUri( soap, media_endpoint, NULL, &stream_req, &stream_resp ); if(SOAP_OK == result && stream_resp.MediaUri) { printf("RTSP URL: %s\n", stream_resp.MediaUri->Uri); }

4.3 常见问题排查指南

在实际开发中,开发者常会遇到以下问题:

  1. 认证失败

    • 检查用户名/密码是否正确
    • 确认设备是否启用了ONVIF认证
    • 验证时间同步(某些设备要求客户端时间与设备时间差不超过5分钟)
  2. 设备无响应

    # 使用curl测试设备基础服务 curl -v http://<device_ip>/onvif/device_service
  3. 内存泄漏检测: gSOAP提供了内存检测工具,在开发时建议启用:

    #define SOAP_MEMORY_LIMIT (1024*1024) // 限制1MB内存使用 soap_set_mode(soap, SOAP_MEMORY_LIMIT);

5. 完整项目集成与优化

5.1 CMake项目配置

完整的CMake配置示例:

cmake_minimum_required(VERSION 3.5) project(onvif-client) set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWITH_OPENSSL -DWITH_DOM") # gSOAP相关源文件 set(GSOAP_SOURCES ${PROJECT_SOURCE_DIR}/thirdparty/gsoap/stdsoap2.c ${PROJECT_SOURCE_DIR}/thirdparty/gsoap/plugin/wsseapi.c ${PROJECT_SOURCE_DIR}/thirdparty/gsoap/plugin/mecevp.c ${PROJECT_SOURCE_DIR}/thirdparty/gsoap/plugin/smdevp.c ) add_executable(${PROJECT_NAME} src/main.c src/discovery.c src/media.c ${GSOAP_SOURCES} ) target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/thirdparty/gsoap ) target_link_libraries(${PROJECT_NAME} ssl crypto)

5.2 异步处理与多线程

gSOAP本身不是线程安全的,但可以通过以下方式实现多设备管理:

// 每个线程独立的SOAP上下文 void* device_thread(void *arg) { struct soap *soap = soap_new(); // 设备处理逻辑 soap_free(soap); return NULL; } // 主线程创建多个设备处理线程 pthread_t threads[MAX_DEVICES]; for(int i=0; i<device_count; i++) { pthread_create(&threads[i], NULL, device_thread, &devices[i]); }

5.3 性能优化建议

  1. 连接复用

    soap->keep_alive = 1; // 启用HTTP Keep-Alive
  2. 缓存管理

    • 缓存设备能力信息,避免重复查询
    • 对媒体配置信息进行本地存储
  3. 错误恢复机制

    if(soap->error) { soap_print_fault(soap, stderr); soap_destroy(soap); soap_end(soap); soap = soap_new(); // 创建新的SOAP上下文 }

6. 进阶功能扩展

6.1 PTZ控制实现

通过ONVIF实现云台控制:

struct _tptz__ContinuousMove move_req; struct _tptz__ContinuousMoveResponse move_resp; move_req.ProfileToken = profile_token; move_req.Velocity = soap_new_tt__PTZSpeed(soap); move_req.Velocity->PanTilt = soap_new_tt__Vector2D(soap); move_req.Velocity->PanTilt->x = 0.5; // 右移 move_req.Velocity->PanTilt->y = 0.0; int result = soap_call___tptz__ContinuousMove( soap, ptz_endpoint, NULL, &move_req, &move_resp );

6.2 事件订阅与处理

ONVIF事件订阅机制:

struct _tev__CreatePullPointSubscription sub_req; struct _tev__CreatePullPointSubscriptionResponse sub_resp; int result = soap_call___tev__CreatePullPointSubscription( soap, events_endpoint, NULL, &sub_req, &sub_resp ); if(SOAP_OK == result) { char *subscription_endpoint = sub_resp.SubscriptionReference.Address; // 定期拉取事件 }

6.3 快照获取与处理

获取设备当前快照:

struct _trt__GetSnapshotUri snapshot_req; struct _trt__GetSnapshotUriResponse snapshot_resp; snapshot_req.ProfileToken = profile_token; int result = soap_call___trt__GetSnapshotUri( soap, media_endpoint, NULL, &snapshot_req, &snapshot_resp ); if(SOAP_OK == result && snapshot_resp.MediaUri) { printf("Snapshot URL: %s\n", snapshot_resp.MediaUri->Uri); }

在实际项目开发中,ONVIF客户端的稳定性和可靠性至关重要。建议开发者建立完善的设备兼容性测试矩阵,覆盖不同厂商、不同固件版本的设备。同时,考虑到嵌入式环境的资源限制,应当特别注意内存管理和网络超时设置。

http://www.rkmt.cn/news/1521287.html

相关文章:

  • LangChain 系列:Structured Output结构化输出与源码解析
  • 2026年热门的秦皇岛全屋整装装修/秦皇岛一站式整装装修/秦皇岛装修/秦皇岛全屋定制装修优选服务公司 - 品牌宣传支持者
  • 2026年高端婚介服务深度观察:成都、长沙主流机构多维对比分析 - 优质品牌商家
  • Windows/Mac双平台实测:Upscayl这6个AI放大模型到底怎么选?附批量处理与压缩设置技巧
  • 保姆级教程:用mavcmd命令行一键搞定PX4无人机指点飞行(附IMU频率设置)
  • 别再傻傻分不清!嵌入式开发选RTOS,SMP和AMP到底哪个更适合你的多核SOC?
  • 从Airflow到Kafka:拆解OpenMetadata与DataHub的元数据‘搬运’哲学
  • 装机小白必看:DDR4内存条怎么选?从频率、时序到颗粒,一篇讲透避坑要点
  • 2026年知名的机架钣金加工/自动化框架钣金加工/苏州铝型材框架钣金加工/钢平台钣金加工厂家选择推荐 - 行业平台推荐
  • ProCAST结果数据搬运工:温度场、应力场导出为PATRAN格式的完整避坑指南
  • 2026年高端熔体静电纺丝设备/对喷静电纺丝设备/山东纳米静电纺丝设备/山东纳米纤维静电纺丝设备优质厂家推荐榜 - 品牌宣传支持者
  • yt-dlp-gui:终极免费视频下载神器,三步搞定YouTube视频下载
  • STC32G12K128开发板到手后,第一件事:用Keil C251和STC-ISP搞定环境与下载
  • 2026年南充桶装水配送评测:厂家地址及服务实力对比 - 优质品牌商家
  • 别被型号搞晕了!一文看懂高通IPQ9574/9554/9514 Wi-Fi 7芯片怎么选(附路由器型号对照表)
  • BaryIR:基于Wasserstein重心的图像修复框架
  • 从SPI、I2C到UART:嵌入式老鸟教你根据项目需求选对通信协议(附对比表格和选型 checklist)
  • 2026年评价高的苏州铝型材框架钣金加工/不锈钢管道钣金加工/苏州移载小车钣金加工深度厂家推荐 - 品牌宣传支持者
  • 2026年重庆黄金回收市场深度观察:哪些回收店值得信赖?本地回收商运营能力与价格透明化趋势解析 - 优质品牌商家
  • 别再傻傻分不清了!一文搞懂单片机里的EPROM、EEPROM和Flash到底怎么选
  • Perplexity AI上手体验:这个“答案引擎”真的比ChatGPT联网搜索更好用吗?
  • 云时代防DDoS,你的钱花对地方了吗?AWS Shield、阿里云高防与自建方案的性价比深度对比
  • 从紫外线到电信号:EPROM到EEPROM的技术演进史,以及为什么你的U盘不用‘晒’
  • 嵌入式OTA三剑客:bsdiff、Hdiffpatch、Xdelta算法到底该怎么选?
  • 2026年靠谱的山东洗煤压滤机/山东带式压滤机/洗沙污泥压滤机高口碑品牌推荐 - 品牌宣传支持者
  • 别再傻傻分不清了!硬件工程师实战笔记:USB3320 (ULPI) 与 USB3450 (UTMI+) 选型、电路设计与避坑指南
  • NSK直线导轨LH45HL升级替换指南
  • Redis篇(四):持久化(下)
  • 2026年口碑好的宿迁碳纤维护套/碳纤维板/碳纤维环/碳纤维源头工厂推荐 - 品牌宣传支持者
  • 为什么你需要重新认识这个AI编程助手体验优化工具?