我们有一个QQ群 341872661,以及我的个人wx: borishaka,可以拉进微信群讨论相关DLNA难点技术。
个人开源的基本DLNA控制库:DLNAKit
本文从DLNA
核心协议层进行原理解释,因此具有平台通用性。(适用iOS/Android/Web
等)
DLNA
是Digital Living Network Alliance
的缩写。从英文全称可以看出,这个东西不是一种协议,而是一个实现数字生活网络解决方案做了一个标准的联盟。目前主流的智能网络设备通信解决方案主要是DLNA
Airplay
和新出的Miracast
,最早索尼、微软、三星、苹果等大牛都加入了DLNA
。到后来苹果退出自立门户做了Airplay
,所以我想Airplay
多少是基于DLNA
标准的吧。
DLNA
不是一种协议,但包括了实现相关标准所需要的一系列协议栈。如HTTP
SSDP
XML
SOAP
UPnP
UDP
TCP/IP
等等。DLNA
不仅可用于WIFI
,还支持Bluetooth
等多种通信协议。这里只讨论在局域网LAN
下使用WIFI
连接的智能设备与客户端之间的协议编程。
首先简单介绍一下客户端和智能设备在DLNA
标准定义下的角色。当手机端仅实现将网络视频投射至电视时,手机端扮演的是控制点Control Point
,而电视或者机顶盒则扮演的是渲染设备
或播放设备
。
要实现从移动端将网络视频投放至智能电视或机顶盒,首先要保证这些设备在同一个局域网的相同网段下,即共享同一个网关Internet Gateway
。这样所有设备都能够拥有独立的IP
,从而具备相互通信的基础了。
试想这样一个场景:所有设备都比作人,整个局域网就好比是一个政府大院。当有人要进入政府大院时,警卫会先让这个新人登记,把他的一些基本信息,如姓名,手机号等记录下来。同时,这个新人可能想知道政府大院里都有什么人,每个人的姓名和联系方式是什么。在这里假设警卫和这个新人都拥有足够的权限告知和获取,那么警卫可能会打印一份名单交给这个新人。名单上列有今天已经进入政府大院的人的基本信息;当有人从大院里出来,这个人会告诉警卫自己的姓名,然后警卫会把他的条目从名单里划掉。
发现设备
这个简单的场景就是支持DLNA
的智能设备加入组网并进行搜索其他设备时的基本流程了。这个过程称为设备发现
。当一个新的CP (Control Point)
加入一个局域网时,为了获取当前网段里都有哪些智能设备,CP
需要遵循SSDP
向默认多播IP和端口发送获取信息的请求。对于CP
,可以使用DLNA
定义的搜索-响应方式
来发现设备,这会用到http
的扩展协议M-SEARCH
。
搜索请求消息的格式如下:
M-SEARCH * HTTP/1.1
MX: 1 //最大时间间隔数
ST: upnp:rootdevice //搜索的设备类型
MAN: "ssdp:discover"
User-Agent: iOS 10.2.1 product/version
Connection: close
Host: 239.255.255.250 //多播地址
这个消息是一个由UDP
端口发送的请求,如果请求成功,则会收到UDP
端口返回的响应消息:
HTTP/1.1 200 OK
Cache-control: max-age=1800
Date: Thu, 16 Feb 2017 09:09:45 GMT
EXT:
LOCATION: http://10.2.9.152:49152/TxMediaRenderer_desc.xml //URL for UPnP description for device
Server: search target
USN: uuid:3c970e3c0c0d0000_MR::upnp:rootdevice //composite identifier for the advertisment
BOOTID.UPNP.ORG: 1487062102 //number increased each time device sends an initial announce or an update message
CONFIGID.UPNP.ORG: 499354 //number used for caching description information
SEARCHPORT.UPNP.ORG: number identifies port on which device responds to unicast M-SEARCH
ST: upnp:rootdevice //device type
上面的响应消息即DLNA
的控制点设备在做发现时得到的某个设备返回的消息。由于这个过程的消息通信是基于UDP
这种不可靠,无连接的协议,所以建议在设备搜索过程多做几次发现请求,以免丢包带来的遗漏。
现在控制点即你的手机端可以获得的信息是:有一台设备,它的IP和端口号、设备描述文档
在什么位置(LOCATION
)、UUID
是什么(USN
)。但它能提供什么服务,是否允许手机端将网络视频投放至它的渲染设备,支不支持移动端进行播放控制等等,我们都还不清楚。因此这一步仅仅是发现了设备,下一步我们需要获取更详细的设备信息,即请求该设备的设备描述文档 (DDD, Device Description Document)
。
请求DDD
DDD
以及后面要说到的服务描述文档 (SDD, Service Description Document)
都是以XML
格式返回给请求端的,这一步的通信则是基于TCP
HTTP
进行可靠传输的。我们向Location
字段的内容即:
http://10.2.9.152:49152/TxMediaRenderer_desc.xml
发出一个GET
请求,成功后会返回如下响应消息:
1
1
urn:schemas-upnp-org:device:MediaRenderer:1
卧室的创维盒子Q+
Plutinosoft LLC
http://www.plutinosoft.com
Plutinosoft AV Media Renderer Device
AV Renderer Device
http://www.plutinosoft.com/platinum
uuid:9c443d47158b-dmr
DMR-1.50
urn:schemas-upnp-org:service:AVTransport:1
urn:upnp-org:serviceId:AVTransport
/AVTransport/9c443d47158b-dmr/scpd.xml
/AVTransport/9c443d47158b-dmr/control.xml
/AVTransport/9c443d47158b-dmr/event.xml
...
这个格式很清晰地描述了设备的详细信息,我们可以知道,此设备的自定义名字是卧室的创维盒子Q+
、设备类型是媒体渲染播放器、制造商相关信息、UUID
、以及它提供的服务列表;每个服务都有serviceType
, serviceId
, SCPDURL
, controlURL
和eventSubURL
。这几个字段都是很关键的信息,比如在以上设备服务列表的第一个服务中,可以看到serviceType
字段内容是urn:schemas-upnp-org:service:AVTransport:1
,这表示这个服务提供的是音视频传输服务,版本号是1。
请求SDD
那么我们如何使用这个服务呢,使用这个服务需要控制端提供什么参数呢。想要得到这些信息,则需要去参考该服务的SDD
了。注意到SCPDURL
这个字段的内容就是请求SDD
的路径地址,我们将其与之前在SSDP
发现设备阶段获取到的响应消息中的Location
字段内容中设备的IP
和端口号拿过来,拼接成完整URL
字符串:
http://10.2.9.152:49152/AVTransport/9c443d47158b-dmr/scpd.xml
做一个GET
请求,则可以获取该服务的描述文档SDD
了:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
1
0
SetAVTransportURI
InstanceID
in
A_ARG_TYPE_InstanceID
CurrentURI
in
AVTransportURI
CurrentURIMetaData
in
AVTransportURIMetaData
...
AVTransportURI
string
...
这里做了省略,只展示了该服务的部分内容。可以看出该服务提供了一个actionList
即动作
列表,一个服务会包含一个或多个功能请求动作
。如actionList
下这个SetAVTransportURI
,顾名思义,这个请求的功能是将一个音视频资源的URI
发送给渲染端。一个动作(Action)
就好比一个API
请求,你还需要传递一些要求的参数,这时就会用到该Action
后面argumentList
里规定的一些Argument
。比如根据第一个参数
,表示的就是你想发送的URI
;同时
表示的是这是一个传入参数,如果为out
则表示该Action
会返回给你这个参数的值。
服务动作请求
那么我们现在有了发送网络视频URI
所需要的全部信息,这时就可以按照DLNA
规定的方式发给设备请求服务了。DLNA
规定请求Action
消息格式如下:
POST /AVTransport/9c443d47158b-dmr/control.xml HTTP/1.1
HOST: 10.2.9.152
Content-Type: text/xml; charset="utf-8"
SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"
0
yourAVURI
的IP
是SSDP
发现设备得到的IP
;
内容是SDD
中serviceType
与Action
名字拼接而成;
也是由serviceType
和Action
名字组合而成;
0
yourAVURI
是由actionList
中的参数组合而成。
如果消息发送没问题,设备会发送请求成功的响应消息:
HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Date: Thu, 16 Feb 2017 09:09:45 GMT
Server: OS/version UPnP/1.1 product/version
<_xmlns:u>"urn:schemas-upnp-org:service:AVTransport:1"
响应信息一目了然,不必多说。
至此,我们就可以初步实现从手机端入网、发现机顶盒设备、获取设备详细信息、获取设备服务、传输URI给播放设备的一个简单通路。
使用DLNA
进行智能电视或普通电视连接智能机顶盒设备投射的开发过程颇为曲折,有大量的坑点,都是泪,等有时间再写一篇文章总结一下。