-
2019-10-22 12:34:18
为实现onvif协议下客户端获取rtsp地址功能,首先需要安装gsoap软件。
1.Linux下gsoap安装
首先下载需要的gsoap安装包给出链接GSOAP并解压出来。
然后安装依赖工具sudo apt-get install build-essential sudo apt-get install libgtk2.0-dev libglib2.0-dev sudo apt-get install checkinstall sudo apt-get install flex bison sudo apt-get install libssl-dev sudo apt-get install openssl
最后进入解压的文件夹,输入一下命令
sudo ./configure sudo make sudo make install
这个时候gsoap就已经安装完成了。我们可以测试一下
输入wsdl2h -o shanchu.h http://www.baidu.com
这时屏幕上显示
安装完成更多相关内容 -
EasyNVR如何获取RTSP格式的视频流?三种获取RTSP地址方式介绍
2021-10-20 17:33:39在使用平台EasyNVR的过程中,部分用户提出需要调用RTSP地址的需求,主要是将EasyNVR内的视频流转为RTSP的格式,放到其他平台使用。其实这个需求可以直接在平台内实现,EasyNVR中提供了获取RTSP流的功能,本文我们就...在使用平台EasyNVR的过程中,部分用户提出需要调用RTSP地址的需求,主要是将EasyNVR内的视频流转为RTSP的格式,放到其他平台使用。其实这个需求可以直接在平台内实现,EasyNVR中提供了获取RTSP流的功能,本文我们就介绍一下具体操作方法。
首先最简单的方法是在EasyNVR视频播放页面F12可以查看到当前播放的信息,页面选择网络后点击XNR,页面中短暂等待可以获取到箭头所示位置。
将对应的播放格式修改为需要的格式输入浏览器即可展示更改后的播放路径。
另外在我们在开发的新版本中考虑到了调用的问题,因此在页面中加入了地址,在播放页面中可以直接看到播放地址,这种方式会节省现场用户很多时间,该版本目前还在测试中,待正式发布后将提供给大家测试。
除了以上两种方案外还有一种比较常规的方式:API接口调用。
打开接口文档可以看到我们需要的内容,使用Postman可以快速调用,并且不局限于RTSP这一种格式,在实际的现场中这个方案实际使用会更多一些。
-
ONVIF工具获取摄像头的RTSP取流地址
2019-01-05 10:14:00ONVIF工具获取摄像头的RTSP取流地址,本人因工作需要制作,再次分享出来供大家参考使用!(通过Onvif测试工具,获取摄像机取流地址 180804.doc) -
ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)
2021-01-22 15:29:39最后,能获得rtsp地址之后,然后去做其他功能比如录像,ptz这些就非常得心应手了。本文出自CSDN-固本培元 ,转载注明出自:leolupy@gmail.com。 前言及鸣谢: 感谢guog先生,快活林高先生,onvif全国交流概要:
目前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 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
另外存在的客户端搜索不到设备情况:
- 是否有vpn,存在的话,本机IP会产生变化导致不能搜到?抓包可以看到,3702端口包的数据源地址改变。
- uuid是否已经赋值。
- 有时,windows宿主机装有虚拟机,也可能造成onvif客户端的ip获取错误。故搜索不到。
这些问题,在交换机或者路由支持本地局域网跨网段数据UDP交互时,均不会产生。
调试技巧:
fsend / frecv 打印出发送和接收到的报文。使用xml编辑器分析。当然也可以直接用浏览器看。
1、打开onvif调试开关,以便让onvif打印一些可用的调试信息。
在Makefile中添加调试宏定义如: CC = gcc -DDEBUG2、打开调试宏后,默认在程序运行的目录产生三个文件:
- 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
SOAP 错误代码表http://wenku.baidu.com/link?url=rujSmnpjBxjS3mGZrejoVVOShcPu_5Wu_9RKrQ6qWCB12xrZUvVoFkYRepLu0y6oTk6-bB5AnJ_7KxF6s8rXcb1BFko6DbBpXg0_7G0D7cu
linux设备上的Onvif 实现8:编写媒体信息获取程序http://blog.csdn.net/u012084827/article/details/12201897 -
Java onvif协议通用协议获取rtsp地址
2020-09-11 18:04:27吐槽下可搜索的资源真的是少,好多都是4 5年前的资料 onvif协议大部分是c或者android 资料 java的比较少 感谢大佬留下的...前言:因为例子有限,没有历史流rtsp地址的使用,在参考例子和源码下,走了很多坑 上代...这里就会获取到实时流和历史流的rtsp地址 后续文章会使用这个地址实现视频推流
Java 视频直播JavaCV(ffmpeg h264)+RTSP实现低延时1秒推流
吐槽下可搜索的资源真的是少,好多都是4 5年前的资料
onvif通用协议就不说了,大多摄像头都可用,利用onvif获取实时流和历史流转换成h264编码推送到前端,用h5新标签直接播放
onvif协议大部分是c或者android 资料 java的比较少 感谢大佬留下的宝贵资源:
1. https://github.com/RootSoft/ONVIF-Java (功能不够完善,没有使用例子)
2. https://github.com/fpompermaier/onvif (这个是基于上面的项目封装,有简单的例子,这里使用的这个)
实际使用:
前言:因为例子有限,没有历史流rtsp地址的使用,在参考例子和源码下,走了很多坑
后续更新利用javacv 使用rtsp地址拉取流推送出去。
上代码:
重点!重点!重点!总要的地方说三遍,将上述的第二个github项目拉下来打包到本地仓库 下面的
pom 中的onvif-ws-client就是打包到本地仓库的
项目地址:https://github.com/xsjzf/onvif_java
核心的地方是:
这里说明下例子中只有实时流rtsp地址的方法获取
在onvif-ws-client的源码中有很多功能例子中只有media 也就是实时流的
要使用历史流则需要自行添加 就是replay和recording 其他功能详见onvif的官网文档
Capabilities capabilities = this.device.getCapabilities(Arrays.asList(CapabilityCategory.ALL));
@javax.xml.bind.annotation.XmlElement(name = "Analytics") protected org.onvif.ver10.schema.AnalyticsCapabilities analytics; @javax.xml.bind.annotation.XmlElement(name = "Device") protected org.onvif.ver10.schema.DeviceCapabilities device; @javax.xml.bind.annotation.XmlElement(name = "Events") protected org.onvif.ver10.schema.EventCapabilities events; @javax.xml.bind.annotation.XmlElement(name = "Imaging") protected org.onvif.ver10.schema.ImagingCapabilities imaging; @javax.xml.bind.annotation.XmlElement(name = "Media") protected org.onvif.ver10.schema.MediaCapabilities media; @javax.xml.bind.annotation.XmlElement(name = "PTZ") protected org.onvif.ver10.schema.PTZCapabilities ptz; @javax.xml.bind.annotation.XmlElement(name = "Extension") protected org.onvif.ver10.schema.CapabilitiesExtension extension;
若需要历史回放功能呢,则需要对上面第二个github 地址进行修改,修改如下:OnvifDevice 的init方法中
Capabilities对象里面除了基本的功能大多在Extension里面 @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "CapabilitiesExtension", propOrder = { "any", "deviceIO", "display", "recording", "search", "replay", "receiver", "analyticsDevice", "extensions" }) public class CapabilitiesExtension { @XmlAnyElement(lax = true) protected List<java.lang.Object> any; @XmlElement(name = "DeviceIO") protected DeviceIOCapabilities deviceIO; @XmlElement(name = "Display") protected DisplayCapabilities display; @XmlElement(name = "Recording") protected RecordingCapabilities recording; @XmlElement(name = "Search") protected SearchCapabilities search; @XmlElement(name = "Replay") protected ReplayCapabilities replay; @XmlElement(name = "Receiver") protected ReceiverCapabilities receiver; @XmlElement(name = "AnalyticsDevice") protected AnalyticsDeviceCapabilities analyticsDevice; @XmlElement(name = "Extensions") protected CapabilitiesExtension2 extensions;
例如我这里需要历史流的功能那么我在init方法中初始化添加上
if (capabilities.getExtension().getReplay() != null && capabilities.getExtension().getReplay().getXAddr() != null) { this.replayPort = new ReplayService().getReplayPort(); this.replayPort = getServiceProxy((BindingProvider) replayPort, capabilities.getExtension().getReplay().getXAddr()) .create(ReplayPort.class); } if (capabilities.getExtension().getRecording() != null && capabilities.getExtension().getRecording().getXAddr() != null) { this.recordingPort = new RecordingService().getRecordingPort(); this.recordingPort = getServiceProxy((BindingProvider) recordingPort, capabilities.getExtension().getRecording().getXAddr()) .create(RecordingPort.class); } if (capabilities.getExtension().getSearch() != null && capabilities.getExtension().getSearch().getXAddr() != null) { this.searchPort = new SearchService().getSearchPort(); this.searchPort = getServiceProxy((BindingProvider) searchPort, capabilities.getExtension().getSearch().getXAddr()) .create(SearchPort.class); }
具体使用:
/** * 获取到OnvifDevice对象 * @param host 摄像头地址 92.168.xx.yy, or http://host[:port] * @param username 用户名 * @param password 密码 * @param profileToken "MediaProfile000" If empty, will use first profile. * @return */ public static OnvifDevice getOnvifCredentials(String host, String username, String password, String profileToken){ try { OnvifCredentials credentials = new OnvifCredentials(host, username, password, profileToken); //补全host URL u = credentials.getHost().startsWith("http") ? new URL(credentials.getHost()) : new URL("http://" + credentials.getHost()); return new OnvifDevice(u, credentials.getUser(), credentials.getPassword()); } catch (MalformedURLException | ConnectException | SOAPException e) { e.printStackTrace(); throw new RrException(e.getMessage()); } } /** * 获取实时rtsp地址 * @param onvifDevice 设备 * @return * @throws Exception */ public static String getRtspUrl(OnvifDevice onvifDevice) throws Exception { List<Profile> profiles = onvifDevice.getMedia().getProfiles(); for (Profile profile : profiles) { String profileToken = profile.getToken(); String rtsp = onvifDevice.getStreamUri(profileToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); } return ""; } /** * 获取历史rtsp地址 * @param onvifDevice 设备 * @return */ public static String getReplayUrl(OnvifDevice onvifDevice){ List<GetRecordingsResponseItem> recordings = onvifDevice.getRecordingPort().getRecordings(); for (GetRecordingsResponseItem recording : recordings) { String recordingToken = recording.getRecordingToken(); String rtsp = onvifDevice.getReplayUri(onvifDevice, recordingToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); } return ""; } /** * 转换 时间段为rtsp时间格式 * @param start 开始 * @param end 结束 * @return ?starttime=20200908t093812z&endtime=20200908t104816z */ public static String getRtspTimeSpace(LocalDateTime start, LocalDateTime end){ Long st = start.toInstant(ZoneOffset.of("+8")).toEpochMilli(); Long ed = end.toInstant(ZoneOffset.of("+8")).toEpochMilli(); String ios8601St = getDate(st); String ios8601Ed = getDate(st); return "?starttime=" + ios8601St + "&endtime=" + ios8601Ed; } /** * 时间戳转换成IOS8601格式 * @param beginTime * @return */ public static String getDate(Long beginTime) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String bt = format.format(beginTime); Date date = null; try { date = format.parse(bt); } catch (Exception e) { e.printStackTrace(); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'z'"); return sdf.format(date); }
2020-11-27:
为
兼容海康,大华和宇视,修改相关rtsp组装
/** * 获取实时rtsp地址 * 海康nvr: * (主码流)rtsp://**.168.101.**:554/Streaming/Unicast/channels/101 * (子码流)rtsp://**.168.101.**:554/Streaming/Unicast/channels/102 * 海康单机: * (主码流)rtsp://**.168.30.**:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 * (子码流)rtsp://**.168.30.**:554/Streaming/Channels/102?transportmode=unicast&profile=Profile_2 * 大华单机: * (主码流)rtsp://**.168.30.**:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif * (子码流)rtsp://**.168.30.**:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif * @param onvifDevice 设备 * @return * @throws Exception */ public static Map<String, String> getRtspUrl(OnvifDevice onvifDevice, Integer id) { Map<String, String> map = Maps.newLinkedHashMap(); //兼容大华设备 List<Profile> profiles = onvifDevice.getMedia().getProfiles(); if (profiles == null || profiles.size() == 0){ LOG.error("未查询到实时Profiles"); return map; } for (Profile profile : profiles) { String profileToken = profile.getToken(); System.out.println(profileToken); String rtsp = onvifDevice.getStreamUri(profileToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); //只取主码流 01结尾 //兼容单个摄像头无法从profileToken获取到通道号 //兼容大华单个摄像头 String channel = id + profileToken; // String channel = profileToken.substring(profileToken.length() - 3); String hkUrl = rtsp.contains("?") ? rtsp.substring(0, rtsp.indexOf("?")) : rtsp; if (hkUrl.endsWith("01") || rtsp.contains("subtype=0")){ map.put(channel, uri); } } return map; }
/** * 获取历史rtsp地址 * rtsp://admin:zouwei678@192.168.101.2:554/Streaming/tracks/301?starttime=20201119T134258z&endtime=20201119T134316z * rtsp://admin:zouwei678@192.168.101.2:554/Streaming/Unicast/channels/301?starttime=20201119T134258z&endtime=20201119T134316z * @param onvifDevice 设备 * @return */ public static Map<String, String> getReplayUrl(OnvifDevice onvifDevice, Integer id){ Map<String, String> map = Maps.newLinkedHashMap(); RecordingPort recordingPort = onvifDevice.getRecordingPort(); if (recordingPort == null){ LOG.error("该设备不支持历史回放"); return map; } List<GetRecordingsResponseItem> recordings = recordingPort.getRecordings(); if (recordings == null || recordings.size() == 0){ LOG.error("未查询到历史Profiles"); return map; } for (GetRecordingsResponseItem recording : recordings) { String recordingToken = recording.getRecordingToken(); // String channel = recordingToken.substring(recordingToken.length() - 3); String channel = id + recordingToken; String rtsp = onvifDevice.getReplayUri(onvifDevice, recordingToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); map.put(channel, uri); } return map; }
在实际项目中使用发现,海康的nvr录像机在使用rtsp拼接时间方式获取历史流中,同一时间只能开启一路,再开启时会报错
这个错误经过多方搜索并不是ffmpeg或者javacv的问题,直接使用vlc打开也是这样的,根本原因是带宽限制,目前没有解决方法,有大佬有方法的麻烦告知下,不胜感激
****持续更新****
2021-04-12 介于很多人导入
<dependency> <groupId>org.onvif</groupId> <artifactId>onvif-ws-client</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在此提供直接放入本地仓库包
提取码:v974
直接解压到本地仓库org文件包下
实际使用封装:
/** * 初始化Onvif * @param ip * @param userName * @param passwd * @param id */ private void initOnvif(String ip, String userName, String passwd, Integer id) { try { OnvifDevice onvifDevice = MediaUtils.getOnvifCredentials(ip, userName, passwd, "MediaProfile000"); Map<String, String> rtspUrl = MediaUtils.getRtspUrl(onvifDevice, id); Map<String, String> replayUrl = MediaUtils.getReplayUrl(onvifDevice, id); if (rtspUrl.isEmpty()){ LOG.error("初始化加载未获取到实时RTSP地址: " + ip); } if (replayUrl.isEmpty()){ LOG.error("初始化加载未获取到历史RTSP地址: " + ip); } map.put(id, new CameraInfo(rtspUrl, replayUrl)); System.out.println("加载成功服务:" + ip); } catch (Exception e) { e.printStackTrace(); System.out.println("加载失败:" + ip); } }
package com.onvif.java.utils; import com.google.common.collect.Maps; import com.onvif.java.model.OnvifCredentials; import com.onvif.java.service.OnvifDevice; import com.onvif.java.service.UdpService; import org.bytedeco.ffmpeg.avcodec.AVPacket; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.*; import org.onvif.ver10.recording.wsdl.RecordingPort; import org.onvif.ver10.schema.GetRecordingsResponseItem; import org.onvif.ver10.schema.Profile; import org.onvif.ver10.schema.TransportProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Random; import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref; /** * @program: javaOnvif * @description: 获取rtsp地址 * @author: zf * @create: 2020-09-08 10:50 **/ @Service public class MediaUtils { /** * 获取到OnvifDevice对象 * @param host 摄像头地址 92.168.xx.yy, or http://host[:port] * @param username 用户名 * @param password 密码 * @param profileToken "MediaProfile000" If empty, will use first profile. * @return */ public static OnvifDevice getOnvifCredentials(String host, String username, String password, String profileToken) throws Exception { OnvifCredentials credentials = new OnvifCredentials(host, username, password, profileToken); //补全host URL u = credentials.getHost().startsWith("http") ? new URL(credentials.getHost()) : new URL("http://" + credentials.getHost()); return new OnvifDevice(u, credentials.getUser(), credentials.getPassword()); } /** * 获取实时rtsp地址 * 海康nvr: * (主码流)rtsp://**.168.101.**:554/Streaming/Unicast/channels/101 * (子码流)rtsp://**.168.101.**:554/Streaming/Unicast/channels/102 * 海康单机: * (主码流)rtsp://**.168.30.**:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 * (子码流)rtsp://**.168.30.**:554/Streaming/Channels/102?transportmode=unicast&profile=Profile_2 * 大华单机: * (主码流)rtsp://**.168.30.**:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif * (子码流)rtsp://**.168.30.**:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif * @param onvifDevice 设备 * @return * @throws Exception */ public static void getRtspUrl(OnvifDevice onvifDevice, Integer id) { Map<String, String> map = Maps.newLinkedHashMap(); //兼容大华设备 List<Profile> profiles = onvifDevice.getMedia().getProfiles(); if (profiles == null || profiles.size() == 0){ return; } for (Profile profile : profiles) { String profileToken = profile.getToken(); String rtsp = onvifDevice.getStreamUri(profileToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); System.out.println(uri); } } /** * 获取历史rtsp地址 * rtsp://admin:zouwei678@192.168.101.2:554/Streaming/tracks/301?starttime=20201119T134258z&endtime=20201119T134316z * rtsp://admin:zouwei678@192.168.101.2:554/Streaming/Unicast/channels/301?starttime=20201119T134258z&endtime=20201119T134316z * @param onvifDevice 设备 * @return */ public static void getReplayUrl(OnvifDevice onvifDevice, Integer id){ Map<String, String> map = Maps.newLinkedHashMap(); RecordingPort recordingPort = onvifDevice.getRecordingPort(); if (recordingPort == null){ return; } List<GetRecordingsResponseItem> recordings = recordingPort.getRecordings(); if (recordings == null || recordings.size() == 0){ return; } for (GetRecordingsResponseItem recording : recordings) { String recordingToken = recording.getRecordingToken(); // String channel = recordingToken.substring(recordingToken.length() - 3); String channel = id + recordingToken; String rtsp = onvifDevice.getReplayUri(onvifDevice, recordingToken, TransportProtocol.RTSP); String uri = "rtsp://" + onvifDevice.getUser() + ":" + onvifDevice.getPassword() + "@" + rtsp.replace("rtsp://", ""); System.out.println(uri); } } /** * 转换 时间段为rtsp时间格式 * @param st 开始 * @param end 结束 * @return ?starttime=20200908t093812z&endtime=20200908t104816z */ public static String getRtspTimeSpace(Long st, Long end){ String ios8601St = getDate(st); String ios8601Ed = getDate(end); return "?starttime=" + ios8601St + "&endtime=" + ios8601Ed; } /** * 时间戳转换成IOS8601格式 * @param beginTime * @return */ public static String getDate(Long beginTime) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String bt = format.format(beginTime); Date date = null; try { date = format.parse(bt); } catch (Exception e) { e.printStackTrace(); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'z'"); return sdf.format(date); } }
package com.onvif.java.model; import lombok.Data; /** * onvif 连接信息 * @author zf */ @Data public class OnvifCredentials { /** * 92.168.xx.yy, or http://host[:port] */ private String host; /** * admin */ private String user; /** * secret */ private String password; /** * "MediaProfile000" If empty, will use first profile. */ private String profile; public OnvifCredentials(String host, String user, String password, String profile) { this.host = host; this.user = user; this.password = password; this.profile = profile; } public String details() { return host + "," + user + "," + password + "," + profile; } }
-
Linux下基于Onvif获取RTSP地址(二)
2019-12-04 15:54:49在前一篇文章中讲到如何安装gsoap,本篇文章将介绍如何使用gsoap获取onvif协议,并通过onvif协议在客户端获取RTSP地址。 1. 通过gsoap获取onvif协议 首先要明白gsoap只是一个工具 ... -
ONVIF客户端读取rtsp地址
2018-02-11 14:16:25Windows下采用VS2017做ONVIF协议的客户端,自己下载gsoap编译C语言框架开发,项目采用的是沃沃安的HDMI编码器作为服务器,实现搜索设备,获取设备信息,获取设备能力,读取音视频流地址,以及图像抓拍地址。... -
onvif学习笔记10:获取RTSP流地址
2019-04-15 23:04:52网友购买了我的ONVIF视频课程,问了我几个问题,其中一个是实现ONVIF服务器的获取RTSP地址功能,本文整理出一个思路,愿帮到在学习ONVIF路上的诸君。 我录制的视频是很早之前的,一直没抽时间再录一个实战视频,... -
获取rtsp流方式
2018-11-28 17:25:32监控视频RTSP对接文档,加上java代码,可以实现对各大厂商提供的rtsp流完美对接 -
Linux下onvif客户端获取h265 IPC摄像头的RTSP地址
2021-05-18 17:09:551、 设备搜索,去获取webserver 的地址 ,目的是在获取能力提供服务地址,demo:https://www.cnblogs.com/croxd/p/10683429.html2、 GetCapabilities获取能力,目的是在GetProfiles时提供媒体地址,demo:... -
Go语言实现Onvif客户端:6、获取rtsp流地址
2021-04-01 18:40:33Go语言实现Onvif客户端:6、获取rtsp流地址 文章目录Go语言实现Onvif客户端:6、获取rtsp流地址1、代码2、结果:3、查看 1、代码 获取并选择Profile token后获取rtsp流地址也是调用接口即可: /** * @Description:... -
Atitit onvif协议获取rtsp地址播放java语言attilx总结
2016-12-23 16:17:40Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用getStreamUri 获得rtsp地址 ,使用类库ONVIF Java ... -
网络摄像机rtsp地址详解。
2021-09-13 10:21:43关于英飞拓、大华、海康品牌的网络摄像机rtsp地址的介绍,方便大家快速的获取rtsp地址格式及信息。 1 1:对应的英飞拓、大华、海康品牌的网络摄像机接入网络; 2 2:英飞拓、大华、海康品牌的网络摄像机的rtsp地址... -
C# onvif获取摄像头rtsp视频流地址和抓拍图片地址
2018-11-15 09:32:20C#使用onvif协议获取网络摄像头rtsp视频流地址和图片地址,将rtsp视频流地址保存到本地mp4可以参考我的另一个资源 -
ONVIF Conformance Test Tool获取RTSP地址及VLC播放音频
2019-04-24 11:19:24ONVIF TestConformance Test Tool获取RTSP地址及VLC播放音频 一、目的 通过VLC软件网络串口流播放拾音器的声音。 二、设备 笔记本 IP 10.0.1.203 拾音器:淘宝某款 IP 10.0.1.201,该拾音器实际就是IPCAMRA主板加... -
Onvif设备发现+鉴权认证+RTSP地址获取VS2010.ra
2018-03-07 09:27:06Onvif设备发现+鉴权认证+RTSP地址获取VS2010.ra Onvif设备发现+鉴权认证+RTSP地址获取VS2010.ra -
海康威视摄像头rtsp地址
2021-11-16 10:23:30rtsp://username:password@<ipaddress>:<port>/<videotype>/ch<number>/<streamtype>/av_stream 说明: username:用户名,例如admin passwd:密码,例如12345 ip:设备的ip地址,... -
python 通过onvif协议获取 Rtsp 视频.zip
2021-03-01 09:45:17python 通过onvif协议获取 Rtsp 视频.zip -
获取海康摄像机/录像机rtsp视频流地址格式
2021-11-30 16:24:56★目前海康录像机、网络摄像机,网络球机的RTSP单播取流格式如下(车载录像机不支持RTSP取流): rtsp://用户名:密码@IP:554/Streaming/Channels/101 →录像机示例: 取第1个通道的主码流预览 rtsp://admin:hik12345... -
视频监控安防平台-国标28181(GB28181)平台通过http请求获取rtsp地址再通过rtsp请求视频(HTTP服务转GB28181...
2018-08-18 11:23:55视频监控安防平台-国标28181(GB28181)平台通过http请求获取rtsp地址再通过rtsp请求视频(HTTP服务转GB28181) 最近在整理一些小型平台的功能,由原来的GB28181_RTSP小型平台改造成http服务+rtsp服务+httpclient等... -
node-rtsp-live555:从IPC获取RTSP流并在FLV流中导出
2021-02-03 19:00:13这是一个包装程序,可让您从IPC获取RTSP流并以FLV流导出。 安装 NPM npm install rtsp-live555安装最新的稳定版本 npm install godka/node-rtsp-live555从github安装最新版本 从Github克隆最新版本 git clone ... -
Atitit onvif协议获取rtsp地址播放java语言 attilx总结
2016-12-23 16:17:00Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用getStreamUri获得rtsp地址 ,使用类库ONVIF Java ... -
C/C++ 解析RTSP地址获取用户名、密码、IP、端口号信息
2020-11-12 10:03:45#include <iostream> using namespace std; long PraseURL( const char *url,char* user,char* psd, char *szip, int *iport ..._strnicmp(url, "rtsp://", 7)) { //2.查找用户名 char *s, *ss; s = -
FFmpeg 获取 rtsp rtmp 流
2021-10-05 07:25:25使用 FFmpeg 获取 rtsp/rtmp 流非常方便,将开发 rtsp/rtmp 客户端工作变的简单了许多。 将 rtsp/rtmp 流路径送入 avformat_open_input 函数进行打开动作,得到 AVFormatContext 封装格式上下文; 调用 avformat_... -
网络摄像机(IPC)开发(5):ONVIF获取rtsp流url
2019-10-12 14:30:37通过GetStreamUri获取IPC的rtsp流url,这个url地址就是后面RTSP交互时,获取视频流的地址,同时,通过VLC等软件加载网络URL,也是可以播放视频流的。 二、获取编码参数实现 1、创建tcp socket,IP为192.168.100.... -
ONVIF协议实现搜索局域网IPC设备+RTSP地址获取(QuickGBLink)
2019-12-08 14:01:53ONVIF协议实现搜索局域网摄像机(IPC)+RTSP地址获取,可以替代Onvif Device Manager实现发现IPC和获取IPC设备信息,原文链接:https://blog.csdn.net/QuickGBLink/article/details/103375967