博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于DLNA实现iOS,Android投屏:SOAP控制设备
阅读量:4204 次
发布时间:2019-05-26

本文共 10482 字,大约阅读时间需要 34 分钟。

UPdP网络中,控制点和服务之间使用简单对象访问协议(Simple Object Access Protocol,SOAP)

根据收到设备描述文档(DDD)和服务描述文档(SDD),通过解析DDD获取 <controlURL> 控制点可以知道该设备上某个服务的控制点地址。再通过解析 DDD 中 <action> 中的 <name><argumentList> 获取该服务动作的动作名称,参数要求。控制点向 controlURL 发出服务调用信息,表明动作名称和相应参数来调用相应的服务。

SOAP简单对象访问协议

控制点和服务之间使用简单对象访问协议(Simple Object Access Protocol,SOAP)的格式。SOAP 的底层协议一般也是HTTP。在 UPnP 中,把 SOAP 控制/响应信息分成 3 种: UPnP Action Request、UPnP Action Response-Success 和 UPnP Action Response-Error。SOAP 和 SSDP 不一样,所使用的 HTTP 消息是有 Body 内容,Body 部分可以写想要调用的动作,叫做 Action invocation,可能还要传递参数,如想播放一个网络上的视频,就要把视频的URL传过去;服务收到后要 response ,回答能不能执行调用,如果出错则返回一个错误代码。

动作调用(UPnP Action Request)

使用POST方法发送控制消息的格式如下

1234567891011121314151617181920
POST 
HTTP/1.0HOST: hostname:portNumberCONTENT-LENGTH: byte in bodyCONTENT-TYPE: text/xml; charset="utf-8"SOAPACTION: "urn:schemas-upnp-org:service:serviceType:v#actionName"
in arg values

  • control URL: 中提到的 设备描述文件urn:upnp-org:serviceId:AVTransport 服务的 <controlURL>
  • HOST: 上述服务器的根地址和端口号。
  • actionName: 需要调用动作的名称,对应相应服务的 服务描述文件<SCPDURL> 中的 <action><name> 字段。
  • argumentName: 输入参数名称,对应相应服务的 服务描述文件<SCPDURL> 中的 <action> <argument> <name> 字段。
  • in arg values: 输入参数值,具体的可以通过 ,可以通过 服务描述文件<SCPDURL> <action> <relatedStateVariable> 提到的状态变量来得知值得类型。
  • urn:schemas-upnp-org:service:serviceType:v:对应该 设备描述文件 相应服务的 <serviceType 字段。

动作响应(UPnP Action Response-Succes)

收到控制点发来的动作调用请求后,设备上的服务必须执行动作调用。,并在 30s 内响应。如果需要超过 30s 才能完成执行的动作,则可以先返回一个应答消息,等动作执行完成再利用事件机制返回动作响应。

1234567891011121314151617
HTTP/1.0 200 OK                             // 响应成功响应头CONTENT-TYPE: text/xml; charset="utf-8" Date: Tue, 01 Mar 2016 10:00:36 GMT+00:00CONTENT-LENGTH: byte in body
out arg value
  • actionNameResponse: 响应的动作名称
  • arugumentName: 当动作带有输出变量时必选,输出变量名称
  • out arg values: 输出变量名称值

动作错误响应(UPnP Action Response-Succes)

如果处理动作过程中出现错误,则返回一个一下格式的错误响应。

123456789101112131415161718192021
HTTP/1.0 500 Internal Server Error          // 响应成功响应头CONTENT-TYPE: text/xml; charset="utf-8" Date: Tue, 01 Mar 2016 10:00:36 GMT+00:00CONTENT-LENGTH: byte in body
s:Client
UPnPError
402
Invalid or Missing Args

  • faultcode: SOAP规定使用元素,调用动作遇到的错误类型,一般为s:Client。
  • faultstring: SOAP规定使用元素,值必须为 UPnPError。
  • detail: SOAP规定使用元素,错误的详细描述信息。
  • UPnPError: UPnP规定元素。
  • errorCode: UPnP规定元素,整数。详见下表。
  • errorDescription: UPnP规定元素,简短错误描述。
