基于DLNA的移动端网络视频投屏技术初探

我们有一个QQ群 341872661,以及我的个人wx: borishaka,可以拉进微信群讨论相关DLNA难点技术。

个人开源的基本DLNA控制库:DLNAKit

本文从DLNA核心协议层进行原理解释,因此具有平台通用性。(适用iOS/Android/Web等)

DLNADigital 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请求,成功后会返回如下响应消息:

<root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:dlna="urn:schemas-dlna-org:device-1-0" configId="499354">
  <specVersion>
    <major>1</major>
    <minor>1</minor>
  </specVersion>
  <device>
    <deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>
    <friendlyName>卧室的创维盒子Q+</friendlyName>
    <manufacturer>Plutinosoft LLC</manufacturer>
    <manufacturerURL>http://www.plutinosoft.com</manufacturerURL>
    <modelDescription>Plutinosoft AV Media Renderer Device</modelDescription>
    <modelName>AV Renderer Device</modelName>
    <modelURL>http://www.plutinosoft.com/platinum</modelURL>
    <UDN>uuid:9c443d47158b-dmr</UDN>
    <dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-1.50</dlna:X_DLNADOC>
    <serviceList>
      <service>
        <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>
        <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
        <SCPDURL>/AVTransport/9c443d47158b-dmr/scpd.xml</SCPDURL>
        <controlURL>/AVTransport/9c443d47158b-dmr/control.xml</controlURL>
        <eventSubURL>/AVTransport/9c443d47158b-dmr/event.xml</eventSubURL>
      </service>
      ...
    </serviceList>
  </device>
</root>

这个格式很清晰地描述了设备的详细信息,我们可以知道,此设备的自定义名字是卧室的创维盒子Q+、设备类型是媒体渲染播放器、制造商相关信息、UUID、以及它提供的服务列表;每个服务都有serviceType, serviceId, SCPDURL, controlURLeventSubURL。这几个字段都是很关键的信息,比如在以上设备服务列表的第一个服务中,可以看到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.
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <actionList>
    <action>
      <name>SetAVTransportURI</name>
      <argumentList>
        <argument>
          <name>InstanceID</name>
          <direction>in</direction>
          <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
        </argument>
        <argument>
          <name>CurrentURI</name>
          <direction>in</direction>
          <relatedStateVariable>AVTransportURI</relatedStateVariable>
        </argument>
        <argument>
          <name>CurrentURIMetaData</name>
          <direction>in</direction>
          <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
        </argument>
      </argumentList>
    </action>
    ...
  <serviceStateTable>
    <stateVariable sendEvents="no">
      <name>AVTransportURI</name>
      <dataType>string</dataType>
    </stateVariable>
    ...
  </serviceStateTable>
</scpd>

这里做了省略,只展示了该服务的部分内容。可以看出该服务提供了一个actionList动作列表,一个服务会包含一个或多个功能请求动作。如actionList下这个SetAVTransportURI,顾名思义,这个请求的功能是将一个音视频资源的URI发送给渲染端。一个动作(Action)就好比一个API请求,你还需要传递一些要求的参数,这时就会用到该Action后面argumentList里规定的一些Argument。比如根据第一个参数<name>CurrentURI</name>,表示的就是你想发送的URI;同时<direction>in</direction>表示的是这是一个传入参数,如果为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"
<?xml version="1.0"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
      <InstanceID>0</InstanceID>
      <CurrentURI>yourAVURI</CurrentURI>
    </u:SetAVTransportURI>
  </s:Body>
</s:Envelope>

<Host>IPSSDP发现设备得到的IP
<SOAPAction>内容是SDDserviceTypeAction名字拼接而成;
<u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">也是由serviceTypeAction名字组合而成;

<InstanceID>0</InstanceID>
<CurrentURI>yourAVURI</CurrentURI>

是由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
<?xml version="1.0"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
      <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
        <u:SetAVTransportURIResponse>
          <_xmlns:u>"urn:schemas-upnp-org:service:AVTransport:1"</_xmlns:u>
        </u:SetAVTransportURIResponse>
      </u:SetAVTransportURI>
    </s:Body>
  </s:Envelope>

响应信息一目了然,不必多说。

至此,我们就可以初步实现从手机端入网、发现机顶盒设备、获取设备详细信息、获取设备服务、传输URI给播放设备的一个简单通路。

使用DLNA进行智能电视或普通电视连接智能机顶盒设备投射的开发过程颇为曲折,有大量的坑点,都是泪,等有时间再写一篇文章总结一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容