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

2025.06.27-14.44 C语言开发:Onvif(二)

认证

SOAP报文认证

Onvif支持在SOAP报文里进行认证,即在XML结构里,加入用户名密码。

在gsoap的实现里,使用wsse插件中的 soap_wsse_add_UsernameTokenDigest即可。

这个函数的原型为:

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

使用的时候,在需要认证的url执行方法之前,调用这个函数加入用户名密码即可实现SOAP报文的认证。

HTTP认证

因为Onvif的Server就是Web Server,所以还有的Onvif服务,光在SOAP报文里认证不够,需要HTTP层的认证。

HTTP层的认证方式包括:要么Basic,要么Digest。

在gsoap的插件里,相应的实现是http_da。

大概分成几步:

  1. soap客户端请求。
  2. 服务端返回错误码401,以及鉴权码。
  3. soap客户端根据鉴权吗,加入认证信息,再次请求。
  4. 服务端认证通过。

示例如下:

// 下面的func、req、response为实际请求的方法、请求结构体指针以及响应结构体指针
int
soap_auth (struct gsoap *soap, const char *uri, const char *username,const char *password)
{int res;struct http_da_info hdinfo[1];// 1. 第一次提交请求,如果不出错,表示不需要认证,直接返回res = func (soap, uri, NULL, req, response);if (res == 0){return 0;}// 2. 如果返回状态码不是401,表示不是认证的问题,直接返回-1if (soap->status != 401){fprintf (stderr, "soap need auth: %d, %d, %s\n", soap->error, *soap_faultcode (soap), *soap_faultstring (soap));return -1;}// 3. 设置认证信息,包括服务端反正的authrealm,以及用户名、密码http_da_save (soap, hdinfo, soap->authrealm, username, password);// 再次提交请求res = func (soap, uri, NULL, req, response);http_da_release (soap, hdinfo);// 4. 如果返回0,表示认证成功if (res == 0){return 0;}// 认证失败fprintf (stderr, "soap auth error: %d, %s, %s\n", soap->error, *soap_faultcode (soap), *soap_faultstring (soap));return -1;
}

需要注意的是,有的摄像机的HTTP服务端,实现的认证过程会多一次。

即,第一次返回401以后,客户端加入realm、用户名、密码,再次提交,再返回401,这时候客户端再次使用返回的realm,以及自己的用户名、密码,进行提交,才会认证通过。

Onvif摄像机点播

Onvif 的摄像机点播的时候,可以分成这样几步:

  1. 取得摄像机的Capabilities
  2. 取得摄像机的Profiles
  3. 取得相应Profile的StreamUri

取得Capabilites

取得Capabilities的函数为soap_call___tds__GetCapabilities,输入参数分别为struct _tds__GetCapabilities *struct _tds__GetCapabilitiesResponse *

struct _tds__GetCapabilitiesResponse的结构里有一个Capabilities结构,这个结构的Media就表示媒体信息。

gchar *  
soap_getcapabilities (struct gsoap* soap, char M_XAddr[], size_t M_XSize)  
{  struct _tds__GetCapabilities req[1];  struct _tds__GetCapabilitiesResponse response[1];  struct soap *soap;  int res;  req->__sizeCategory = 2;  req->Category = (enum tt__CapabilityCategory *)soap_malloc (  soap, 2 * sizeof (enum tt__CapabilityCategory));  *req->Category = tt__CapabilityCategory__Media;  *(req->Category + 1) = tt__CapabilityCategory__Events;  response->Capabilities = (struct tt__Capabilities *)soap_malloc (  soap, sizeof (struct tt__Capabilities));  xaddr = NULL;  res = soap_call___tds__GetCapabilities(soap, uri, req, response);  if (res == 0)  {  if (response->Capabilities != NULL && response->Capabilities->Media)  {  snprintf (M_XAddr, M_XSize, "%s",  response->Capabilities->Media->XAddr);  return 0;} }  return -1;  
}

取得Profiles

取得Capabilites里面的地址之后,就可以根据地址,取得Profile。

取得Profile的函数为soap_call___trt__GetProfiles

