概要:

  目前ONVIF协议家族设备已占据数字监控行业半壁江山以上,亲,作为开发者的你还在犹豫是否了解下吗?本文介绍了ONVIF客户端从设备搜索,鉴权,能力获取,媒体信息获取,URI地址获取的整套流程。文章只讲述了比较重要或其他博文没有讲述的开发点,详细可以参考文末参考文章。最后,能获得rtsp地址之后,然后去做其他功能比如录像,ptz这些就非常得心应手了。本文出自CSDN-固本培元 ,转载注明出自:leolupy@gmail.com。

前言及鸣谢:

  感谢guog先生,快活林高先生,onvif全国交流群的的酷夏先生在开发过程中给予的巨大支持,没有你们的帮助开发过程将异常艰难啊。谢谢了!

ONVIF介绍:

  ONVIF致力于通过全球性的开放接口标准来推进网络视频在安防市场的应用,这一接口标准将确保不同厂商生产的网络视频产品具有互通性。2008年11月,论坛正式发布了ONVIF第一版规范——ONVIF核心规范1.0。随着视频监控的网络化应用,产业链的分工将越来越细。有些厂商专门做摄像头,有些厂商专门做DVS,有些厂商则可能专门做平台等,然后通过集成商进行集成,提供给最终客户。这种产业合作模式,已经迫切的需要行业提供越来越标准化的接口平台。

流程总览:

  本文开发环境:Centos6.4 Gsoap:2.8.16 soap:1.2 onvif:2.4 。 注: 本文提供的参考代码其实网上都可以找到,这里做一个整理,供大家交流学习,共同提高。

  • 搜索:Probe: 发现网络摄像头,获取webserver地址:http://192.168.15.240/onvif/device_service
  • 能力获取:GetCapabilities:获取设备能力文件,从中识别出媒体信息地址URI: http://192.168.15.240/onvif/Media
  • 媒体信息获取:GetProfiles: 获取媒体信息文件,识别主通道、子通道的视频编码分辨率
  • RTSP地址获取:GetStreamUri:获取指定通道的流媒体地址 rtsp://192.168.15.240:554/Streaming/Channels/2?transportmode=unicast

Gsoap及开发框架生成:

1. 下载Gsoap

  地址: http://sourceforge.net/projects/gsoap2/files/gSOAP/

2. 安装

./configure && make && make install

  期间可能会有一些报错,自己解决哦。

3. 离线或者在线生成onvif.h。

  如果不需要最新的版本推荐离线方式。笔者使用的是这种方式。离线文件下载地址:http://download.csdn.net/detail/u011597695/5875143(感谢guog先生的共享)

  记得拷贝gsoap的typemap文件至生成目录下,wsdl2h命令需要这个。

  在线命令:

wsdl2h -o onvif.h -c -s -t ./typemap.dat http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl http://www.onvif.org/onvif/ver10/display.wsdl http://www.onvif.org/onvif/ver10/deviceio.wsdl http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl http://www.onvif.org/onvif/ver10/receiver.wsdl  http://www.onvif.org/onvif/ver10/recording.wsdl http://www.onvif.org/onvif/ver10/search.wsdl http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl http://www.onvif.org/onvif/ver10/replay.wsdl http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl http://www.onvif.org/ver10/actionengine.wsdl http://www.onvif.org/ver10/pacs/accesscontrol.wsdl http://www.onvif.org/ver10/pacs/doorcontrol.wsdl

  离线命令:

wsdl2h -o onvif.h -c -s -t ./typemap.dat devicemgmt.wsdl media.wsdl event.wsdl display.wsdl deviceio.wsdl imaging.wsdl ptz.wsdl receiver.wsdl recording.wsdl search.wsdl remotediscovery.wsdl replay.wsdl analytics.wsdl analyticsdevice.wsdl actionengine.wsdl accesscontrol.wsdl doorcontrol.wsdl

  现在可以开始生成了:如下:

  如果直接生成对应C的库文件会发生重复定义错误,如下:

wsa5.h(288): **ERROR**: remote method name clash: struct/class 'SOAP_ENV__Fault' already declared at line 274

  可以修改该文件 gsoap_2.8.16/gsoap-2.8/gsoap/import/ wsa5.h
  将277行int SOAP_ENV__Fault修改为int SOAP_ENV__Fault_alex

  笔者没有使用这种方法,是将这个结构体直接注释的方式,最后的结果是,都可以使用。
  同时,上一步生成的onvif.h文件中没有打开wsse.h, 导致最后生成代码中SOAP_ENV__Header 结构体中缺少定义 wsse__Security数据段,无法进行鉴权命令。即:添加对openssl的支持,在上一步生成的onvif.h中添加(可选)

