news 2026/6/1 11:22:57

在Ubuntu 22.04上,用gSOAP手把手实现一个能发现海康/大华摄像头的ONVIF客户端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在Ubuntu 22.04上,用gSOAP手把手实现一个能发现海康/大华摄像头的ONVIF客户端

在Ubuntu 22.04上实现海康/大华摄像头的ONVIF设备发现与流媒体接入实战

当我们需要将海康威视或大华的网络摄像头集成到智能安防系统中时,ONVIF协议无疑是最佳选择。作为行业标准协议,ONVIF确保了不同厂商设备间的互操作性。本文将手把手指导您在Ubuntu 22.04环境下,使用gSOAP工具包开发一个能够自动发现并获取海康、大华摄像头视频流的ONVIF客户端。

1. 环境准备与工具链配置

在开始编码前,我们需要搭建完整的开发环境。Ubuntu 22.04 LTS提供了稳定的基础,而gSOAP则是实现ONVIF协议通信的核心工具包。

1.1 安装基础依赖

首先更新系统并安装必要的开发工具:

sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential cmake openssl libssl-dev

1.2 编译安装gSOAP

gSOAP的最新稳定版本(2.8.123)提供了完整的ONVIF支持:

wget https://sourceforge.net/projects/gsoap2/files/gsoap-2.8/gsoap_2.8.123.zip unzip gsoap_2.8.123.zip cd gsoap-2.8 ./configure --prefix=/usr/local make -j$(nproc) sudo make install

提示:安装完成后建议执行ldconfig更新动态链接库缓存

1.3 生成ONVIF框架代码

ONVIF的WSDL文件定义了完整的服务接口,我们需要用gSOAP的wsdl2h和soapcpp2工具生成C语言框架:

mkdir onvif_wsdl && cd onvif_wsdl wget https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl wget https://www.onvif.org/ver10/media/wsdl/media.wsdl wsdl2h -c -o onvif.h devicemgmt.wsdl media.wsdl soapcpp2 -c -x -I/usr/local/share/gsoap/import onvif.h

这会生成约50个源文件,包括SOAP消息的序列化/反序列化代码。

2. 设备发现机制实现

ONVIF使用WS-Discovery协议进行网络设备发现,这是整个客户端的基础功能。

2.1 WS-Discovery工作原理

设备发现过程遵循以下流程:

  1. 客户端向组播地址239.255.255.250:3702发送Probe消息
  2. 支持ONVIF的设备监听该组播地址并响应ProbeMatch
  3. 响应中包含设备的服务端点地址(XAddr)

2.2 实现设备发现功能

创建discovery.c文件实现核心发现逻辑:

#include "soapH.h" #include "wsdd.nsmap" #define DISCOVERY_TIMEOUT 5 // 搜索超时(秒) #define MULTICAST_ADDR "soap.udp://239.255.255.250:3702" int discover_devices() { struct soap *soap = soap_new(); struct wsdd__ProbeType probe; struct __wsdd__ProbeMatches resp; // 初始化探测参数 soap_default_wsdd__ProbeType(soap, &probe); probe.Types = "dn:NetworkVideoTransmitter"; // 发送组播探测 if (soap_send___wsdd__Probe(soap, MULTICAST_ADDR, NULL, &probe)) { soap_perror(soap, "Probe发送失败"); return -1; } // 接收响应 while (soap_recv___wsdd__ProbeMatches(soap, &resp) == SOAP_OK) { if (resp.wsdd__ProbeMatches) { for (int i = 0; i < resp.wsdd__ProbeMatches->__sizeProbeMatch; i++) { printf("发现设备: %s\n", resp.wsdd__ProbeMatches->ProbeMatch[i].XAddrs); } } } soap_destroy(soap); soap_end(soap); soap_free(soap); return 0; }

3. 海康/大华设备特殊处理

主流安防厂商设备虽然遵循ONVIF标准,但在实现细节上存在差异需要特别注意。

3.1 品牌识别与适配

通过设备返回的Scopes字段可以识别厂商:

// 在ProbeMatch回调中添加品牌识别 if (strstr(resp.wsdd__ProbeMatches->ProbeMatch[i].Scopes->__item, "onvif://www.hikvision.com")) { printf("检测到海康威视设备\n"); handle_hikvision_special(resp.wsdd__ProbeMatches->ProbeMatch[i].XAddrs); } else if (strstr(resp.wsdd__ProbeMatches->ProbeMatch[i].Scopes->__item, "onvif://www.dahuatech.com")) { printf("检测到大华设备\n"); handle_dahua_special(resp.wsdd__ProbeMatches->ProbeMatch[i].XAddrs); }

3.2 常见厂商差异处理