int  
soap_getprofiles (struct gsoap *soap, const char *media_uri)  
{  struct _trt__GetProfiles req[1];  struct _trt__GetProfilesResponse response[1];  struct tt__Profile *profile;  int res;  soap_call___trt__GetProfiles(media_uri, req, response);  if (res == 0)  {  if (response->Profiles != NULL)  {  for (i = 0; i < response->__sizeProfiles; ++i)  {  profile = response->Profiles + i;  if (profile->VideoSourceConfiguration == NULL  || profile->VideoSourceConfiguration->SourceToken == NULL)  continue;  fprintf (stdout, "Profile token: %s\n", profile->token);  if (profile->VideoSourceConfiguration->Bounds)  {  }  if (profile->VideoEncoderConfiguration)  {  if (op->ve_Encoding == tt__VideoEncoding__H264)  {  }  else if (op->ve_Encoding == tt__VideoEncoding__H265)  {  }  if (profile->VideoEncoderConfiguration->Resolution)  {  }  // profile->VideoEncoderConfiguration->Quality;  // profile->VideoEncoderConfiguration->RateControl;  }  if (profile->AudioEncoderConfiguration)  {  // profile->AudioEncoderConfiguration->Name);  // profile->AudioEncoderConfiguration->Encoding;  // profile->AudioEncoderConfiguration->Bitrate;  // profile->AudioEncoderConfiguration->SampleRate;  }  }  }  }  return 0;  
}

取得StreamUri

我们遍历了Profile之后,就可以根据我们的意愿,选择取得哪个Profile的媒体地址,使用的函数是soap_call___trt__GetStreamUri

const gchar *  
soap_getstreamuri (struct gsoap *soap, const char *media_uri,  const char *profile, gchar *out, size_t outs  
ize)     
{     struct _trt__GetStreamUri req[1];     struct _trt__GetStreamUriResponse response[1];     int res;     req->StreamSetup = (struct tt__StreamSetup *)soap_malloc (     soap, sizeof (struct tt__StreamSetup));     req->StreamSetup->Stream = 0;     req->StreamSetup->Transport = (struct tt__Transport *)soap_malloc (     soap, sizeof (struct tt__Transport));     req->StreamSetup->Transport->Protocol = 0;     req->StreamSetup->Transport->Tunnel = 0;     req->StreamSetup->__size = 0;     req->StreamSetup->__any = NULL;     soap_default_xsd__anyAttribute (soap, &req->StreamSetup->__anyAttribute);     req->ProfileToken = (char *)profile;     res = soap_call___trt__GetStreamUri(soap, media_uri, req, response);     if (res == 0)     {     if (response->MediaUri == NULL)     {     sh_error ("soap no media uri.\n");     return NULL;     }     snprintf (out, outsize, "%s", response->MediaUri->Uri);     return out;     }     return NULL;    
}

加入Onvif组播

Onvif支持通过组播的方式,向网络里面通告自己的Onvif服务。

绑定UDP的3702端口,加入239.255.255.250地址的组播地址,即可以使用soap_wsdd_listen来监听Onvif客户端的查询请求。

void  
app_listen (struct app_struct *app)  
{  struct soap *soap;  soap = soap_new1 (SOAP_IO_UDP | SOAP_IO_FLUSH);  soap->user = app;  soap_set_mode (soap, SOAP_C_UTFSTRING);  soap->bind_flags = SO_REUSEADDR;  soap_register_plugin (soap, soap_wsa);  if (!soap_valid_socket (soap_bind (soap, app->host, 3702, 10))){  fprintf (stderr, "bind %s:%d error: %s\n", app->host, 3702, *soap_faultstring (soap));  soap_print_fault (soap, stderr);}  else  {  struct ip_mreq mcast;mcast.imr_multiaddr.s_addr = inet_addr ("239.255.255.250");  mcast.imr_interface.s_addr = inet_addr (app->host);  if (setsockopt (soap->master, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast,  sizeof (mcast))  < 0)  {  fprintf (stderr,   "onvif set 3702 port group error: %d, %s, %s\n", soap->error, *soap_faultcode (soap),  *soap_faultstring (soap));}  else  {  while (soap_wsdd_listen (soap, -100000) == SOAP_OK)  {  printf ("onvif is listening\n");  }soap_done (soap);}}  SOAPCLEANUP (soap);  soap_free (soap);  
}
http://www.dtcms.com/a/266634.html

相关文章:

  • 批量PDF转换工具,一键转换Word Excel
  • Spring Boot多环境开发-Profiles
  • [netty5: HttpObject]-源码解析
  • OpenShift AI - 使用 NVIDIA Triton Runtime 运行模型
  • ubuntu 20.04.6 sudo 源码包在线升级到1.9.17p1
  • 跨境电商ERP怎么选?有没有适合新手起步免费版的ERP系统?
  • Zabbix Web检测报错“Could not resolve host: blog.cn; Unknown error”
  • ABP VNext + RediSearch:微服务级全文检索
  • Java项目:基于SSM框架实现的在线投稿管理系统【ssm+B/S架构+源码+数据库+毕业论文】
  • 供应链管理:指标评估方式分类与详解
  • JVM 简介与作用
  • Unity HDRP + Azure IoT 的 Python 后端实现与集成方案
  • git教程-pycharm使用tag打标签
  • 云渲染时,电脑能关机吗?关键阶段操作指南
  • Android课程前言
  • Vue-19-前端框架Vue之应用基础组件通信(二)
  • Linux基本命令篇 —— uname命令
  • HarmonyOS学习记录3
  • 【技术架构解析】国产化双复旦微FPGA+飞腾D2000核心板架构
  • 「源力觉醒 创作者计划」_文心 4.5 开源模型玩出花——教育场景下 Scratch 积木自动化生成的部署实践与优化
  • 【算法刷题记录001】整型数组合并(java代码实现)
  • 转Go学习笔记
  • RT‑DETRv2 详解:Real‑Time DETR 的 Bag‑of‑Freebies 与部署优化
  • PNG图像压缩优化工具
  • 钉钉小程序开发技巧:getSystemInfo 系统信息获取全解析
  • IRIV算法详解 | 变量选择的迭代保留法
  • 全星稽核管理软件系统——企业智能化稽核管理的最佳解决方案
  • zxing去白边
  • 督皇口粮酱酒 平价不平质
  • 第十五节:第三部分:特殊文件:XML概述、解析