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

Onvif协议:IPC客户端开发-IPC相机控制(c语言版)

前言:

本博文主要是借鉴OceanStar大神的博文,在他的博文的基础之上做了一部分修改与简化。

博文链接:

Onvif协议:IPC客户端开发之鉴权_onvif鉴权方式-CSDN博客
Onvif协议:IPC客户端开发之PTZ控制_onvif ptz-CSDN博客

不知道什么是Onvif协议以及相关的基础知识可以查看大神的相关文章或者看我前面的文章:

ONVIF协议网络摄像机客户端使用gsoap获取RTSP流地址GStreamer拉流播放_onvif取流软件-CSDN博客

onvif代码都是靠工具生成的,我们得根据我们的要求生成头文件,onvif是根据模块来生成头文件,需要的模块多,生成的代码就庞大,相反则很少,使用onvif官方提供的wsdl来生成代码。

根据上面链接博文中介绍的使用gsoap的工具来生成客户端脚本

1:生成头文件

1.1:准备工作

首先先创建一个目录,目录里面放连个文件夹分别名字为:bin、gsoap

然后把gsoap的工具:

拷贝到刚刚创建好的bin文件中。

然后把源码中的

拷贝到gsoap目录下

这样准备工作就基本完成了。

1.2:编写生成头文件脚本

mkdir onvif_head
cd onvif_head
# 添加 -c 选项生成C头文件
../bin/wsdl2h -c -o onvif.h -s -d -x -t ../gsoap/WS/typemap.dat \
http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl \
https://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl \
http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl \
http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl \
http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl

然后记得给脚本赋予权限然后执行

执行成功

1.3:修改头文件内容

在生成的Onvif.h文件中添加

#import "wsse.h"

2:自动生成程序

2.1:编写脚本

这里是使用c语言实现的所以生成的程序就只生成c语言的即可

#!/bin/bash
DIR=soap
mkdir $DIR
cd $DIR../bin/soapcpp2 -2 -x -c -C ../onvif_head/onvif.h  -L -I ../gsoap/import -I ../gsoap/

然后赋予权限运行。

运行会出现问题:

wsa5.h(290): **ERROR**: service operation name clash: struct/class 'SOAP_ENV__Fault' already declared at wsa.h:278

解决方法:

打开 gsoap\import 路径下的wsa5.h, 将277行的SOAP_ENV__Fault结构体注释掉

正确修改完后的结果为:

3:编写核心程序main.c

学习大神的方法,把相关的程序全部放在一起方便管理。

创建一个目录用来存放这些文件

创建一个application目录,存放我们需要的所有代码(比如soap下生成的soapC.c、soapClient.c、soapH.h、soapStub.h、wsdd.nsmap;gsoap源码目录下的stdsoap2.c、stdsoap2.h,gsoap/目录下(dom.c)/gsoap/import目录下的(dom.h)gsoap/plugin目录下的wsseapi.h、wsseapi.c、smdevp.h、smdevp.c、mecevp.c、mecevp.h、threads.c、threads.h、wsaapi.c、wsaapi.h等)/gsoap/custom的(struct_timeval.c、struct_timeval.h)。并创建一个main.c

我这个main.c是简化版本的,里面没有使用到设备发现和获取流地址,适用于设备已知的情况的ptz相机控制,其中的功能有绝对移动和持续移动、停止移动的控制。