errorCode errorDescription 描述
401 Invalid Action 这个服务中没有该名称的动作
402 Invalid Args 参数数据错误 not enough in args, too many in arg, no in arg by that name, one or more in args 之一
403 Out of Sycs 不同步
501 Action Failed 可能在当前服务状态下返回,以避免调用此动作
600 ~ 699 TBD 一般动作错误,由 UPnP 论坛技术委员会定义
700 ~ 799 TBD 面向标准动作的特定错误,由 UPnP 论坛工作委员会定义
800 ~ 899 TBD 面向非标准动作的特定错误,由 UPnP 厂商会定义

投屏基本命令及其响应

所有命令以发向 发现的设备。除了网址以外,其余部分均不需要修改。

所有动作请求使用 POST 请求发送,并且请求Header均如下所示,其中:

  • control URL: 中提到的 设备描述文件urn:upnp-org:serviceId:AVTransport 服务的 <controlURL>
  • HOST: 上述服务器的根地址和端口号。
  • urn:schemas-upnp-org:service:serviceType:v:对应相应设备的 设备描述文件 相应服务的 <serviceType 字段。
  • actionName: 需要调用动作的名称,对应相应服务的 服务描述文件<SCPDURL> 中的 <action><name> 字段。
12345
POST /dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/action HTTP/1.0HOST: 192.168.1.243:46201CONTENT-LENGTH: byte in bodyCONTENT-TYPE: text/xml; charset="utf-8"SOAPACTION: "urn:schemas-upnp-org:service:serviceType:v#actionName"

下面请求和响应均忽略Header,参数列表中列出Header的SOAPACTION值

设置播放资源URI

动作请求

设置当前播放视频动作统一名称为 SetAVTransportURI 。 需要传递参数有

  • InstanceID:设置当前播放时期时为 0 即可。
  • CurrentURI: 播放资源URI
  • CurrentURIMetaData: 媒体meta数据,可以为空
  • Header_SOAPACTION: “urn:upnp-org:serviceId:AVTransport#SetAVTransportURI”

有些设备传递播放URI后就能直接播放,有些设备设置URI后需要发送播放命令,可以在接收到 SetAVTransportURIResponse 响应后调用播放动作来解决。

12345678910
0
http://125.39.35.130/mp4files/4100000003406F25/clips.vorwaerts-gmbh.de/big_buck_bunny.mp4

响应

123456

播放

动作请求

播放视频动作统一名称为 Play 。 需要传递参数有

  • InstanceID:设置当前播放时期时为 0 即可。
  • Speed:播放速度,默认传 1 。
  • Header_SOAPACTION: “urn:upnp-org:serviceId:AVTransport#Pause”
123456789
0
1

响应

123456

暂停

动作请求

暂停视频动作统一名称为 Pause 。 需要传递参数有

  • InstanceID:设置当前播放时期时为 0 即可。
  • Header_SOAPACTION: “urn:upnp-org:serviceId:AVTransport#Pause”
12345678
0
1

响应

123456

获取播放进度

动作请求

获取播放进度动作统一名称为 GetPositionInfo 。 需要传递参数有

  • InstanceID:设置当前播放时期时为 0 即可。
  • MediaDuration: 可以为空。
  • Header_SOAPACTION: “urn:upnp-org:serviceId:AVTransport#MediaDuration”
123456789
0

响应

获取播放进度响应中包含了比较多的信息,其中我们主要关心的有一下三个:

  • TrackDuration: 目前播放视频时长
  • RelTime: 真实播放时长
  • AbsTime: 相对播放时长

注:目前为止还没发现 RelTime AbsTime 和不一样的情况,选用 RelTime 就ok。

123456789101112131415
0
00:04:32
00:00:07
00:00:07
2147483647
2147483647

跳转至特定进度或视频

动作请求

跳转到特定的进度或者特定的视频(多个视频播放情况),需要调用 Seek 动作,传递参数有:

  • InstanceID: 一般为 0 。
  • Unit:REL_TIME(跳转到某个进度)或 TRACK_NR(跳转到某个视频)。
  • Target: 目标值,可以是 00:02:21 格式的进度或者整数的 TRACK_NR。
  • Header_SOAPACTION: “urn:upnp-org:serviceId:AVTransport#Seek”
12345678910
0
REL_TIME
00:02:21

响应

123456

iOS实现

需要用到库

  1. - 轻量 XML 库,用于构造和解析XML

构造动作XML

首先利用 构造动作 XML 部分。由于所有动作结构相似,写了个构造方法

