背景
上一篇文章主要讲述了如何获取设备的能力。获取到设备的能力之后,就可以做很多事情了。本篇主要讲述如何利用获取的设备能力集合,找到设备的流媒体地址。
目录
文章发表的顺序可能有点乱,麻烦根据《基于ONVIF协议的摄像机监控管理平台(1)概述》里面的文章目录进行阅读,抱歉了
一、整体思路
- 搜索发现网络中的设备,并且登记他们的服务地址。
利用ONVIF协议中的GetCapabilities接口,得到Media的服务地址。
利用GetProfiles接口,得到主码流、辅码流等信息,这些信息里包含一个ProfileToken。
使用 ProfileToken 去Media的服务地址那里,调用GetStreamUri接口,得到主码流和辅码流的流媒体地址。
二、调用GetProfiles接口获取token
关于上面整体思路的第一和第二点我就不叙述了,因为在之前的文章中已经详细说过了。
之前我们调用GetCapabilities接口,得到Media的信息我再贴一下,xAddr就是服务地址了。
<tt:Media> <tt:XAddr>http://192.168.110.163:80/onvif/media</tt:XAddr> <tt:StreamingCapabilities> <tt:RTPMulticast>true</tt:RTPMulticast> <tt:RTP_TCP>true</tt:RTP_TCP> <tt:RTP_RTSP_TCP>true</tt:RTP_RTSP_TCP> </tt:StreamingCapabilities> <tt:Extension> <tt:ProfileCapabilities> <tt:MaximumNumberOfProfiles>6</tt:MaximumNumberOfProfiles> </tt:ProfileCapabilities> </tt:Extension> </tt:Media>
- 下一步就是去http://192.168.110.163:80/onvif/media ,调用GetProfiles接口。
具体代码如下。
private async void HttpGetProfiles()
{
//ONVIF服务所在的地址
var url = "http://192.168.110.163/onvif/media";
//创建一个http请求
using (var client = new HttpClient())
{
//清楚http请求的所有默认Header,在Header中添加SOAPAction
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("SOAPAction", "http://www.onvif.org/ver10/media/wsdl/GetProfiles");
//这段xml字符串是复制过来的,用它生成一个xml结构,然后选择性的修改一部分内容
String xmlStr = "<?xml version=\"1.0\" encoding=\"utf - 8\"?><s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\"><s:Header><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><wsse:UsernameToken><wsse:Username>admin</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">59zlfHOLcoptDNzx2MVvsIiqcow=</wsse:Password><wsse:Nonce>hlCkYZgzndYSXPcrr+RRXg==</wsse:Nonce><wsu:Created>2017-12-28T09:57:22Z</wsu:Created></wsse:UsernameToken></wsse:Security></s:Header><s:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GetProfiles xmlns=\"http://www.onvif.org/ver10/media/wsdl\"/></s:Body></s:Envelope>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlStr);
XmlNode username = doc.GetElementsByTagName("wsse:Username").Item(0);
XmlNode digest = doc.GetElementsByTagName("wsse:Password").Item(0);
XmlNode nonce = doc.GetElementsByTagName("wsse:Nonce").Item(0);
XmlNode created = doc.GetElementsByTagName("wsu:Created").Item(0);
Console.WriteLine(username.InnerText.ToString());
Console.WriteLine(digest.InnerText.ToString());
Console.WriteLine(nonce.InnerText.ToString());
Console.WriteLine(created.InnerText.ToString());
//生成请求和相应的参数
username.InnerText = "admin";
nonce.InnerText = GetNonce();
created.InnerText = GetCreated();
digest.InnerText = GetPasswordDigest(nonce.InnerText, created.InnerText, "123456");
Console.WriteLine(doc.OuterXml);
//设置一下 http post的格式
var content = new StringContent(doc.OuterXml, Encoding.UTF8, "application/xml");
//发起请求
var response = await client.PostAsync(url, content);
//查看响应信息
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString.ToString());
}
}
需要注意一下的是,我们现在调用的不是之前的devicemgmt里面的接口了,相应的地址要改一下。
主要就是SOAPAction的地址和如果调用不出错的话,会得到如下的一段很长很长的响应应答信息(我把绝大部分都删掉了,留下几个我准备用到信息)。
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:xmime4="http://www.w3.org/2004/11/xmlmime" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:wsrf-bf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrf-r="http://docs.oasis-open.org/wsrf/r-2" xmlns:tes-e="http://www.onvif.org/ver10/events/wsdl/EventBinding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tes-nc="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding" xmlns:tes-np="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding" xmlns:tes-sm="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:xmime="http://www.w3.org/2004/06/xmlmime" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tan-ae="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding" xmlns:tan-re="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding" xmlns:trc="http://www.onvif.org/ver10/recording/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:tse="http://www.onvif.org/ver10/search/wsdl" xmlns:tpl="http://www.onvif.org/ver10/plus/wsdl" xmlns:tplt="http://www.onvif.org/ver10/plus/schema">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<trt:GetProfilesResponse>
<trt:Profiles fixed="true" token="media_profile1"> </trt:Profiles>
<trt:Profiles fixed="true" token="media_profile2"> </trt:Profiles>
</trt:GetProfilesResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
- 有两个Profile Token。可以先记一下。
<trt:Profiles fixed="true" token="media_profile1"> </trt:Profiles>
<trt:Profiles fixed="true" token="media_profile2"> </trt:Profiles>
这里获取到三个Profiles,分别代表三个流的配置。(数量上可能会有所差异的,不同设备未必数量上一样)
三个Profiles分别对应三个token
三、利用Profiles token调用GetStreamUri
调用GetStreamUri的xml格式以及参数说明请参考官方wsdl文件里面的GetStreamUri部分。调用RTP视频和RTSP视频会略有差异,多播、单播也有差异。
我这里给出一个获取RTP单播视频地址的例子(调用方法和调用其它接口是一样的,唯一不同就是xml部分)。
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
<UsernameToken>
<Username>admin</Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">EGnQxQBm47j744fg9BWRC7YLu3U=</Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">dCJFttH1DE2v3CatSXGAOlsBAAAAAA==</Nonce>
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-12-30T09:59:13.753Z</Created>
</UsernameToken>
</Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetStreamUri xmlns="http://www.onvif.org/ver10/media/wsdl">
<StreamSetup>
<Stream xmlns="http://www.onvif.org/ver10/schema">RTP-Unicast</Stream>
<Transport xmlns="http://www.onvif.org/ver10/schema">
<Protocol>UDP</Protocol>
</Transport>
</StreamSetup>
<ProfileToken>Profile_1</ProfileToken>
</GetStreamUri>
</s:Body>
</s:Envelope>
- 与之前调用其它接口不同的地方,其实就只有GetStreamUri那部分。
贴一下官方描述吧。- Stream [StreamType]
Defines if a multicast or unicast stream is requested - enum { ‘RTP-Unicast’, ‘RTP-Multicast’ }
Transport [Transport] - Protocol [TransportProtocol]
Defines the network protocol for streaming, either UDP=RTP/UDP, RTSP=RTP/RTSP/TCP or HTTP=RTP/RTSP/HTTP/TCP - enum { ‘UDP’, ‘TCP’, ‘RTSP’, ‘HTTP’ }
*最重要的是enum,Stream和Protocol的值是从enum里面选
- Stream [StreamType]
四、调用GetStreamUri的具体代码
上面给了个获取RTP单播地址的xml例子。下面的参考代码是获取RTSP地址,GetStreamUri那部分有所差异。(请根据实际情况从enum中选择合适的参数,实际上GetStreamUri还有其他输入参数的,可以参照官方文档)
private async void HttpGetStreamUri()
{
//ONVIF服务所在的地址
var url = "http://192.168.110.163/onvif/media";
//创建一个http请求
using (var client = new HttpClient())
{
//清楚http请求的所有默认Header,在Header中添加SOAPAction
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("SOAPAction", "http://www.onvif.org/ver10/media/wsdl/GetStreamUri");
//这段xml字符串是复制过来的,用它生成一个xml结构,然后选择性的修改一部分内容
String xmlStr = "<?xml version=\"1.0\" encoding=\"utf - 8\"?><s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\"><s:Header><wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><wsse:UsernameToken><wsse:Username>admin</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">59zlfHOLcoptDNzx2MVvsIiqcow=</wsse:Password><wsse:Nonce>hlCkYZgzndYSXPcrr+RRXg==</wsse:Nonce><wsu:Created>2017-12-28T09:57:22Z</wsu:Created></wsse:UsernameToken></wsse:Security></s:Header><s:Body xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GetStreamUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><StreamSetup><Stream xmlns=\"http://www.onvif.org/ver10/schema\">RTP-Unicast</Stream><Transport xmlns=\"http://www.onvif.org/ver10/schema\"><Protocol>RTSP</Protocol></Transport></StreamSetup><ProfileToken>media_profile2</ProfileToken></GetStreamUri></s:Body></s:Envelope>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlStr);
XmlNode username = doc.GetElementsByTagName("wsse:Username").Item(0);
XmlNode digest = doc.GetElementsByTagName("wsse:Password").Item(0);
XmlNode nonce = doc.GetElementsByTagName("wsse:Nonce").Item(0);
XmlNode created = doc.GetElementsByTagName("wsu:Created").Item(0);
Console.WriteLine(username.InnerText.ToString());
Console.WriteLine(digest.InnerText.ToString());
Console.WriteLine(nonce.InnerText.ToString());
Console.WriteLine(created.InnerText.ToString());
//生成请求和相应的参数
username.InnerText = "admin";
nonce.InnerText = GetNonce();
created.InnerText = GetCreated();
digest.InnerText = GetPasswordDigest(nonce.InnerText, created.InnerText, "123456");
Console.WriteLine(doc.OuterXml);
//设置一下 http post的格式
var content = new StringContent(doc.OuterXml, Encoding.UTF8, "application/xml");
//发起请求
var response = await client.PostAsync(url, content);
//查看响应信息
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString.ToString());
}
}
五、输出的结果
如果调用正常的话,得到的返回结果大概如下。
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:xmime4="http://www.w3.org/2004/11/xmlmime" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:wsrf-bf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrf-r="http://docs.oasis-open.org/wsrf/r-2" xmlns:tes-e="http://www.onvif.org/ver10/events/wsdl/EventBinding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tes-nc="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding" xmlns:tes-np="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding" xmlns:tes-sm="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:xmime="http://www.w3.org/2004/06/xmlmime" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tan-ae="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding" xmlns:tan-re="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding" xmlns:trc="http://www.onvif.org/ver10/recording/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:tse="http://www.onvif.org/ver10/search/wsdl" xmlns:tpl="http://www.onvif.org/ver10/plus/wsdl" xmlns:tplt="http://www.onvif.org/ver10/plus/schema">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<trt:GetStreamUriResponse>
<trt:MediaUri>
<tt:Uri>rtsp://192.168.110.163/media/video2</tt:Uri>
<tt:InvalidAfterConnect>false</tt:InvalidAfterConnect>
<tt:InvalidAfterReboot>false</tt:InvalidAfterReboot>
<tt:Timeout>PT60S</tt:Timeout>
</trt:MediaUri>
</trt:GetStreamUriResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
很明显可以看到地址了
<tt:Uri>rtsp://192.168.110.163/media/video2</tt:Uri>
六、补充说明
有问题的可以咨询我。有偿咨询,哈哈
后记
有任何的疑问或者想法,或者对本文有质疑或者补充的话,欢迎在留言区评论,期待你的分享!
同时,也欢迎关注扫描屏幕下方关注本人微信公众号或者打赏一下这篇文章。
http://xzh.i3geek.com
1 条评论
林微信15915761063 · 2020年3月14日 15:17
我也是C#的,请问有demo吗