1. 有趣的

在一个项目工作中偶然接触到 UPnP(Universal Plug and Play),一个基于本地网络的即插即用设备互联协议标准,后来经了解发现 UPnP 是个很有趣的东西。这篇博客分享一些 UPnP 的知识及介绍在 Android 应用中使用 UPnP(资料 UPnP Device Architecture 2.0)。

1.1 特性

  • 对等,没有主次
    所有 UPnP 网络中的设备即可以提供服务,也可以主动发起数据传输,设备的角色取决于软件(不像USB协议那样的主从模式)。如果一个设备实现了 AVTransport 那么它可以提供媒体渲染服务,例如一台智能电视;如果一个设备实现了 ContentDirectory 那么它可以提供媒体浏览和流媒体传输服务,例如开启媒体流的功能的 PC;如果设备是使用 ControlPoint 那么它就像是个智能遥控器,例如安装一些 DLNA 控制软件的手机;一台设备可以支持多个服务同时还可以有控制功能,例如 Windows Media Player 它即可以提供媒体内容服务,也可以播放推送来的媒体,还可以把媒体推送到其他设备上播放。UPnP 网络中的设备没有一个 Master,所有设备都是对等。

  • 即插即用,零配置
    这个即插即用不是硬件层面的(不像 USB 那样的串行总线设备拓展),而是通过本地网络实现。设备可以动态加入网络(通过 SDDP 发现设备),传达其功能(使用 XML 描述),并了解其他设备的存在和功能。设备可以顺利自动地离开网络,而不会留下任何不必要的状态。这些都可以不需要用户配置,设备处于同一个网络下就可以通过 UPnP 直接互动。

1.2 关于安全性与路由器的 UPnP 功能

广泛使用的路由器 UPnP 端口映射功能用的是 Internet Gateway Device(简称 IGD),是 UPnP 定义的设备之一,提供端口映射功能(NAT 穿越),让本地网络的设备端口映射到外网。这样软件就能使用 IGD 绕过 NAT 进行数据传输,映射之后外部网络就能很轻松的直接访问内网设备,他安全性取决于使用者对软件的信任。下图是路由器开启 UPnP IGD 功能后被其他设备发现,软件能够直接使用它的(端口映射)功能,不需要用户配置。

It's about the network
路由器 IGD 设备

UPnP 设备互动一般没有身份验证,比如家用智能电视机任何设备都可以控制,但是本地网络的 UPnP 应用都是可预测可控制的。虽然 ControlPoint 能够推送外网上的视频给电视机,但是就和打开电脑浏览器看电影差不多和开不开 IGD 没关系。

2. 媒体分享与无线显示的区别

UPnP 本身包含了一组网络协议如 TCP、UDP、HTTP、XML,而 DLNA(Digital Living Network Alliance)则是包含了更多协议的标准,下图 DLNA 的技术架构里面列出了对 UPnP 的使用(资料 DLNA Guidelines)。

It's about the network
DLNA 技术架构

DLNA/UPnP 的内容很多是无线媒体分享娱乐方面,和无线显示技术 Miracast 作用很像但是又有着本质的区别

2.1 硬件支持

  • DLNA/UPnP:基于网络的设备互联,不需要硬件、操作系统支持,软件就可以实现。
  • Miracast:需要操作系统支持(Android 4.2 以上、Windows 8.1 以上),还需要 Wi-Fi 硬件模块支持。

2.2 连接对象

  • DLNA/UPnP:软件上的服务和控制点,一个服务允许多个设备访问,一个控制点也可以处理多个服务。一个设备可以同时实现多个服务,服务和控制点连接没有绑定。比如推送的一个电影到电视上播放,电影有可能在手机上、电脑上、电视机本身、还可以是一个外网视频,而电视剧同时还能支持其他设备的内容访问及播放控制。
  • Miracast:点对点(Peer to Peer),是设备连接扩展一个无线显示屏幕。如果一个屏幕需要被其他设备连接使用,则首先要解除原有的连接。

