【视频开发】ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)
转载地址:http://blog.csdn.net/gubenpeiyuan/article/details/25618177
概要:
,转载注明出自:leolupy@gmail.com。
前言及鸣谢:
感谢guog先生,快活林高先生,onvif全国交流群的的酷夏先生在开发过程中给予的巨大支持,没有你们的帮助开发过程将异常艰难啊。谢谢了!
ONVIF介绍:
流程总览:
搜索: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及开发框架生成:
命令:
- 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
离线文件在:
- 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
(记得拷贝gsoap的typemap文件至生成目录下,wsdl2h命令需要这个。)
- 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开发框架及已经搭建完成。
设备搜索原理及编程技巧:
- 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;
- }
- }
设备鉴权:
- soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
原理也很容易明白其实,就是讲http的soap消息加入对应header中xml的元素而已,然后敏感消息digest MD5加密编码。
获取能力:
- 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:
- 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:
- 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 type
- trt__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
SENT.log
TEST.log
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
参考文章:
http://blog.csdn.net/ghostyu/article/details/8162280
http://blog.csdn.net/max_min_go/article/details/17964643
linux设备上的Onvif
实现10:获取支持通道的RTSP地址
http://gaohtao.blog.163.com/blog/static/58241823201381113214599/
http://blog.csdn.net/max_min_go/article/details/17617057
ONVIF协议开发资源
http://www.csdn.net/tag/onvif%252520%2525E5%25258D%25258F%2525E8%2525AE%2525AE
http://blog.csdn.net/love_xjhu/article/details/11821037
http://blog.csdn.net/u012084827/article/details/19031969
http://blog.csdn.net/love_xjhu/article/details/9772361
http://blog.csdn.net/zsl461975543/article/details/8971143
代码框架生成之Onvif开发
http://www.yc-edu.org/C__peixun/6655.html
linux设备上的Onvif
实现4:成功编译gsoap 2.8.15
http://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
SOAP 错误代码表
【视频开发】ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)的更多相关文章
- ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)
原文 http://blog.csdn.net/gubenpeiyuan/article/details/25618177 概要: 目前ONVIF协议家族设备已占据数字监控行业半壁江山以上,亲, ...
- ONVIFclient搜索设备获取rtsp地址开发笔记(精华篇)
概要: 眼下ONVIF协议家族设备已占领数字监控行业半壁江山以上.亲,作为开发人员的你还在犹豫是否了解下吗?本文介绍了ONVIFclient从设备搜索,鉴权,能力获取,媒体信息获取 ...
- ONVIFclient搜索设备获取rtsp解决开发笔记(精华文章)
总结: 眼下ONVIF协议系列设备已经超过一半的数字监控行业占据更多,关闭,作为一个开发者,你还在犹豫下就明白了?本文介绍了ONVIFclient从搜索,认证,获取,媒体信息获取. ...
- Onvif开发之客户端搜索篇
关于ONVIF的广播,有客户端搜索和服务端发现的区别:客户端向固定的网段和固定的端口发送广播消息,服务端在对应的端口回复广播请求消息本文首先介绍客户端如何进行广播的已经对广播回复的信息的基本处理. 客 ...
- Atitit onvif协议获取rtsp地址播放java语言 attilx总结
Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...
- Struts 2.x仍然明显落后于时代。 Struts 2.x这一类老牌Web MVC开发框架仅能用于开发瘦客户端应用,无法用来开发对于交互体验要求更高的应用。
后来我在工作中陆续使用过Struts 1.x和Struts 2.x.我曾经把一个开源的基于Struts 1.x的自助式广告联盟应用移植到Spring MVC,还基于Struts 2.x做过网站开发.S ...
- Onvif开发之客户端鉴权获取参数篇
前面一篇已经介绍了客户端如何发些设备,下面这篇简单介绍下在发现设备后,如何通过ONVIF协议来获取设备的相关参数 ONVIF中不管是客户端还是设备端,最先实现的接口都是关于能力的那个接口,在客户端实现 ...
- 电脑无法上网,DHCP客户端不能正确获取IP地址
问题特征:DHCP服务器更新[保留]配置信息后,给一客户端绑定了新的IP地址;但客户端IP地址并未正确更新; 处理: 一.检查DHCP服务器配置; 1.MAC地址.IP地址均正确;并已“添加到筛选器” ...
- iOS7以下设备获取mac地址
注意,是iOS7以下的设备,直接上源码,获取mac地址都是为了唯一标识一个设备的,但iOS7设备的mac地址为 020000000000 MacAddress.h #include <sys/s ...
随机推荐
- linux中/dev/null与2>&1讲解
首先先来看下几种标识的含义: /dev/null 表示空设备文件 0 表示stdin标准输入 1 表示stdout标准输出 2 表示stderr标准错误 先看/dev/null command > ...
- SparkStreaming 性能、稳定、容错与语义
怎样提高Spark Streaming的性能 1.创建多个接收器 待定:: 2.调节每一个batch interval的数据块的数量,其实就是调整上面第二个问题中提到的配置spark.stream ...
- excel双击下拉制作(以及双击下拉字符超限处理)
最近,在项目的开发过程中,遇到了一个问题,自己要修改代码中的excel模板,有些列要处理成双击下拉的形式. excel制作双击下拉: 当然,我想,这对于大家来说是不难的,好实现,但是,我在制作的过程中 ...
- service里无法注入mapper,mapper空指针
被困扰了一天,终于解决了,记录一下 下面是mapper的代码 @Mapper public interface ProductDao { @Select("select * from pro ...
- 洛谷P3258 [JLOI]2014松鼠的新家
题目 树上差分 树上点差分,注意会出现路径端点多记录的情况,这时需要在最后输出的时候输出子树的差分数组的和-1,而不是在处理原数据的时候减1.并且a[n]不需要糖果,最后也减去就行. #include ...
- jupyter的补充
目录 jupyter 的使用 常用命令模式快捷键: 常用编辑模式快捷键: jupyter 的使用 Cells状态分为命令模式和编辑模式,Enter进入编辑模式,ESC进入命令模式,命令模式和编辑模式下 ...
- 您使用的私钥格式错误,请检查RSA私钥配置,charset = utf-8 密钥集不存在
支付宝突然报异常 您使用的私钥格式错误,请检查RSA私钥配置,charset = utf-8 经排查:系统日志 System.Security.Cryptography.CryptographicEx ...
- Java常见集合的默认大小及扩容机制
在面试后台开发的过程中,集合是面试的热话题,不仅要知道各集合的区别用法,还要知道集合的扩容机制,今天我们就来谈下ArrayList 和 HashMap的默认大小以及扩容机制. 在 Java 7 中,查 ...
- 解析prototxt文件的python库 prototxt-parser(使用parsy自定义文件格式解析)
解析prototxt文件的python库 prototxt-parser https://github.com/yogin16/prototxt_parser https://test.pypi.or ...
- MLflow系列1:MLflow入门教程(Python)
英文链接:https://mlflow.org/docs/latest/tutorial.html 本文链接:https://www.cnblogs.com/CheeseZH/p/11943280.h ...