DLNA投屏

Change Log

2019-12.17: 初稿

目录

一.目前投屏的方式
二.UPnP简单流程
三.UPnP详细流程
3.1).设备寻址
3.2).设备发现
3.3).设备描述
3.4).设备控制
3.5).设备事件
3.6).设备表示
四.设备发现过程简介
五.SSDP协议消息
六.DLNA & UPnP
七.DLNA & UPnP协议关系
八.名词解释
九.HTTP协议基础扫盲
十.UPnP应用
十一.运行Platinum库
十二.抓包分析
十三.OC工程源码接入Platinum
十四.源码接入Platinum遇到的问题
十五.参考文档
十六.结语

一.目前投屏的方式

注: 使用所有投屏的先决条件.投屏设备(手机)和接受设备(盒子)必须在同一个局域网下.

方式一: 苹果私有协议投屏 Airplay Mirror/Cast

由苹果公司定制的DMC/DMR/DMS之间的通讯协议

Airplay Cast打开方式:
需要你的APP实现苹果的方法.
AVRoutePickerView: 显示周围的AirPlay设备.并且控制连接.

Airplay Mirror打开方式:

控制中心.png

投屏选择面板.png

如果控制中心没有Airplay Mirror选项.可以在”设置->控制中心->自定控制”中打开.

方式二: DLNA通用协议投屏

由索尼、英特尔、微软等定制的DMC/DMR/DMS之间的通讯协议
可以使用三方库Platinum接入

方式三: 私有协议投屏

自己定制的DMC/DMR/DMS之间的通讯协议
需要全部自己定义和写.


二.UPnP简单流程

UPnP简单流程.png

1、 首先控制点(DMC)和设备(DMR)都先获取IP地址并且在同一局域网后才能进行下一步的工作;
2、 控制点(DMC)首先要寻找整个网络上的UPnP设备(DMR),同时网络上的设备(DMR)也要宣告自身的存在;
3、 控制点(DMC)要取得设备(DMR)的描述,包括这些设备(DMR)提供什么样的服务;
4、 控制点(DMC)发出动作信息给设备;
5、 控制点(DMC)监听设备(DMR)的状态,当状态改变时作出相应的处理动作;


三.UPnP详细流程

image.png
image.png
image.gif
image.gif

投屏的过程主要分为 设备发现设备控制 两个阶段

3.1).设备寻址

地址是整个UPnP系统工作的基础条件,UPnP网络的基础就是TCP/IP协议族,UPnP设备能在TCP/IP协议下工作的关键就是正确的设备寻址。

设备(DMR)寻址的一般过程是:

首先向 DHCP服务器发送DHCPDISCOVER消息,如果在指定的时间内,设备没有收到DHCPOFFERS回应消息,设备必须使用 Auto-IP完成IP地址的设置。

如果是使用Auto-IP完成的寻址.那么此后还必须要定时检测DHCP服务器是否存在.如果存在就释放Auto-IP的地址.然后使用DHCP重新寻址.

这一步完成后.我们的设备(DMR)就已经连接到局域网上并且分配了地址了.

设备加入网络后,它将向组播发送类似如下的消息:

NOTIFY * HTTP/1.1\r\n
HOST: 239.255.255.250:1900\r\n  // 这里必须使用IANA(InternetAssigned Numbers Authority)为SSDP预留的组播地址:239.255.255.250:1900。
Cache-control:max-age=1800  // max-age的数值表明设备将在这段时间(单位为秒)后失效,因此,设备应当在失效前,重发这样的消息,标准指出,这里的数值应当不小于1800秒,但实际上,这里的取值范围取决于UPnP的实现。
LOCATION: http://143.162.60.25:49152/description.xml\r\n  // 设备描述文件(DDD)的URL。
NT: upnp:rootdevice\r\n  // Notification Type,这里的值upnp:rootdevice)表明这是一个“根设备”。每个设备可以有自己的子设备。
NTS: ssdp:alive\r\n   // Notification Sub Type,标准规定必须是ssdp:alive。
USN: uuid:F7CA5454-3F48-4390-8009-203de144c5aa::upnp:rootdevice\r\n // Unique Service Name,是一个设备实例的标识符。
SERVER: Linux/3.14.29, UPnP/1.0, Portable SDK for UPnP devices/1.6.13\r\n
image.png