#import "wsse.h"

  随后使用命令生成:

soapcpp2  -c onvif.h -x -I/root/Tools/Gsoap/gsoap-2.8/gsoap/import -I/root/Tools/Gsoap/gsoap-2.8/gsoap/ -I/root/Tools/Gsoap/gsoap-2.8/gsoap/custom -I/root/Tools/Gsoap/gsoap-2.8/gsoap/extras -I/root/Tools/Gsoap/gsoap-2.8/gsoap/plugin


  到此为止,基于 C 的客户端和服务器的Onvif开发框架及已经搭建完成。

设备搜索原理及编程技巧:

  搜索发现的基本原理是:设备上服务器监听239.255.255.250的3702端口。所以,如果要实现跨网段搜索onvif设备需要路由的支持。只要组播数据包能收到,设备就能被搜到。原理是这样。参考代码:

struct soap* NewSoap(struct SOAP_ENV__Header *header,struct soap* soap,wsdd__ProbeType *req_,wsdd__ScopesType *sScope_)
{soap = soap_new();if(NULL == soap ){printf("sopa new error\r\n");return NULL;}soap->recv_timeout = 5;soap_set_namespaces(soap, namespaces);soap_default_SOAP_ENV__Header(soap, header);uuid_t uuid;char guid_string[100];uuid_generate(uuid);uuid_unparse(uuid, guid_string);header->wsa__MessageID = guid_string;header->wsa__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";header->wsa__Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";soap->header = header;soap_default_wsdd__ScopesType(soap, sScope_);sScope_->__item = "";soap_default_wsdd__ProbeType(soap, req_);req_->Scopes = sScope_;req_->Types = ""; //"dn:NetworkVideoTransmitter";return soap ;
}
int i = 0;
result = soap_send___wsdd__Probe(soap, MULTICAST_ADDRESS, NULL, &req);while(result == SOAP_OK)
{result = soap_recv___wsdd__ProbeMatches(soap, &resp);if(result == SOAP_OK){if(soap->error)  {printf("soap error 1: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));result = soap->error;}else{printf("Onvif Device detected *********************************************\r\n");for(i = 0; i < resp.wsdd__ProbeMatches->__sizeProbeMatch; i++){printf("__sizeProbeMatch        : %d\r\n", resp.wsdd__ProbeMatches->__sizeProbeMatch);  printf("wsa__EndpointReference       : %p\r\n", resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference);  printf("Target EP Address       : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);  printf("Target Type             : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->Types);  printf("Target Service Address  : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);  printf("Target Metadata Version : %d\r\n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);  if(resp.wsdd__ProbeMatches->ProbeMatch->Scopes)  {printf("Target Scopes Address   : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);}}break;}}else if (soap->error){printf("[%d] soap error 2: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));result = soap->error;}
}

  注:搜索到的设备可以加入到自己的设备管理中,这里就不做过多的说明了。

设备鉴权:

  鉴权的实现可以很简单也可以很难,这里笔者使用的是gsoap提供的方法:直接调用即可:

soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);

  原理也很容易明白其实,就是讲http的soap消息加入对应header中xml的元素而已,然后敏感消息digest MD5加密编码。
  所以编译过程中需要使用 lcrypto 也就很正常了。

获取能力:

  soap 的http消息通信,参考代码:

void UserGetCapabilities(struct soap *soap   ,struct __wsdd__ProbeMatches *resp,struct _tds__GetCapabilities *capa_req,struct _tds__GetCapabilitiesResponse *capa_resp)
{capa_req->Category = (enum tt__CapabilityCategory *)soap_malloc(soap, sizeof(int));capa_req->__sizeCategory = 1;*(capa_req->Category) = (enum tt__CapabilityCategory)(tt__CapabilityCategory__Media);capa_resp->Capabilities = (struct tt__Capabilities*)soap_malloc(soap,sizeof(struct tt__Capabilities)) ;soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);printf("\n--------------------Now Gettting Capabilities NOW --------------------\n\n");int result = soap_call___tds__GetCapabilities(soap, resp->wsdd__ProbeMatches->ProbeMatch->XAddrs, NULL, capa_req, capa_resp);if (soap->error){printf("[%s][%d]--->>> soap error: %d, %s, %s\n", __func__, __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));int retval = soap->error;exit(-1) ;}else{printf(" \n--------------------GetCapabilities  OK! result=%d--------------\n \n",result);if(capa_resp->Capabilities==NULL){printf(" GetCapabilities  failed!  result=%d \n",result);}else{printf(" Media->XAddr=%s \n", capa_resp->Capabilities->Media->XAddr);}}
}