12345678910111213
private func prepareXMLFileWithCommand(command:AEXMLElement) -> String {
// 创建 AEXMLDocument 实例 let soapRequest = AEXMLDocument() // 设置XML外层 let attributes = [ "xmlns:s" : "http://schemas.xmlsoap.org/soap/envelope/","s:encodingStyle" : "http://schemas.xmlsoap.org/soap/encoding/"] let envelope = soapRequest.addChild(name: "s:Envelope", attributes: attributes) let body = envelope.addChild(name: "s:Body") // 把 command 添加到 XML 中间 body.addChild(command) return soapRequest.xmlString }

根据不同动作构造 XML ,比如 传递URI播放动作

1234567891011121314151617181920212223242526
/**投屏- parameter URI: 视频URL*/func SetAVTransportURI(URI:String) {
let command = AEXMLElement("u:SetAVTransportURI",attributes: ["xmlns:u" : "urn:schemas-upnp-org:service:AVTransport:1"]) command.addChild(name: "InstanceID", value: "0") command.addChild(name: "CurrentURI", value: URI) command.addChild(name: "CurrentURIMetaData") let xml = self.prepareXMLFileWithCommand(command) self.sendRequestWithData(xml,action: "SetAVTransportURI")}/**播放视频*/func Play() {
let command = AEXMLElement("u:Play",attributes: ["xmlns:u" : "urn:schemas-upnp-org:service:AVTransport:1"]) command.addChild(name: "InstanceID", value: "0") command.addChild(name: "Speed", value: "1") let xml = self.prepareXMLFileWithCommand(command) self.sendRequestWithData(xml,action: "Play")}

发送动作请求

123456789101112131415161718192021222324252627
private func sendRequestWithData(xml:String, action:String) {
let request = NSMutableURLRequest(URL: NSURL(string: controlURL)!) // 使用 POST 请求发送动作 request.HTTPMethod = "POST" request.addValue("text/xml", forHTTPHeaderField: "Content-Type") // 添加SOAPAction动作名称 request.addValue("\(service.serviceId)#\(action)", forHTTPHeaderField: "SOAPAction") request.HTTPBody = xml.dataUsingEncoding(NSUTF8StringEncoding) let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in guard error == nil && data != nil else {
print("error=\(error)") return } // 检查是否正确响应 if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { print("statusCode should be 200, but is \(httpStatus.statusCode)") print("response = \(NSString(data: data!, encoding: NSUTF8StringEncoding)))") } // 解析响应 self.parseRequestResponseData(data!) } task.resume()}

解析响应

解析请求响应

1234567891011121314151617181920212223242526272829
private func parseRequestResponseData(data:NSData) {
do {
let xmlDoc = try AEXMLDocument(xmlData: data) if let response = xmlDoc.root["s:Body"].first?.children.first {
switch response.name {
case "u:SetAVTransportURIResponse": print("设置URI成功") //获取播放长度 case "u:GetPositionInfoResponse": // 进度需要进一步解析。如realTime = response["RelTime"].value print("已获取播放进度") case "u:PlayResponse": print("已播放") case "u:PauseResponse": print("已暂停") case "u:StopResponse": print("已停止") default : print("未定义响应 - \(xmlDoc.xmlString)") } } else {
print("返回不符合规范 - XML:\(xmlDoc.xmlString)") } } catch {
return }}

转载地址:http://rwvli.baihongyu.com/

你可能感兴趣的文章
升级ADT 22 引发的错误
查看>>
ProGuard
查看>>
14 lessons after five years of professional programming
查看>>
声明、请求和检查许可
查看>>
Android代码调试工具 traceview 和 dmtracedump的波折演绎
查看>>
为Android应用程序读取/dev下设备而提权(一)
查看>>
为Android应用程序读取/dev下设备而提权(二)
查看>>
Java 理论与实践: 线程池与工作队列
查看>>
Java多线程1-安全性、互斥与同步
查看>>
Java多线程2-线程协作、Timer和TimerTask
查看>>
Java多线程3-线程池、Callable和Future
查看>>
Java多线程4- Lock、Condition
查看>>
Android bootchart使用步骤
查看>>
Android不同层次开启硬件加速的方式
查看>>
Android 4.1性能分析的一般步骤
查看>>
Analyzing Display and Performance with Systrace
查看>>
Profiling with Traceview and dmtracedump
查看>>
SVN Eclipse插件Subclipse安装和配置
查看>>
SQLIteDatabase.query method
查看>>
ACM题目推荐
查看>>