#include <assert.h>
#include "soapH.h"
#include "wsdd.nsmap"
#include "soapStub.h"
#include "wsseapi.h"
#include "wsaapi.h"
#include "dom.h"  // 添加 DOM 支持
#include "time.h"
// #include <map>//定义移动枚举
enum PTZCMD
{PTZ_CMD_LEFT,PTZ_CMD_RIGHT,PTZ_CMD_UP,PTZ_CMD_DOWN,PTZ_CMD_LEFTUP,PTZ_CMD_LEFTDOWN,PTZ_CMD_RIGHTUP,PTZ_CMD_RIGHTDOWN,PTZ_CMD_ZOOM_IN,PTZ_CMD_ZOOM_OUT,
};#define USERNAME "admin"     // 替换为实际用户名
#define PASSWORD "admin"     // 替换为实际密码
#define DEVICE_IP "192.168.20.10"  // 替换为设备IP
#define DEVICE_PORT 8000       // 替换为设备端口
#define DEVICE_XADDR "http://%s:%d/onvif/device_service"  // 设备服务地址模板
#define SOAP_ASSERT     assert
#define SOAP_DBGERR     printf#define SOAP_SOCK_TIMEOUT    (10)               // socket超时时间(单秒秒)void soap_perror(struct soap *soap, const char *str)
{if (NULL == str) {SOAP_DBGERR("[soap] error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));} else {SOAP_DBGERR("[soap] %s error: %d, %s, %s\n", str, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));}
}#define SOAP_CHECK_ERROR(result, soap, str) \do { \if (SOAP_OK != (result) || SOAP_OK != (soap)->error) { \soap_perror((soap), (str)); \if (SOAP_OK == (result)) { \(result) = (soap)->error; \} \goto EXIT; \} \
} while (0)struct soap *ONVIF_soap_new(int timeout)
{struct soap *soap = NULL;                                                   // soap环境变量SOAP_ASSERT(NULL != (soap = soap_new()));soap_set_namespaces(soap, namespaces);                                      // 设置soap的namespacessoap->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
#endifsoap_set_mode(soap, SOAP_C_UTFSTRING);                                      // 设置为UTF-8编码,否则叠加中文OSD会乱码return soap;
}void ONVIF_soap_delete(struct soap *soap)
{soap_destroy(soap);                                                         // remove deserialized class instances (C++ only)soap_end(soap);                                                             // Clean up deserialized data (except class instances) and temporary datasoap_done(soap);                                                            // Reset, close communications, and remove callbackssoap_free(soap);                                                            // Reset and deallocate the context created with soap_new or soap_copy
}int ONVIF_GetCapabilities(const char *deviceXAddr, char **ptzXAddr)
{int result = 0;struct soap *soap = NULL;struct _tds__GetCapabilities devinfo_req;struct _tds__GetCapabilitiesResponse devinfo_resp;// 初始化结构体memset(&devinfo_req, 0, sizeof(devinfo_req));memset(&devinfo_resp, 0, sizeof(devinfo_resp));SOAP_ASSERT(deviceXAddr != NULL);SOAP_ASSERT((soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)) != NULL);result = soap_call___tds__GetCapabilities(soap, deviceXAddr, NULL, &devinfo_req, &devinfo_resp);SOAP_CHECK_ERROR(result, soap, "GetCapabilities");if (devinfo_resp.Capabilities != NULL && devinfo_resp.Capabilities->PTZ != NULL && devinfo_resp.Capabilities->PTZ->XAddr != NULL) {// 分配内存并复制字符串*ptzXAddr = strdup(devinfo_resp.Capabilities->PTZ->XAddr);if (*ptzXAddr == NULL) {result = -1; // 内存分配失败goto EXIT;}} else {*ptzXAddr = NULL; // 设置为NULL表示未找到}EXIT:if (soap != NULL) {// 注意:这里可能需要清理devinfo_resp结构体ONVIF_soap_delete(soap);}return result;
}static int ONVIF_SetAuthInfo(struct soap *soap, const char *username, const char *password)
{int result = 0;SOAP_ASSERT(NULL != username);SOAP_ASSERT(NULL != password);result = soap_wsse_add_UsernameTokenDigest(soap, NULL, username, password);SOAP_CHECK_ERROR(result, soap, "add_UsernameTokenDigest");EXIT:return result;
}int ONVIF_GetProfiles(const char *ptzXAddr, char **profilesToken)
{int result = 0;struct soap *soap = NULL;struct _trt__GetProfiles devinfo_req;struct _trt__GetProfilesResponse devinfo_resp;// 初始化结构体memset(&devinfo_req, 0, sizeof(devinfo_req));memset(&devinfo_resp, 0, sizeof(devinfo_resp));SOAP_ASSERT(ptzXAddr != NULL);SOAP_ASSERT((soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT)) != NULL);ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);result = soap_call___trt__GetProfiles(soap, ptzXAddr, NULL, &devinfo_req, &devinfo_resp);SOAP_CHECK_ERROR(result, soap, "ONVIF_GetProfiles");SOAP_ASSERT(devinfo_resp.__sizeProfiles > 0 && devinfo_resp.Profiles != NULL);// 分配内存并复制token*profilesToken = strdup(devinfo_resp.Profiles[0].token);if (*profilesToken == NULL) {result = -1; // 内存分配失败goto EXIT;}EXIT:if (soap != NULL) {// 注意:这里可能需要清理devinfo_resp结构体ONVIF_soap_delete(soap);}return result;
}/************************************************************************
**函数:ONVIF_PTZAbsoluteMove
**功能:绝对移动
**参数:[in] ptzXAddr     - ptzXAddr[in] ProfileToken - 配置令牌
**返回:0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZAbsoluteMove(const char *ptzXAddr, const char *ProfileToken)
{int result = 0;struct soap *soap = NULL;struct _tptz__AbsoluteMove           absoluteMove;struct _tptz__AbsoluteMoveResponse   absoluteMoveResponse;// 参数检查if (ptzXAddr == NULL || ProfileToken == NULL) {printf("Invalid input parameters.\n");return -1;}// 创建 SOAP 上下文soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);if (!soap) {printf("Failed to create soap context.\n");return -1;}// 设置认证信息ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);// 初始化 AbsoluteMove 请求结构体memset(&absoluteMove, 0, sizeof(absoluteMove));// 设置 ProfileToken(从参数拷贝)absoluteMove.ProfileToken = (char *)soap_strdup(soap, ProfileToken);absoluteMove.Position = soap_new_tt__PTZVector(soap,sizeof(struct tt__PTZVector));absoluteMove.Position->PanTilt = soap_new_tt__Vector2D(soap, sizeof(struct tt__Vector2D));absoluteMove.Position->Zoom = soap_new_tt__Vector1D(soap, sizeof(struct tt__Vector1D));absoluteMove.Speed = soap_new_tt__PTZSpeed(soap, sizeof(struct tt__PTZSpeed));absoluteMove.Speed->PanTilt = soap_new_tt__Vector2D(soap, sizeof(struct tt__Vector2D));absoluteMove.Speed->Zoom = soap_new_tt__Vector1D(soap, sizeof(struct tt__Vector1D));// 设置目标位置absoluteMove.Position->PanTilt->x = 0;  // PanabsoluteMove.Position->PanTilt->y = 0;    // TiltabsoluteMove.Position->Zoom->x = 0;       // Zoom// 设置速度absoluteMove.Speed->PanTilt->x = 0.5;     // Pan speedabsoluteMove.Speed->PanTilt->y = 0.5;     // Tilt speedabsoluteMove.Speed->Zoom->x = 0.5;        // Zoom speed//绝对移动接口函数调用result = soap_call___tptz__AbsoluteMove(soap,ptzXAddr, NULL,&absoluteMove,&absoluteMoveResponse);if (result != SOAP_OK) {printf("ONVIF_PTZAbsoluteMove failed: %d\n", result);} else {printf("AbsoluteMove succeeded.\n");}return 0;}/************************************************************************
**函数:ONVIF_PTZStopMove
**功能:停止运动
**参数:
**返回:0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZStopMove(const char* ptzXAddr, const char* ProfileToken){int result = 0;struct soap *soap = NULL;struct _tptz__Stop tptzStop;struct _tptz__StopResponse tptzStopResponse;// 参数检查if (ptzXAddr == NULL || ProfileToken == NULL) {printf("Invalid input parameters.\n");return -1;}// 创建 SOAP 上下文soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);if (!soap) {printf("Failed to create soap context.\n");return -1;}ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);tptzStop.ProfileToken = (char *)ProfileToken;result = soap_call___tptz__Stop(soap, ptzXAddr, NULL, &tptzStop, &tptzStopResponse);SOAP_CHECK_ERROR(result, soap, "ONVIF_PTZStopMove");printf("%s result\n", __func__);EXIT:if (NULL != soap) {ONVIF_soap_delete(soap);}return result;
}
/************************************************************************
**函数:ONVIF_PTZContinuousMove
**功能:连续移动
**参数:[in] ptzXAddr     - ptzXAddr[in] ProfileToken - 配置令牌[in] cmd          - 移动命令[in] speed        - 速度
**返回:0表明成功,非0表明失败
**备注:
************************************************************************/
int ONVIF_PTZContinuousMove(const char *ptzXAddr, char *ProfileToken, enum PTZCMD cmd, float speed)
{      int result = 0;struct soap *soap = NULL;struct _tptz__ContinuousMove continuousMove;struct _tptz__ContinuousMoveResponse continuousMoveResponse;// 参数检查if (ptzXAddr == NULL || ProfileToken == NULL) {printf("Invalid input parameters.\n");return -1;}// 创建 SOAP 上下文soap = ONVIF_soap_new(SOAP_SOCK_TIMEOUT);if (!soap) {printf("Failed to create soap context.\n");return -1;}// 设置认证信息ONVIF_SetAuthInfo(soap, USERNAME, PASSWORD);// 初始化请求结构体memset(&continuousMove, 0, sizeof(continuousMove));continuousMove.ProfileToken = ProfileToken;printf("continuousMove.ProfileToken:%s\n", continuousMove.ProfileToken);continuousMove.Velocity = soap_new_tt__PTZSpeed(soap,-1);continuousMove.Velocity->PanTilt = soap_new_tt__Vector2D(soap,-1);continuousMove.Velocity->Zoom = soap_new_tt__Vector1D(soap,-1);// 设置空间URI(必需字段)continuousMove.Velocity->PanTilt->space = "http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace";continuousMove.Velocity->Zoom->space = "http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace";switch (cmd){case  PTZ_CMD_LEFT:continuousMove.Velocity->PanTilt->x = -speed;continuousMove.Velocity->PanTilt->y = 0;break;case  PTZ_CMD_RIGHT:continuousMove.Velocity->PanTilt->x = speed;continuousMove.Velocity->PanTilt->y = 0;break;case  PTZ_CMD_UP:continuousMove.Velocity->PanTilt->x = 0;continuousMove.Velocity->PanTilt->y = speed;break;case  PTZ_CMD_DOWN:continuousMove.Velocity->PanTilt->x = 0;continuousMove.Velocity->PanTilt->y = -speed;break;case  PTZ_CMD_LEFTUP:continuousMove.Velocity->PanTilt->x = -speed;continuousMove.Velocity->PanTilt->y = speed;break;case PTZ_CMD_LEFTDOWN:continuousMove.Velocity->PanTilt->x = -speed;continuousMove.Velocity->PanTilt->y = -speed;break;case  PTZ_CMD_RIGHTUP:continuousMove.Velocity->PanTilt->x = speed;continuousMove.Velocity->PanTilt->y = speed;break;case PTZ_CMD_RIGHTDOWN:continuousMove.Velocity->PanTilt->x = speed;continuousMove.Velocity->PanTilt->y = -speed;break;case  PTZ_CMD_ZOOM_IN:continuousMove.Velocity->PanTilt->x = 0;continuousMove.Velocity->PanTilt->y = 0;continuousMove.Velocity->Zoom->x = speed;break;case  PTZ_CMD_ZOOM_OUT:continuousMove.Velocity->PanTilt->x = 0;continuousMove.Velocity->PanTilt->y = 0;continuousMove.Velocity->Zoom->x = -speed;break;default:break;}result = soap_call___tptz__ContinuousMove(soap,ptzXAddr, NULL,&continuousMove,&continuousMoveResponse);SOAP_CHECK_ERROR(result, soap, "soap_call___tptz__ContinuousMove");printf("soap_call___tptz__ContinuousMove :result = %d\n", result);// sleep(3); //如果当前soap被删除(或者发送stop指令),就会停止移动// printf("延时3S停止运行\n");// ONVIF_PTZStopMove(ptzXAddr, ProfileToken);
EXIT:if (NULL != soap) {ONVIF_soap_delete(soap);}return result;    
}// 在main函数前添加内存释放函数
void ONVIF_FreeString(char **str)
{if (str && *str) {free(*str);*str = NULL;}
}int main(int argc, char **argv)
{char * ptzXAddr = NULL;char *profilesToken = NULL;char buffer[1024];sprintf(buffer, DEVICE_XADDR, DEVICE_IP , DEVICE_PORT );//拼接服务器的地址// 获取能力if (ONVIF_GetCapabilities(buffer, &ptzXAddr) != 0) {printf("GetCapabilities failed\n");return -1;}if (!ptzXAddr) {printf("PTZ service not found\n");return -1;}// 获取配置if (ONVIF_GetProfiles(ptzXAddr, &profilesToken) != 0) {printf("GetProfiles failed\n");ONVIF_FreeString(&ptzXAddr);return -1;}if (!profilesToken) {printf("No profiles found\n");ONVIF_FreeString(&ptzXAddr);return -1;}printf("Using PTZ service at: %s\n", ptzXAddr);printf("Using profile token: %s\n", profilesToken);ONVIF_PTZAbsoluteMove(ptzXAddr, profilesToken);// ONVIF_PTZContinuousMove(ptzXAddr, profilesToken, PTZ_CMD_RIGHTDOWN, 0.5);//持续移动// sleep(3); //如果当前soap被删除(或者发送stop指令),就会停止移动// printf("延时3S停止运行\n");// ONVIF_PTZStopMove(ptzXAddr, profilesToken);// 释放内存ONVIF_FreeString(&ptzXAddr);ONVIF_FreeString(&profilesToken);return 0;
}

4:编写Makefile

# ONVIF PTZ 控制程序 Makefile (C语言版本)
TARGET = onvif-ptz
CC = gcc
CFLAGS = -DWITH_NONAMESPACES -DWITH_DOM -DWITH_OPENSSL -DWITH_PTHREAD -I. -g -Wall -Werror
LDFLAGS = -lssl -lcrypto -lpthread# 所有源文件
SRCS = dom.c \main.c \mecevp.c \smdevp.c \soapC.c \soapClient.c \stdsoap2.c \struct_timeval.c \threads.c \wsaapi.c \wsseapi.cOBJS = $(SRCS:.c=.o)all: $(TARGET)$(TARGET): $(OBJS)$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)# 显式依赖规则
dom.o: dom.c dom.h$(CC) $(CFLAGS) -c -o $@ $<main.o: main.c soapH.h soapStub.h stdsoap2.h wsseapi.h dom.h$(CC) $(CFLAGS) -c -o $@ $<mecevp.o: mecevp.c mecevp.h
smdevp.o: smdevp.c smdevp.h
soapC.o: soapC.c soapH.h
soapClient.o: soapClient.c soapH.h
stdsoap2.o: stdsoap2.c stdsoap2.h
struct_timeval.o: struct_timeval.c struct_timeval.h
threads.o: threads.c threads.h
wsaapi.o: wsaapi.c wsaapi.h
wsseapi.o: wsseapi.c wsseapi.h# 通用规则
%.o: %.c$(CC) $(CFLAGS) -c -o $@ $<clean:rm -f $(TARGET) $(OBJS).PHONY: all clean