3.2).设备发现

同样的控制点(DMC)也需要加入与设备(DMR)相同的局域网.

加入局域网后控制点(DMC)也需要发出一个组播消息.用来发现设备:

M-SEARCH * HTTP/1.1\r\n
HOST: 239.255.255.250:1900\r\n 
MAN: "ssdp:discover"\r\n  // 必须是“ssdp:discover”。
MX: 15\r\n  // 1到15之间的一个值,表示最大的等待应答的秒数。
ST: upnp:rootdevice\r\n  // Seatch Targer,表示搜索的节点类型。
Connection: close\r\n
User-Agent: UPnP/1.0 LEBODLNA/lebodlna/NewDLNA/1.0\r\n
image.png

这一步完成后你就可以发现与你的控制点(DMC)在同一局域网的所有设备(DMR)了.

image.jpeg

当控制点(DMC)发出搜索消息时.同一局域网的设备(DMR)就会产生如下应答

HTTP/1.1 200 OK\r\n
Cache-Control: max-age=600\r\n
Date: Mon, 16 Dec 2019 11:44:00 GMT\r\n
Ext: \r\n
Location: http://192.168.50.1:1990/WFADevice.xml\r\n
Server: POSIX UPnP/1.0 UPnP Stack/7.14.124.5204\r\n
ST: upnp:rootdevice\r\n
USN: uuid:d8f578c7-aa7b-9c7a-b0b3-11cde09379cf::upnp:rootdevice\r\n

控制点(DMC)可以通过设备(DMR)的应答获取到设备(DMR)的基本信息.如Usn/Location等...

image.png

详情见设备发现过程简介

3.3).设备描述

至此,我们已经得到了一个重要的信息:Location,向Location地址发送一个简单的HTTP请求,就可以得到该设备(DMR)的详细信息了(DDD).如下:

对于一个设备的UPnP描述一般分成两个部分:设备描述文档(DDD)和服务描述文档(SDD)。都是油XML形式输出.

<?xmlversionxmlversion="1.0" encoding="utf-8"?>  
<rootxmlnsrootxmlns="urn:schemas-upnp-org:device-1-0">  
  <specVersion>  
    <major>1</major>  
    <minor>1</minor>  
   </specVersion>  
   <device> 
   <deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType>  // 设备类型,格式为:“urn:schemas-upnp-org:device:deviceType:v”,这里deviceType和v是由设备定义的。
   <friendlyName>KitchenLights</friendlyName>  // 一个更加友好的设备名。即给人看的名字.
   <manufacturer>OpenedHand</manufacturer>  // 制造商。
   <modelName>VirtualLight</modelName>  // 型号。
   <UDN>uuid:cc93d8e6-6b8b-4f60-87ca-228c36b5b0e8</UDN>  // Unique Device Name,设备的UUID。根据UUID生成的时间无关的设备失败码,其中包含了UUID,我们可以以此为设备id区分不同设备、处理设备的掉线和重连等。
   <serviceList>  // 服务列表。
       <service>  
       <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>  // 判断设备提供的服务类型的依据,对于支持投屏的设备,一般有AVTransport、RenderingControl、ConnectionManager三种服务,投屏过程中主要使用前两种
       <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>  // 服务ID,通常于serviceType对应。
       <SCPDURL>/dlna/Render/AVTransport_scpd.xml</SCPDURL>  // 每个服务的支持的控制指令可以通过SCPDURL查看。SDD
       <controlURL>_urn:schemas-upnp-org:service:AVTransport_control</controlURL>  // 用于控制的URL。controlURL是服务的控制地址,指令的发送就是往这个地址发的;
       <eventSubURL>_urn:schemas-upnp-org:service:AVTransport_event</eventSubURL>  // 用于订阅事件的URL。用来向目标设备订阅该服务相关的事件回调的,需要控制端运行一个ServerSocket监听tcp请求。
      </service>  
   </serviceList>  
  </device>  
</root>

此时你就可以从上面的详细信息(DDD)里的SCPDURL字段获取设备的服务描述文档(SDD)了.如下:

<?xml version="1.0" encoding="utf-8"?>  
<scpd xmlns="urn:schemas-upnp-org:service-1-0">  
  <specVersion>  
   <major>1</major>  
   <minor>1</minor>  
  </specVersion>  
  <actionList>  // 定义了“行为”
    <action>  
      <name>SetTarget</name>  // 名称
      <argumentList>  
        <argument>  // 参数
          <name>NewTargetValue</name>  // 名字
          <relatedStateVariable>Target</relatedStateVariable>  // 关联的状态变数,标明UPnP设备或程序的一些状态。一个程序可以订阅(subscribe)状态的变化,从而得到通知。而一个参数之所以必须关联一个状态变数,是因为状态变数的类型决定了参数的类型。
          <direction>in</direction>  // 传递方向(direction,取值in或者out)
        </argument>  
      </argumentList>  
    </action>  
    <action>  
     <name>GetTarget</name>  
      <argumentList>  
        <argument>  
         <name>RetTargetValue</name>  
         <relatedStateVariable>Target</relatedStateVariable>  
         <direction>out</direction>  
        </argument>  
      </argumentList>  
    </action>  
    <action>  
     <name>GetStatus</name>  
      <argumentList>  
        <argument>  
         <name>ResultStatus</name>  
         <relatedStateVariable>Status</relatedStateVariable>  
         <direction>out</direction>  
        </argument>  
      </argumentList>  
    </action>  
  </actionList>  
  <serviceStateTable>  // 定义了“状态变数”
     <stateVariablesendEventsstateVariablesendEvents="no">  
     <name>Target</name>  
     <dataType>boolean</dataType>  
     <defaultValue>0</defaultValue>  
     </stateVariable>  
       <stateVariablesendEventsstateVariablesendEvents="yes">  
       <name>Status</name>  
       <dataType>boolean</dataType>  
       <defaultValue>0</defaultValue>  
     </stateVariable>  
  </serviceStateTable>  
</scpd>

3.4).设备控制

在接收到DDD和SDD之后,会从描述中“提炼”出要进行的操作并获悉所有的服务.

要控制某个设备(DMR),控制点(DMC)必须先发送一个控制行为请求,要求设备(DMR)开始服务

一般是向设备(DMR)的控制URL地址发送一个适当的控制请求.

最后,服务会返回响应信息,指出服务是成功或是失败。

例子:

比如往设备(DMR)对应服务的controlURL发送HTTP请求,这里以POST方式为例,向TV的AVTransport服务发送SetAVTransportURI指令,作用是告诉TV需要播放的直播流的地址.

image.png

控制点(MDC)发出的请求如下:

POST /_urn:schemas-upnp-org:service:AVTransport_control HTTP/1.1\r\n
Host: 143.162.60.25:39620\r\n
Content-Type: text/xml; charset="utf-8"\r\n
Connection: keep-alive\r\n
SOAPAction: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"\r\n
User-Agent: UPnP/1.0 LEBODLNA/lebolna/NewDLNA/1.0\r\n
Content-Length: 1348\r\n
<s:Envelope
    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/“
    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/“
    xmlns:u="urn:schemas-upnp-org:service:AVTransport:1”>
    <s:Body>
        <u:SetAVTransportURI>
            <InstanceID>
                0
                </InstanceID>
            <CurrentURI>
                 [truncated]http://upos-hz-mirrorkodou.acgvideo.com/upgcxcode/37/82/135668237/135668237-1-208.mp4?e=ig
                </CurrentURI>
            <CurrentURIMetaData>
                 [truncated]&lt;DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"&gt;&lt;item id="0" parentID="-1" restricted="1"&gt;&lt;upnp:ge
                </CurrentURIMetaData>
            </u:SetAVTransportURI>
        </s:Body>
    </s:Envelope>
image.png

设备(DMR)返回的结果如下:

HTTP/1.1 200 OK\r\n
Content-Type: text/xml; charset="utf-8"\r\n
Server: Linux/3.14.29 UPnP/1.0 IQIYIDLNA/1.0\r\n
Content-Length: 305\r\n
Date: Mon, 16 Dec 2019 19:43:55 GMT\r\n
Server: Linux/3.14.29 UPnP/1.0 IQIYIDLNA/1.0\r\n
[Request URI: http://143.162.60.25:39620/_urn:schemas-upnp-org:service:AVTransport_control]
<?xml
    version=“1.0"
    encoding="utf-8”
?>
<s:Envelope
    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/“
    s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/“>
    <s:Body>
        <u:SetAVTransportURIResponse
            xmlns:u="urn:schemas-upnp-org:service:AVTransport:1”>
            </u:SetAVTransportURIResponse>
        </s:Body>
    </s:Envelope>
image.png

到这一步.我们就已经可以通过手机控制盒子了.

可以向盒子发送命令.

3.5).设备事件