获取媒体信息Profile:

  soap 的http消息通信,参考代码:

void UserGetProfiles(struct soap *soap,struct _trt__GetProfiles *trt__GetProfiles,struct _trt__GetProfilesResponse *trt__GetProfilesResponse ,struct _tds__GetCapabilitiesResponse *capa_resp)
{int result=0 ;printf("\n-------------------Getting Onvif Devices Profiles--------------\n\n");soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);result = soap_call___trt__GetProfiles(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetProfiles, trt__GetProfilesResponse);if (result==-1)//NOTE: it may be regular if result isn't SOAP_OK.Because some attributes aren't supported by server.//any question email leoluopy@gmail.com{printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));result = soap->error;exit(-1);}else{printf("\n-------------------Profiles Get OK--------------\n\n");if(trt__GetProfilesResponse->Profiles!=NULL){if(trt__GetProfilesResponse->Profiles->Name!=NULL){printf("Profiles Name:%s  \n",trt__GetProfilesResponse->Profiles->Name);}if(trt__GetProfilesResponse->Profiles->token!=NULL){printf("Profiles Taken:%s\n",trt__GetProfilesResponse->Profiles->token);}}else{printf("Profiles Get inner Error\n");}}printf("Profiles Get Procedure over\n");}

获取RTSP的URI:

  soap 的http消息通信,参考代码:

void UserGetUri(struct soap *soap, struct _trt__GetStreamUri *trt__GetStreamUri, struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse, struct _trt__GetProfilesResponse *trt__GetProfilesResponse, struct _tds__GetCapabilitiesResponse *capa_resp)
{int result=0 ;trt__GetStreamUri->StreamSetup = (struct tt__StreamSetup*)soap_malloc(soap,sizeof(struct tt__StreamSetup));//初始化,分配空间trt__GetStreamUri->StreamSetup->Stream = 0;//stream typetrt__GetStreamUri->StreamSetup->Transport = (struct tt__Transport *)soap_malloc(soap, sizeof(struct tt__Transport));//初始化,分配空间trt__GetStreamUri->StreamSetup->Transport->Protocol = 0;trt__GetStreamUri->StreamSetup->Transport->Tunnel = 0;trt__GetStreamUri->StreamSetup->__size = 1;trt__GetStreamUri->StreamSetup->__any = NULL;trt__GetStreamUri->StreamSetup->__anyAttribute =NULL;trt__GetStreamUri->ProfileToken = trt__GetProfilesResponse->Profiles->token ;printf("\n\n---------------Getting Uri----------------\n\n");soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);soap_call___trt__GetStreamUri(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetStreamUri, trt__GetStreamUriResponse);if (soap->error) {printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));result = soap->error;}else{printf("!!!!NOTE: RTSP Addr Get Done is :%s \n",trt__GetStreamUriResponse->MediaUri->Uri);}
}

  最后贴一个终端截图:

开发注意事项:(必读)

  soap通信的命名空间如果错误则不能检索到设备:编译好的wsdd.nsmap文件需要修改命名空间,如下:

  如果要正常开发,被检索到,或者发现其他设备需要nsmap修改如下:1.1换1.2

以下命名空间表示SOAP1.1版本:

{“SOAP-ENV”, “http://schemas.xmlsoap.org/soap/envelope/”, “http://www.w3.org/*/soap-envelope”, NULL},

{“SOAP-ENC”, “http://schemas.xmlsoap.org/soap/encoding/”, “http://www.w3.org/*/soap-encoding”, NULL}, //1.1

以下命名空间表示SOAP1.2版本:

{“SOAP-ENV”, “http://www.w3.org/2003/05/soap-envelope”, “http://schemas.xmlsoap.org/soap/envelope/”, NULL},

{“SOAP-ENC”, “http://www.w3.org/2003/05/soap-encoding”, “http://schemas.xmlsoap.org/soap/encoding/”, NULL}, //1.2

另外存在的客户端搜索不到设备情况:

  1. 是否有vpn,存在的话,本机IP会产生变化导致不能搜到?抓包可以看到,3702端口包的数据源地址改变。
  2. uuid是否已经赋值。
  3. 有时,windows宿主机装有虚拟机,也可能造成onvif客户端的ip获取错误。故搜索不到。

  这些问题,在交换机或者路由支持本地局域网跨网段数据UDP交互时,均不会产生。

调试技巧:

  fsend / frecv 打印出发送和接收到的报文。使用xml编辑器分析。当然也可以直接用浏览器看。
  1、打开onvif调试开关,以便让onvif打印一些可用的调试信息。
  在Makefile中添加调试宏定义如: CC = gcc -DDEBUG

  2、打开调试宏后,默认在程序运行的目录产生三个文件:

  • RECV.log是onvif接收到的SOAP数据,没接收一条,都会在RECV.log中记录
  • SENT.log是onvif发送出去的SOAP数据,没发送一套,也会在SENT.log中生成记录
  • TEST.log,如果说RECV和SENT可以用wireshark工具抓包代替,那么TEST.log是谁也替代不了的,TEST.log记录了onvif的实时的工作状态。

  尤其当出现segmentation fault错误,TEST.log就成了唯一一个能够定位到具体内存出错的地方了。

SOAP_TYPE返回soap->error=4的错误说明

  关于数据正确(抓包可收到数据),但soap返回错误,为4及SOAP_TYPE的问题:GetCapabilities的过程错误时。
  多次调试后得出结论,是tt__CapabilityCategory 的设置问题,有的设备不具备全部功能,而请求全部或请求没有的功能就可能造成这种问题,推荐写5(tt__CapabilityCategory__Media) 这是大多数设置有的能力,而且最常用。

GetProfile时错误:
   其实数据在抓包过程中也能完全抓到,多次调试后,发现结构体需要的Name以及token关键字被赋值。其他的没有,说明本点返回与服务器的支持性有很大关系。及,开发过程中需要对应自己的需求,根据实际的需要和返回错误,读取返回结构体数据。

资源:

  ONVIFDEVICEMANAGER下载地址:http://pan.baidu.com/share/link?shareid=1967805400&uk=70662920&fid=3981296515
  ONVIFTESTTOOL下载地址:http://www.cr173.com/soft/66448.html
  官网开发者向导资料下载地址:http://www.onvif.org/Resources/WhitePapers.aspx

参考文章:

  onvif规范的实现:使用gSOAP创建SOAP调用实例http://blog.csdn.net/ghostyu/article/details/8162280
  Onvif开发之服务端成功对接Rtsp视频流篇http://blog.csdn.net/max_min_go/article/details/17964643
  linux设备上的Onvif 实现10:获取支持通道的RTSP地址http://gaohtao.blog.163.com/blog/static/58241823201381113214599/
  Onvif开发之客户端鉴权获取参数篇http://blog.csdn.net/max_min_go/article/details/17617057
  ONVIF协议开发资源http://www.csdn.net/tag/onvif%252520%2525E5%25258D%25258F%2525E8%2525AE%2525AE
  onvif开发之设备发现功能的实现https://blog.csdn.net/love_xjhu/article/details/11821037
  Linux设备上的Onvif实现16:实现Onvif鉴权http://blog.csdn.net/u012084827/article/details/19031969
  Onvif开发之Linux下gsoap的使用及移植http://blog.csdn.net/love_xjhu/article/details/9772361
  onvif开发总结http://blog.csdn.net/zsl461975543/article/details/8971143
  代码框架生成之Onvif开发http://www.yc-edu.org/C__peixun/6655.html
  linux设备上的Onvif 实现4:成功编译gsoap 2.8.15http://blog.csdn.net/u012084827/article/details/12202133
  onvif规范的实现:onvif开发常用调试方法 和常见的segmentation fault错误http://blog.csdn.net/ghostyu/article/details/8432760
  linux设备上的Onvif 实现6:获取摄像头的流媒体地址完整流程http://blog.csdn.net/u012084827/article/details/12201997
  S​O​A​P​ ​错​误​代​码​表http://wenku.baidu.com/link?url=rujSmnpjBxjS3mGZrejoVVOShcPu_5Wu_9RKrQ6qWCB12xrZUvVoFkYRepLu0y6oTk6-bB5AnJ_7KxF6s8rXcb1BFko6DbBpXg0_7G0D7cu
  linux设备上的Onvif 实现8:编写媒体信息获取程序http://blog.csdn.net/u012084827/article/details/12201897

ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)相关推荐

  1. 【视频开发】ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)

    转载地址:http://blog.csdn.net/gubenpeiyuan/article/details/25618177 概要:           目前ONVIF协议家族设备已占据数字监控行业 ...

  2. ONVIFclient搜索设备获取rtsp地址开发笔记(精华篇)

    概要: 眼下ONVIF协议家族设备已占领数字监控行业半壁江山以上,亲,作为开发人员的你还在犹豫是否了解下吗?本文介绍了ONVIFclient从设备搜索.鉴权,能力获取,媒体信息获取.URI地址获取的整 ...

  3. ONVIFclient搜索设备获取rtsp解决开发笔记(精华文章)

    总结: 眼下ONVIF协议系列设备已经超过一半的数字监控行业占据更多,关闭,作为一个开发者,你还在犹豫下就明白了?本文介绍了ONVIFclient从搜索,认证,获取,媒体信息获取.URI地址获取的整套 ...

  4. Atitit onvif协议获取rtsp地址播放java语言 attilx总结

    Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...

  5. 蜻蜓设备+支付宝会员的开发笔记

    蜻蜓设备+支付宝会员的开发笔记 开发前准备 准备好要用的参数 一.上传会员卡模板需要的图片 二.创建会员卡模板 三.创建会员开卡表单模板 四.获取会员卡领卡投放链接 五.用户授权换取access_to ...

  6. 微信小程序开发笔记 进阶篇④——getPhoneNumber 获取用户手机号码(小程序云)

    文章目录 一.前言 二.前端代码wxml 三.前端代码js 四.云函数 五.程序流程 一.前言 微信小程序开发笔记--导读 大部分微信小程序开发者都会有这样的需求:获取小程序用户的手机号码. 但是,因 ...

  7. 微信小程序开发笔记 进阶篇⑤——getPhoneNumber 获取用户手机号码(基础库 2.21.2 之前)

    文章目录 一.前言 二.前端代码wxml 三.前端代码js 四.后端java 五.程序流程 六.参考 一.前言 微信小程序开发笔记--导读 大部分微信小程序开发者都会有这样的需求:获取小程序用户的手机 ...

  8. 微信小程序开发笔记 进阶篇⑥——getPhoneNumber 获取用户手机号码(基础库 2.21.2 之后)

    文章目录 一.前言 二.前端代码wxml 三.前端代码js 四.后端java 五.程序流程 六.参考 一.前言 微信小程序开发笔记--导读 大部分微信小程序开发者都会有这样的需求:获取小程序用户的手机 ...

  9. Google Map 开发笔记——基础篇(Javascript )

    Google Map 开发笔记--基础篇 说明: 一.使用入门: 1.在您需要显示地图的 html 页面嵌入这段 script 2.地图 DOM 元素 3.初始化地图 二.地图画点.线.面 1.标记( ...

最新文章

  1. 专家认为自动驾驶汽车需要很多年的五个原因
  2. Hibernate之一级缓存和二级缓存
  3. python删除排序数组中的重复项
  4. 点亮Web的灯---silverlight
  5. Matlab坐标轴中的希腊字母
  6. java struts json_struts2的json插件配置详解(附demo)
  7. C++实现剔除不能识别的非ASCIII、非中文字符
  8. windows修改时间服务器,在Windows中设置时间服务器 2012 R2
  9. element表格动态列、本地分页、动态form、自定义校验集成
  10. 快手上市首日涨近161% 两大创始人身家破千亿
  11. 《Look at Boundary: A Boundary-Aware Face Alignment Algorithm 》阅读笔记
  12. windows下载及安装redis
  13. 安卓第三方登录之微信登录(图文详解)
  14. 当我再次看到你————中秋致Leslie
  15. t5_Sophisticated Algorithmic Strategies(MeanReversion+APO+StdDev_TrendFollowing+APO)_StatArb统计套利_PnL
  16. 解决vscode突然不能自动补全html标签
  17. 高分屏win10PS/AI等软件界面字太小解决方法
  18. Python二手车价格预测(二)—— 模型训练及可视化
  19. Realsense D435i使用笔记
  20. iview select多选下拉 鼠标移出自动收回

热门文章

  1. 12864液晶屏接口定义,16引脚的(普中科技所配)
  2. 关于导出文件中文名乱码问题,response.setHeader(),postman测试有误,直接用浏览器测试
  3. 数据库不能读取也不能打开的解决办法
  4. 相忘江湖不如相濡以沫(Ⅰ)
  5. TCP网络调试助手上提示错误:“1035 未知错误”的有效解决方法,本人实测确实可行
  6. 美团/饿了么外卖CPS联盟返利公众号小程序核心源码代码
  7. Jenkins自动化构建vue项目然后发布到远程服务器
  8. CANopen 7.过程数据对象 PDO Process data object)
  9. 【C++】---日期计算器
  10. 输入法出现异常???输入英文字母分隔很大???你真的会使用输入法了吗???如何高效使用输入法???微软自带输入法切换