我编译出现的问题是:

这个问题出现的原因是在soapStub.h中定义过了

在dom.h中修改这个两句话就可以了

然后再编译:Make

编译成功,测试:

相关文章:

  • 【论文精读】2024 ECCV--MGLD-VSR现实世界视频超分辨率(RealWorld VSR)
  • 进行性核上性麻痹健康护理全指南:从症状管理到生活照护
  • 广告流量监测和IP地址离线库
  • 从 “金屋藏娇” 到 自然语言处理(NLP)
  • 【第4章 图像与视频】4.2 图像的缩放
  • XMOS以全新智能音频及边缘AI技术亮相广州国际专业灯光音响展
  • vue计算属性computed的详解
  • C语言的函数调用,允许参数缺省和乱序
  • python----下载安装,配置环境
  • mkdir: cannot create directory ‘gitlab-stu’: No space left on device
  • Python基于Django的高校社团学生会管理系统【附源码、文档说明】
  • 【QQ音乐】sign签名| data参数加密 | AES-GCM加密 | webpack实战 (下)
  • 前端工程化 Source Map(源码映射)详解
  • React 微应用接入:qiankun 深度集成实战
  • 音视频中的复用器
  • mac笔记本如何快捷键截图后自动复制到粘贴板
  • 从零开始的数据结构教程(六) 贪心算法
  • 【HTML/CSS面经】
  • 华为OD机试真题——简单的自动曝光平均像素(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
  • OCC笔记:面、边的方向(TopAbs_Orientation)
  • 优化方案英语2024版答案/杭州seo全网营销
  • 关键词没有排名的网站怎么做/网站百度收录要多久
  • 有多少专门做兼职的网站/百度经验官网登录
  • 网站建设开发的主要流程/黄山网站建设
  • 网站建设开发教程视频教程/seo点击工具
  • 口碑好的网站开发公司/seo排名优化网站