2.3 媒体传输,编解码

  • DLNA/UPnP:Play To(推送),当用户推送一个电影到播放器(如电视机,DLNA 渲染设备,简称 DMR)时候是向 DMR 发送了电影的 URL,DMR 收到这个 URL 推送时就播放这个电影,数据传输是在 DMS (DLNA 媒体服务器,简称 DMS)和 DMR 之间。
  • Miracast:Cast(屏幕投射),它是将屏幕内容、声音编码为特定媒体格式,通过媒体流传输到接收端,接收端再解码呈现。
  • 过程:DLNA/UPnP,屏幕 —> 编码 —> 接收端解码。接收端只需支持固定解码。Miracast,DMS 媒体源 —> DMR 解码。DMR 需要支持全格式解码。

2.4 功能对比,应用场合

  • DLNA/UPnP:没有涉及到屏幕内容传输,Android 5.0 以上可以通过 MediaProjection 取得屏幕数据,编码,推送到 DMR,这样也可以以软件的方式实现屏幕传输,但是效率不会有底层支持模式下的 Miracast 高,另外像远程屏幕点击的功能也不好实现。DLNA 的方式更适用于无线媒体分享娱乐。
  • Miracast:本身支持在无线屏幕上显示不一样的内容,默认显示主屏镜像,但是 Miracast 没法支持远程控制,比如控制屏幕扬声器的音量。如果通过 Miracast 看高清电影会多一个转码过程,其效率没有 DLNA 高,而且 1080p 以上的高质量媒体源很难保持原始质量。Miracast 的方式更适用于多屏幕内容展现互动。

3. Android 应用使用 DLNA/UPnP

在发布 UPnP 协议规范的网站上(Open Connectivity)列出了一些开源的 UPnP SDK,Cling 就是其中一款,它是 java 实现的可以在 Android 上使用。cling 主要分两部分:cling core,由 java 实现的 UPnP Device Architecture 1.0;cling support,Media Server、Media Renderer 的一些拓展还有路由器 IGD API,使用 cling 可以实现大部分的 DLNA/UPnP 功能应用。

3.1 使用 cling 库

导入库

cling 依赖的库不少,该项目源码是通过 maven 在线构建的方式编译,也可以将依赖库下载到本地通过 Android Studio(gradle)直接构建。这个是 cling 2.1.1 的完整依赖库(供参考)

cling-core-2.1.1.jar,cling-support-2.1.1.jar,
seamless-http-1.1.1.jar,seamless-util-1.1.1.jar,seamless-xml-1.1.1.jar

jetty-client-8.1.21.v20160908.jar,jetty-continuation-8.1.21.v20160908.jar,
jetty-http-8.1.21.v20160908.jar,jetty-io-8.1.21.v20160908.jar,
jetty-security-8.1.21.v20160908.jar,jetty-server-8.1.21.v20160908.jar,
jetty-servlet-8.1.21.v20160908.jar,jetty-util-8.1.21.v20160908.jar,
servlet-api-3.0.jar

javax.enterprise-cdi-api-1.2.jar,javax.mail-mail-1.4.7.jar
net.sf.kxml-kxml2-2.3.0.jar
org.dbunit-dbunit-2.2.3.jar
org.hibernate-hibernate-core-3.6.10.Final.jar
org.slf4j-slf4j-api-1.6.6.jar,org.slf4j-slf4j-jdk14-1.6.6.jar

创建设备,实现服务

如果设备提供功能,能让其他设备看到,则需要创建 LocalDevice