一个服务的UPnP描述包括服务响应的动作列表和运行时模拟服务状态的变量列表。当这些变量改变时,服务就会发布更新,则控制点就会收到设备事件。

当控制端通过SetAVTransportURI、Play让目标设备开始播放视频时,设备会进行加载缓冲,并开始播放,或者用户通过遥控器暂停/继续播放,甚至其它控制端抢占了TV等,都是我们需要关心的事件,以便控制端进行状态处理。获取目标设备的状态变化有两种方式,

方式一: 轮询GetTransportInfo、GetMediaInfo等Action,
方式二: 向目标设备注册订阅

两种方式各有优缺点,因为目标设备实现存在差异,每个设备的在状态的处理上不完全一致。前面已经提到过,GetTransportInfo返回结果不太靠谱;第二种方式,结果较为准确,但是对控制端抢占TV导致更换URL等情况大多不会告知订阅者;所以结合两种方式,可以达到较好的效果。

方式一:轮询就是不断发送获取想要状态信息的请求就行了.
方式二: 事件订阅
image.png
订阅:

设备描述的服务列表中,每个服务都有一个eventSubURL,我们可以在控制端运行一个ServerSocket绑定一个端口(记为端口A),通过accept监听tcp请求,并将本机ip和端口A和自定义回调路径拼接为url通过SUBSCRIBE Action发送给目标设备,即可完成订阅,在必要的时候,通过SUBSCRIBE Action(与订阅使用同一个Action,但参数不同)续订,通过UNSUBSCRIBE Action取消订阅。

为了订阅事件,订阅者发送一个订阅消息。如果出版者收到此消息,它将以这个订阅的持续时间作为响应。为了保持订阅,订阅者必须在订阅到期之前进行续订。在订阅者不需要出版者发送的事件时,订阅者必须取消这个订阅。出版者通过发送事件消息提醒订阅者状态变量改变。事件消息包含多个状态变量的名字和这些变量的当前值。在订阅者第一次订阅时,需要发送初始化事件消息,这个事件包含所有事件变量的名和值并且允许订阅者出示化服务变量值。为了支持多个控制点,在动作生效之后所有订阅者都将接到通知。事件消息使用HTTP协议传送,事件详细定义在通用事件通知结构(General Event Notification Architecture)协议中。

订阅发出的请求:

SUBSCRIBE publisher pathHTTP/1.1  
HOST: publisherhost:publisher port  
USER-AGENT: OS/versionUPnP/1.1 product/version  
CALLBACK: <deliveryURL>  // 回调的URL
NT: upnp:event  // 订阅事件

订阅事件的应答:

HTTP/1.1 200 OK  
DATE: when response was generated  
SERVER: OS/version UPnP/1.1 product/version  
SID: uuid:subscription-UUID  // 本订阅的标识符,通常使用UUID
CONTENT-LENGTH: 0  
TIMEOUT: Second-1800  // 这里的值表示本订阅的有效期。这意味着在超时前,必须续订。

续订的请求:

SUBSCRIBE publisher path HTTP/1.1  
HOST: publisher host:publisher port  
SID: uuid:subscription UUID  

退订的请求:

UNSUBSCRIBE publisher path HTTP/1.1  
HOST: publisher host:publisher port  
SID: uuid:subscription UUID

服务更新时发出的事件消息:

NOTIFY delivery path HTTP/1.1  
HOST: delivery host:delivery port  
CONTENT-TYPE: text/xml; charset="utf-8"  
NT: upnp:event  
NTS: upnp:propchange  
SID: uuid:subscription-UUID  
SEQ: event key  // 消息的顺序号,从0开始
CONTENT-LENGTH: bytes in body  
<?xml version="1.0"?>  
<e:propertysetxmlns:ee:propertysetxmlns:e="urn:schemas-upnp-org:event-1-0">  
    <e:property>  // 每个产生事件的状态变数对应一个e:property标识
       <variableName>new value</variableName>  // 变数名
    </e:property>  
</e:propertyset>  