特性海康威视大华标准ONVIF
默认用户名adminadmin无默认值
默认密码hik12345admin需首次配置
媒体服务端口8080任意端口
PTZ控制命名空间tptz:HikVisiontptz:DHtptz:PTZ
智能分析接口私有协议扩展私有协议扩展RuleEngine

3.3 认证处理示例

海康威视设备需要特殊的认证头处理:

int add_hikvision_auth(struct soap *soap, const char *username, const char *password) { // 添加WS-Security头 soap_wsse_add_Security(soap); // 海康设备需要特殊的Nonce处理 char nonce[16]; generate_random_nonce(nonce, sizeof(nonce)); return soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password, nonce); }

4. 获取视频流地址

发现设备后,获取RTSP流地址是最终目标,这需要通过多个ONVIF服务调用来完成。

4.1 完整流程步骤

  1. 调用GetCapabilities获取设备能力
  2. 从返回中提取媒体服务地址
  3. 调用GetProfiles获取媒体配置集
  4. 使用ProfileToken调用GetStreamUri

4.2 关键代码实现

创建streaming.c文件实现流地址获取:

#include "soapH.h" #include "wsseapi.h" char* get_stream_uri(struct soap *soap, const char *device_xaddr, const char *username, const char *password) { struct _tds__GetCapabilitiesResponse capabilities_resp; struct _trt__GetProfilesResponse profiles_resp; struct _trt__GetStreamUriResponse stream_uri_resp; // 初始化请求 soap_default__tds__GetCapabilities(soap, &capabilities_req); // 设置认证 if (soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password)) { return NULL; } // 获取设备能力 if (soap_call___tds__GetCapabilities(soap, device_xaddr, NULL, &capabilities_req, &capabilities_resp)) { return NULL; } // 获取媒体配置集 if (soap_call___trt__GetProfiles(soap, capabilities_resp.Capabilities->Media->XAddr, NULL, &profiles_req, &profiles_resp)) { return NULL; } // 获取主码流地址 struct tt__StreamSetup stream_setup; struct tt__Transport transport; stream_setup.Stream = tt__StreamType__RTP_Unicast; stream_setup.Transport = &transport; stream_setup.Transport->Protocol = tt__TransportProtocol__RTSP; if (soap_call___trt__GetStreamUri(soap, capabilities_resp.Capabilities->Media->XAddr, NULL, &stream_setup, profiles_resp.Profiles->token, &stream_uri_resp)) { return NULL; } return stream_uri_resp.MediaUri->Uri; }

4.3 海康设备流地址特殊处理

海康设备的RTSP地址通常需要添加认证信息:

void format_hikvision_rtsp_url(const char *original_uri, const char *username, const char *password, char *output, size_t output_size) { const char *p = strstr(original_uri, "rtsp://"); if (!p) return; p += 7; // 跳过"rtsp://" snprintf(output, output_size, "rtsp://%s:%s@%s", username, password, p); }

5. 项目构建与调试

将所有组件整合到CMake项目中,确保跨平台兼容性。

5.1 CMakeLists.txt配置

cmake_minimum_required(VERSION 3.12) project(onvif_client C) set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") # 查找OpenSSL find_package(OpenSSL REQUIRED) # 包含gSOAP头文件 include_directories( ${PROJECT_SOURCE_DIR} /usr/local/include ) # 源文件列表 set(SOURCES src/main.c src/discovery.c src/streaming.c ${PROJECT_SOURCE_DIR}/soapC.c ${PROJECT_SOURCE_DIR}/soapClient.c ) # 生成可执行文件 add_executable(${PROJECT_NAME} ${SOURCES}) # 链接库 target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} pthread )

5.2 常见问题排查

问题1:设备无响应

  • 检查网络防火墙是否阻止了3702端口
  • 确认设备已启用ONVIF功能
  • 海康设备需在"配置→网络→高级配置→集成协议"中开启ONVIF

问题2:认证失败

  • 尝试使用厂商默认凭证
  • 大华新设备可能需要先通过网页界面激活ONVIF
  • 检查时间同步,WS-Security对时间偏差敏感

问题3:媒体服务不可达

  • 海康设备可能使用非标准端口(如8000)
  • 大华设备可能需要添加"/onvif/media_service"路径

6. 功能扩展与优化

基础功能实现后,可以考虑以下增强功能:

6.1 异步设备发现

使用多线程实现不阻塞的持续设备发现:

pthread_t discovery_thread; pthread_create(&discovery_thread, NULL, continuous_discovery, NULL); void* continuous_discovery(void* arg) { while (!should_exit) { discover_devices(); sleep(10); // 每10秒搜索一次 } return NULL; }

6.2 设备信息缓存

将发现的设备信息保存到SQLite数据库:

sqlite3 *db; sqlite3_open("devices.db", &db); // 创建设备表 sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS devices (" "id TEXT PRIMARY KEY, " "xaddr TEXT, " "manufacturer TEXT, " "model TEXT, " "last_seen INTEGER)", NULL, NULL, NULL); // 插入设备记录 void save_device(struct wsdd__ProbeMatchType *match) { sqlite3_stmt *stmt; sqlite3_prepare_v2(db, "INSERT OR REPLACE INTO devices VALUES " "(?, ?, ?, ?, strftime('%s','now'))", -1, &stmt, NULL); // 从Scopes中提取设备ID const char *scopes = match->Scopes->__item; const char *id = extract_device_id(scopes); sqlite3_bind_text(stmt, 1, id, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, match->XAddrs, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, "Hikvision", -1, SQLITE_STATIC); // 实际应从GetDeviceInformation获取 sqlite3_bind_text(stmt, 4, "DS-2CD2342WD-I", -1, SQLITE_STATIC); sqlite3_step(stmt); sqlite3_finalize(stmt); }

6.3 视频流处理管道

获取RTSP地址后,可以使用FFmpeg进行流处理:

ffmpeg -i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \ -c:v libx264 -preset ultrafast -f flv rtmp://live.twitch.tv/app/streamkey

或者使用GStreamer管道:

gst-launch-1.0 rtspsrc location="rtsp://admin:password@192.168.1.64/Streaming/Channels/101" \ ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! autovideosink

7. 安全最佳实践

在实现ONVIF客户端时,安全考虑至关重要:

7.1 认证安全

  • 永远不要硬编码凭证
  • 实现安全的凭证存储机制
  • 使用TLS加密通信(ONVIF WS-Security)

7.2 输入验证

对所有来自设备的响应进行严格验证:

int validate_xaddr(const char *xaddr) { // 验证IP地址格式 struct sockaddr_in sa; return inet_pton(AF_INET, extract_ip(xaddr), &(sa.sin_addr)) != 0; } const char* extract_ip(const char *xaddr) { static char ip[16]; const char *p = strstr(xaddr, "://"); if (!p) return NULL; p += 3; const char *end = strchr(p, ':'); if (!end) end = strchr(p, '/'); size_t len = end ? (size_t)(end - p) : strlen(p); len = len < sizeof(ip)-1 ? len : sizeof(ip)-1; strncpy(ip, p, len); ip[len] = '\0'; return ip; }

7.3 错误处理

实现健壮的错误处理机制:

#define CHECK_SOAP_ERROR(soap, retval) \ do { \ if ((soap)->error) { \ soap_perror((soap), "SOAP调用失败"); \ soap_destroy((soap)); \ soap_end((soap)); \ return (retval); \ } \ } while(0) int safe_get_capabilities(struct soap *soap, const char *xaddr) { struct _tds__GetCapabilitiesResponse resp; struct _tds__GetCapabilities req; soap_default__tds__GetCapabilities(soap, &req); if (soap_call___tds__GetCapabilities(soap, xaddr, NULL, &req, &resp)) { CHECK_SOAP_ERROR(soap, -1); } // 处理响应... return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/1 11:21:00

XXMI启动器:让游戏模组管理像点外卖一样简单![特殊字符]

XXMI启动器&#xff1a;让游戏模组管理像点外卖一样简单&#xff01;&#x1f680; 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 想象一下这样的场景&#xff1a;你刚下载了一…

作者头像 李华
网站建设 2026/6/1 11:19:02

ChatGPT核心原理、高阶应用与提示词实战指南

1. 项目概述&#xff1a;为什么我们需要重新认识ChatGPT最近和几个刚入行的产品经理、运营朋友聊天&#xff0c;发现一个挺有意思的现象&#xff1a;大家天天把“ChatGPT”挂在嘴边&#xff0c;用它写周报、查资料、做脑暴&#xff0c;但当我问起“它到底是怎么工作的&#xff…

作者头像 李华
网站建设 2026/6/1 11:16:32

AI与质性研究的融合(四):AI提升学术写作与发表

对很多质性研究者来说&#xff0c;真正的难点往往不是“没有材料”&#xff0c;而是&#xff1a;材料很多&#xff0c;但写不出清晰有力的论文&#xff1b;分析很扎实&#xff0c;但表达不够凝练&#xff1b;论证有深度&#xff0c;但结构不够顺畅&#xff1b;结果有价值&#…

作者头像 李华
网站建设 2026/6/1 11:16:30

CANoe AutoSequence避坑指南:从Standard到OnBoard模式,这些细节别踩雷

CANoe AutoSequence高级实战&#xff1a;从Standard到OnBoard模式的工程化迁移策略在汽车电子测试领域&#xff0c;AutoSequence作为CANoe中的可视化自动化工具&#xff0c;已经成为工程师快速实现测试自动化的利器。但当我们从实验室环境转向真实车载测试时&#xff0c;Standa…

作者头像 李华