new LocalDevice(
new DeviceIdentity(new UDN("1111")),
new UDADeviceType("MediaRenderer"),
new DeviceDetails("My MediaRenderer"),
new LocalService[]{
                   createAVTransportService(),
                   createRenderingControlService()
           });
  • DeviceIdentity:设备的 UDN,即 UPnP 网络中唯一的设备标识符需要唯一,程序可以读取机器的一些特征 ID 来生成 UDN。
  • UDADeviceType:设备类型,比如媒体渲染器是 “MediaRenderer”,媒体服务器是 “MediaServer”。
  • DeviceDetails:设备的名称,制造商,型号等信息。这些信息一般是对用户可见的,可以参考 android.os.Build 生成默认信息,然后用户可以设置。
  • LocalService:设备提供的服务,这里就是 UPnP 设备服务功能的入口,要根据上面的 UDADeviceType 实现不同的服务,如这里的 DMR 按照规范需要实现的 AVTransport。cling 库封装好的各种设备服务的抽象类,继承服务抽象类,实现功能即可。

注册表,事件侦听,控制点

UPnP 的控制能操控网络中的各种服务,cling 的 API 功能主要封装在那个 AndroidUpnpService 里。

  • Registry(不是操作系统的注册表)是通过 AndroidUpnpService.getRegistry() 得到 UPnP 服务的 Registry,这里有 UPnP 网络中的本地及远程设备列表,也是在这里添加删除本地服务,另外在这里设置 UPnP 事件侦听。
  • RegistryListener 事件侦听器里有设备上线、下线、更新等消息。
  • ControlPoint 通过 AndroidUpnpService.getControlPoint() 得到,通过 ControlPoint 就可以搜索设备,可以执行各种使用服务的操作。

4. 媒体流传输

如果应用需要能够分享本地媒体,让 UPnP 网络上的其他设备能浏览,或是能够主动推送本地媒体到一个远程 DMR 播放。那么这个应用需要实现 HTTP/RTP 媒体流传输功能,还要创建 DMS 设备。媒体流服务一般用 HTTP,因为 HTTP 媒体流传输是 DLNA 规范要求支持,而其他方式媒体流传输是 Optional。

下图左边是 Android 推送一个本地视频到 Windows 10 Media Player,播放显示的媒体信息来源来自于 192.168.1.101:9000 即 Android 提供的流媒体服务(URL 地址资源路径经过编码)。

It's about the network
媒体推送

4.1 一些 HTTP Server 库的信息

  • Apache HttpComponents Apache 开发的,很专业,需求匹配基本上就可以选用了。早期版本的 Android 集成了该库后面没有集成了(参考信息 Apache HttpClient for Android),如果现在使用该库可能会遇到兼容性问题,如 Android Studio 提示过期的 API(deprecated),编译会提示缺少 class 之类的信息。

  • NanoHTTPD
    引用官方的说法:Java 实现的一个小型的 Web Server。确实很小,把几个 java 文件引入项目即可,该库也有实现了 Seekable Content 服务的代码,目前他是以源码的形式发布。使用方便,不过在开发后期的代码检查和分析里(Inspection)会有不少警告,该库提升空间比较大,例如下面源码里的一行代码(NanoHTTPD.java 第 651 行)

this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();

/*
该行是要得到一个地址字符串给 remoteIp(String 类型),inetAddress.getHostAddress() 返回的已经是 String 类型,
但是这里是调用了 String.toString 来返回一个 String。
*/

5. 结束

以前做过一个 DLNA/UPnP 全功能的 Android 播放器 APP,使用 Android 系统 MediaPlayer 做媒体播放,用多媒体数据库来构建播放器媒体库,用 Apache Httpd 来支持 Http 在线媒体访问。断断续续的更新了几个版本,启用了 Material Design,优化了 UPnP 操作,播放核心换成支持硬解的 VLC,使用 NanoHTTPD 作为新的 Http 服务器。

虽然有安全性的忧虑,但是在标准化和广泛的设备支持下,我觉得 UPnP 在家庭网络上还是有一些可能性。

参考资料

UPnP Device Architecture 2.0 http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v2.0.pdf
DLNA Guidelines https://spirespark.com/dlna/guidelines/
Cling UPnP Stack https://github.com/4thline/cling/
NanoHTTPD https://github.com/NanoHttpd/nanohttpd/