当订阅者收到消息之后,必须在30秒内发送确认。

如果订阅者没有确认,设备仍然会发送之后的消息,直到本次订阅超时。

3.6).设备表示

ServerSocket接收Socket链接,并读取回调内容,后面过程与处理Action回包类似,解析upnp内容即可(upnp是XML子集),其中需要关注的只有lastchange标签里的内容,以播放事件为例,播放事件的LastChange标签内容为:

设备表征是UPnP设备的最后一步。如果设备有表征的URL,那么控制点就能通过URL得到页面,在浏览器中装载页面,并使得用户根据页面提供的功能控制设备或者浏览设备状态。它具体能完成到什么与设备和表征页面的功能有关。

设备表征包含在设备描述的presentationURL字段。设备表征可以完全由设备制造商提供,它采用HTML页的形式,使用HTTP进行发布。


四.设备发现过程简介

UPnP使用简单服务发现协议SSDP(Simple Service Discovery Protocol)来完成发现,这是一个工作在UDP上的HTTP协议。

此协议为网络客户提供一种无需任何配置、管理和维护网络上设备服务的机制。此协议采用基于通知和发现路由的多播发现方式实现。协议客户端在保留的多播地址239.255.255.250发现服务,同时每个设备服务也在此地址上监听服务发现请求。如果服务监听到的发现请求与此服务相匹配,此服务会使用单播方式响应。每个服务也可以向多播端口发送通知声明服务存在。

UPnP主动发现和被动发现

image.jpeg
主动发现: (在当前的网络中查找有无对应的可用设备)

主动发现是指设备主动通过UDP发出Search组播到指定地址和端口(ipv4为239.255.255.250:1900,ipv6为[FF0x::C]:1900)

目标设备收到组播后会通过UDP单播发送设备基本信息(所以终端需要用Socket绑定search发送的那个随机端口,receive单播回包)

然后根据基本信息中的设备描述地址获取设备的详细信息。

被动发现: (设备(DMR)自己向网络”广播"自己已经进入网络)

被动发现是指某一设备(DMR)接入网络.取得IP地址之后.就开始向网络“广播”自己已经进入网络,即寻找控制请求。

即通过UDP发送Notify组播到局域网(所以终端需要启动一个MulticastSocket joinGroup到上述组播地址监听组播),控制点(DMC)收到组播后可以通过主动发现获取设备(DMR)的详细信息。

常见的协议请求消息有两种类型,第一种是服务通知,第二种是查询请求

服务通知

设备和服务使用此类通知消息声明自己存在

查询请求

协议客户端用此请求查询某种类型的设备和服务。请求消息中包含设备的特定信息或者某项服务的信息,例如设备类型、标识符和指向设备描述文档的URL地址。

image.gif

设备发现过程允许控制点使用一个设备类型或标识,或者是服务类型进行查询。这要求标准设备或服务类型,或者设备特定实例的发现和广告消息基于一个独一无二的标识,UPnP设备和服务类型的定义是UPnP论坛工作委员会的责任。从设备获得响应的内容基本上与多址传送的设备广播相同,只是采用单址传送方式。


五.SSDP协议消息

设备通知消息

在设备(DMR)加入网络,UPnP发现协议允许设备(DMR)向控制点(DMC)广告它的服务。它使用向一个标准地址和端口多址传送发现消息来实现。控制点(DMC)在此端口上侦听是否有新服务加入系统。为了通知所有设备,一个设备为每个其上的嵌入设备(MDR)和服务发送一系列相应的发现消息。每个消息也包含它表征设备或服务的特定信息。


六.DLNA & UPnP

DLNA & UPnP协议典型应用

通过DLNA协议,你可以方便地把手机里播放的视频投射到家里的电脑上或者智能电视上。

如果你购买了某些电视盒子,就可以在盒子中安装类似PPTV/搜狐TV这样的视频软件,然后在手机播放视频的界面中,选择投射到电视或者盒子上即可。

七.DLNA & UPnP协议关系

DLNA协议是建立在UPnP协议的基础上的,它依靠UPnP协议来完成设备的搜索、发现和控制,DLNA协议在此基础上对多媒体设备进行了更加详细的分类和约束,它其实更像是UPnP协议的一个子集。相比于UPnP协议,它更加专注于解决多媒体内容的共享的架构。

在实际的开发中,UPnP协议提供了丰富的SDK,有着各种开源版本,我们只需要在UPnP官网提供的SDK的基础上,根据DLNA协议或者UPnP-AVArchitecture的定义实现相关的功能/服务即可。


八.名词解释

DLNA : (终端互联解决方案)

Digital Living Network Alliance (数字生活网络联盟)

简单来说,DLNA协议定义了一种通用的行业规范,目的是让电子设备之间可以方便地共享照片、视频、音乐等多媒体内容。

其宗旨是Enjoy your music, photos and videos, anywhere anytime

旨在解决个人电脑,移动设备在内的无线网络和有线网络的互联互通.

DLNA并不是创造技术,而是形成一种解决的方案,一种大家可以遵守的规范。所以,其选择的各种技术和协议都是当前所应用很广泛的技术和协议。

DLNA将其整个应用规定成5个功能组件。从下到上依次为:网络互连,网络协议,媒体传输,设备的发现控制和管理,媒体格式。

DLNA标准包括多项协议及标准,其中最重要的部分是UPnP。对于我们目前的需求UPnP就能满足全部要求。

DLNA协议栈为设备之间信息交流提供了一种彼此听得懂的语言工具。

UPnP:

Universal Plug and Play通用即插即用

而UPnP协议主要用于实现智能设备的互联互通,它定义了局域网内的设备如何互相发现对方,如何提供服务,如何控制对方等一系列的过程。

是由“通用即插即用论坛”(UPnP™ Forum)推广的一套网络协议。

协议的目标是使家庭网络(数据共享、通信和娱乐)和公司网络中的各种设备能够相互无缝连接,并简化相关网络的实现。

UPnP通过定义和发布基于开放、因特网通讯网协议标准的UPnP设备控制协议来实现这一目标。

UPnP体系允许PC间的点对点连接、网际互连和无线设备。它是一种基于TCP/IP、UDP和HTTP的分布式、开放体系。使它能够无缝的融入现有网络。

UPnP的基础是IP地址解析。每一个设备都应当有一个DHCP客户端并在连入网络的时候自动搜索DHCP服务。如果没有找到DHCP服务,也就是说网络是缺乏管理状态,那么设备必须给自己设定一个地址。如果在和DHCP服务器交互的过程中,设备获得了一个域名(比如通过DNS服务器或者DNS传递),那么它应当在接下来的网络操作中使用这个域名;否则,设备应当使用它的IP地址。

UPnP 技术实现了 控制点(DMC)、 设备(DMR)和 服务(DMS)之间通讯的支持,并且设备和相关服务的也使用XML定义并且公布出来。使用UPnP,设备可以动态加入网络,自动获得一个IP地址,向其他设备公布它的能力或者获知其他设备的存在和服务,所有这些过程都是自动完成的,此后设备能够彼此直接通讯。

SSDP: 搜索

Simple Sever Discovery Protocol 简易服务发现协议

UPnP中的第三层是HTTP、HTTPU、HTTPMU,这一层,属于传送协议层。传送的是内容都经过“封装”后,存放在特定的XML文件中的。

对应的SSDP、GENA、SOAP指的是保存在XML文件中的数据格式。到这一层,已经解决了UPnP设备的IP地址和传送信息问题。

SSDP能够在局域网能简单地发现设备提供的服务。SSDP有两种发现方式:主动通知和搜索响应方式。

DDD:

Device Description Document

设备描述文档

DDD包括制造商信息,包括模块名称和编号,序列号,制造商名称,制造商网站的URL等等。

也包括所有嵌入设备描述和URL地址集。

对于一个物理设备可以包含多个逻辑设备,多个逻辑设备既可以是一个根设备其中嵌入多个设备,也可以是多个根设备的方式实现。

SDD:

Service Description Document

服务描述文档

包括一系列命令或者动作,服务响应,动作的参数。

也包含一系列变量,这些变量描述了服务运行时刻的状态,这包括数据类型、取值范围和事件特性的描述。

解析SDD获取 <controlURL> 控制点可以知道该设备上某个服务的控制点地址

通过解析 SDD 中 <action> 中的 <name> 和 <argumentList> 获取该服务动作的动作名称,参数要求。

控制点向 controlURL 发出服务调用信息,表明动作名称和相应参数来调用相应的服务。

DMC: 手机

Digital Media Controller数位媒体控制器

作为遥控装置使用,可寻找DMS上的多媒体档案,并指定可播放该多媒体档案的DMR进行播放或是控制多媒体档案上下传到DMS的装置,一般是手机.

控制UPnP设备工作的网络终端,主要功能包括获取设备描述和相关服务列表;获取感兴趣的服务描述;发出控制消息控制设备动作;向感兴趣的服务发出订阅消息,以便当服务状态改变时,自动获得时间通知。

DMS: 服务端

Digital Media Server 数位媒体服务器

提供了媒体档案的获取、录制、储存以及作为源头的装置。一般是公网上流媒体服务器

DMR: 盒子

Digital Media Renderer 数位媒体控制器

可接收并播放从DMC push过来的媒体档案。即接收投屏数据,一般是智能电视, OTT盒子等.

这三者的关系是,DMC通过获取DMS上的歌曲或者视频(也可以不是DMS上的,而仅仅只是一个链接),把它们传送到DMR上,由DMR进行播放。

SOAP: 控制协议

Simple Object Access Protocol 简易对象访问协议

它是一种应用程序之间进行数据通讯的机制。它是一种在HTTP上使用XML发送命令并接收值的远程过程调用。

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 ,回答能不能执行调用,如果出错则返回一个错误代码。

UPC

Universal Product Code

通用产品编码的缩写,它由12个数字构成,由统一编码委员会(Uniform Code Council)管理。这个值可由UPnP制造商指定。

UDN

Unique Device Name

单一设备名基于UUID,每个表示一个设备。在不同的时间,对于同一个设备此值应该是唯一的。

Platinum

a modular UPnP Framework

就是对UPnP网络协议的一层封装库

DHCP

Dynamic Host Configuration Protocol动态主机配置协议

是一个局域网的网络协议。指的是由服务器控制一段lP地址范围,客户机登录服务器时就可以自动获得服务器分配的lP地址和子网掩码。默认情况下,DHCP作为Windows Server的一个服务组件不会被系统自动安装,还需要管理员手动安装并进行必要的配置。

动态主机控制协议,可以访问 RFC 2131获得更详细的信息。

Neptune: 海王星

a C++ Runtime Library


九.HTTP协议基础扫盲

HTTP(Hyper Text Transfer Protocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC 2616。

客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。

服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头域,请求头域,响应头域和实体头域四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。


十.UPnP应用

智能家庭网络

许多智能家居环境使用了现存的家庭控制网络,例如X10网络来控制和监控整个家居环境,比如灯光,安防和其他家庭设备。这些网络可以连接PC上,但是除了PC之外,不能被其他的设备存取。使用UPnP设备可以桥接这些网络成为一个网络,并提供用户更多设备存取家庭网络中的设备。在实现时也无须对X10网络中的现有布线和设备进行昂贵的升级,只需要将设备变成UPnP设备并能够与控制点通讯并接受控制点的控制命令。

数字音频文件管理

可以在PC和其他设备上播放的数字化音频文件在近几年正在成指数级的增长。一个家庭中,可能有几台计算机或者其他设备用于保存这些音频文件。使用UPnP可以使这些分布在不同PC上的音乐库被统一的管理。这些设备能被发现然后被其他控制点(比如个人电脑、UPnP接收器)控制。同时这些控制点也可以控制家庭中的任何一个扬声器。

数字图片库

许多家庭使用数字相机拍照,或者将已有照片扫描保存,然后将这些照片上载到他们的计算机中保存。在计算机中对其进行分类,或者以幻灯片的形式进行显示。随着照片的增加,照片可能保存在多种设备或者多种介质上,比如光盘、硬盘、Flash卡。使用UPnP技术,图片库可以自己作为一个设备存在,并自动在网络上声明。这使得一个照片库可能临时为多个应用程序使用,例如可以进行幻灯片显示的同时,在电子像框、机顶盒和电视上进行显示。


十一.运行Platinum库

Platinum Github地址: https://github.com/plutinosoft/Platinum

运行iOS测试代码:

1.clone工程
git clone https://github.com/plutinosoft/Platinum.git
2.buiding SDK and Sample Applications
2.1.安装carthage

brew install carthage

2.2.Build Neptune & Platinum frameworks
image.png

Both Neptune and Platinum frameworks binaries can be found under Carthage/Build folders which you can link with your applications. Follow the instructions on the carthage page. If you are building for iOS, special instructions here.

If you are interested in building sample apps or tests, you can also open the XCode project file located @ Build/Targets/universal-apple-macosx/Platinum.xcodeproj.


十二.抓包分析

第一步:使用USB数据线将iOS设备连接到MAC上
第二步:获得iOS设备的UDID,可以使用Xcode的Organizer工具查看
image.png
第三步:创建RVI接口

在mac 终端执行命令$ rvictl -s <UDID>

/// 这样会创建一个虚拟网卡rv0
~/Desktop/crash $ rvictl -s 9e82c017xxxxxxxxxxxxxxxxxxx598152ec3
Starting device 9e82c017xxxxxxxxxxxxxxxxxxx598152ec3 [SUCCEEDED] with interface rvi0
/// 用ifconfig rv0 可以看数据
~/Desktop/crash $ ifconfig rvi0
rvi0: flags=3005<UP,DEBUG,LINK0,LINK1> mtu 0
第四步: 用抓包软件针对rv0抓包即可.

这里我使用 wireshark,在新建时,双击rvi0进行抓包

image.png

如下图就是你发出的GetPositionInfo命令

GetPositionInfo命令.png

如下图为接收到设备发来的消息GetPositionInfoResponse

GetPositionInfoResponse.png

搜索方法:

搜索方法.png

十三.OC工程源码接入Platinum

网上有很多将Platinum以静态库的方式接入工程的文章.我以源码方式接入有三点原因:
一: 以静态库的方式接入工程.如果出了问题不好查找根本原因.
二: 接入源码可以二次开发.定制一些功能.
三: 大佬要求

各位可以根据自己的诉求来判断通过哪种方式接入.

接入方法:
下载PlatinumNeptune这两个库的最新tag的源码.

其中Neptune库取这几个文件夹.


Neptune.png

其中Platinum库取这几个文件夹


image.png

拖入后的工程:

image.png

然后run一下工程.有错误修复错误就可以了.
其中会出现arc的错误.还有duplicated(重复文件)错误.
因为Platinum库是支持多种语言/设备的.所以源码里对每种语言/设备都写了一份代码.
把那些你不需要的删除即可.

然后就很愉快的run成功啦.
开始你的DLNA之旅吧.

源码: https://github.com/moonbeammm/GXDLNA


十四.源码接入Platinum遇到的问题

问题一:

将Platinum和Neptune两个库拖入工程后.
引入#import <Neptune/Neptune.h>就报错.
这是因为你的工程是OC代码.如果想引入c++代码.
需要将OC的.m尾缀改为.mm即可.
只有需要引入c++代码的类需要将尾缀改为.mm.

image.png

问题二:

ARC forbids explicit message send of ‘dealloc'
这是因为这个类使用了MRC的方法.
需要手动将这个类设置为MRC.

image.png

解决办法:

image.png

十五.参考文档

非常感谢以下这些文章作者的分享.没有这些文章.感觉我就两眼一抹黑不知所措了.

Platinum官方
https://www.plutinosoft.com/platinum/

Platinum Demo
https://coding.net/u/ttxoox/p/PlatinumDemo/git

Platinum
https://github.com/plutinosoft/Platinum

抓包分析:
https://www.jianshu.com/p/98c4a2c86429

IBM: UPnP协议编程实践(一)UMPnP设备工作过程
https://www.ibm.com/developerworks/cn/linux/other/UPnP/part1/#1_1

IBM: UPnP协议编程实践(二)设备发现过程
https://www.ibm.com/developerworks/cn/linux/other/UPnP/part2/

基于Platinum实现DMS的Demo
https://github.com/wangshuaidavid/DLNA_iOS_Platinum

DLNA官方网址
http://www.dlna.org/

UPnP官方网址
http://upnp.org/

DLNA维基百科
http://en.wikipedia.org/wiki/Digital_Living_Network_Alliance

UPnP维基百科
http://zh.wikipedia.org/zh/UPnP

十六.结语

因为之前没接触过DLNA.这次是紧急分配给我这个任务.
所以文档写的也很仓促.主要是作为学习DLNA过程中的笔记.
有不对的地方.希望有大佬可以指正.非常感谢.