2024年9月10日发(作者:傅业)
无线传屏技术
—— Android下的Miracast实现
AirPlay
AirPlay是苹果开发的一致无线技术,可以通过Wi-FiI将iPhone、iPad、iPodTouch等iOS
设备上的包括图片、音频、视频通过无线的方式传输到支持AirPlay设备。
AirPlay具备DLNA所没有的镜像功能(AirPlay镜像),可将iPhone或iPad上的画面无线
传输到电视上,即设备显示的是什么,电视屏幕显示的就是什么,而不仅限于图片和视频,
你可以拿着iPad来当做方向盘,看着大屏玩游戏。AirPlay镜像最牛的地方是可以实现双屏
游戏,让你的游戏有更多的交互,比如电视里显示的是游戏画面,而iPad上显示的是比赛
的路线图。
目前,AirPlay只适用于认证过的苹果设备,主要是苹果自己的设备,包括iPad、iPhone、
Apple TV等,和一些苹果授权的合作伙伴的设备,如向Pioneer和Sony提供技术授权的音响。
DLNA
DLNA:Digital Living Network Alliance,是索尼、英特尔、微软等发起的一套PC、移动设
备、消费电器之间互联互通的协议,其宗旨是“随时随地享受音乐、照片和视频”。
DLNA与AirPlay功能比较类似,协议也大体相同,他们都可以让你手机中的媒体内容投
放到电视屏幕上。不同的是手机上的DLNA并没有类似Apple TV的AirPlay镜像功能,也没
有Apple TV所支持的双屏体游戏体验。目前DLNA更多只是能将手机的照片和视频投送到大
屏幕中。
另外,在线视频也可以用DLNA模式推送到客厅电视上显示,安卓系统部分播放器就具
备DLNA功能,目前支持无线推送的视频客户端有:腾讯视频、搜狐视频、PPTV视频。前
提是你有能支持DLNA的电视或电视盒。
DLNA是基于文件的,媒体文件可能有各种各样的编码格式,播放器这端必须能够处理
这么多种编码格式,通常为了比较好的播放体验,DLNA会先缓存一小段时间。
Miracast
Miracast是由Wi-Fi联盟与2012年所制定,以Wi-Fi直连为基础的无线显示标准。支持
此标准的设备可通过无线方式分享视频画面,例如手机壳通过Miracast将影片或照片直接在
电视或其它装置播放而无需受到连接线缆长度的影响。与DLNA不同的是,Miracast也有类
似于AirPlay的镜像功能,可以将手机中屏幕内容直接投放到高清电视屏幕里,这样你也可
以通过电视屏幕来玩游戏了。Android4.2版本以后系统标配此功能(在设置或显示菜单中可
以找到,应用名称诸如:Wlan display、Wifi display、Miracast、Allshare cast、Mirroring screen、
无线显示等,只是各厂家命名不同而已)。可以将手机屏幕通过无线显示接收器将画面无线
传输到其它较大屏幕上,画面传输延迟150ms以下。
Miracast是实时的,它可以实时传输源端(Source)的输出,源端任何屏幕的操作都会
被传输到接收(Sink)端。如果源端是播放媒体文件,源端负责先对媒体文件解码,然后再
编码为H.264格式,接收端只需要做H.264的解码就可以了。相对DLNA,Miracast对于WiFi
通路的要求要更高一些。
Android下的Miracast实现
实际上,Miracast是Wi-Fi联盟(Wi-Fi Alliance)对支持Wi-Fi Display功能的设备的认证
名称。通过Miracast认证的设备将在最大程度内保持对Wi-Fi Display功能的支持和兼容。由
此可知,Miracast考察的就是Wi-Fi Display(本文后续将不再区分Miracast和Wi-Fi Display)。
而Wi-Fi Display的核心功能就是让设备之间通过Wi-Fi无线网络来分享视音频数据。图1
所示为Wi-Fi Display中使用的其它Wi-Fi技术项。
Wi-Fi Miracast
Wi-Fi Protect Setup
11n/WMM/WPA2
图1 Miracast的支撑体系结构
Wi-Fi Direct:即Wi-Fi P2P,支持在没有AP(Access Point)的情况下,两个Wi-Fi
Wi-Fi Direct
设备直连并通信。
Wi-Fi Protect Setup:用于帮助用户自动配置Wi-Fi网络、添加Wi-Fi设备等。
11n/WMM/WPA2:其中11n指802.11n协议;WMM即Wi-Fi MultiMedia,是一种
针对实时视音频数据的QoS服务;而WPA2即Wi-Fi Protected Access第二版,主要用来给传
输的数据进行加密保护。
上述技术展,绝大部分功能由硬件厂商实现,而在Android中,对Miracast来说最重要
的是两个基础技术:一是Wi-Fi Direct,该功能由Android中的WifiP2pService来管理和控制;
一是Wi-Fi MultiMedia,为了支持Miracast,Android 4.2对MultiMedia系统也进行了修改。
Miracast的拓扑结构如图2所示,所支持的视音频格式如表1。
图2 Miracast的四种拓扑结构
分辨率 17种CEA格式,分辨率从640x480到1920x1080,帧率从24到60
29种VESA格式,分辨率从800x600到1920x1080,帧率从30到60
12种手持设备格式,分辨率从640x360到960x540,帧率从30到60
视频
音频
H.264高清
必选:LPCM 16bits,48kHz采样率,双声道
可选:LPCM 16bits,44.1kHz采样率,双声道
Advanced Audio coding
Dolby Advanced Codec 3
表1 Miracast视音频格式支持
Miracast以session为单位来管理两个设备之间的交互工作,主要步骤包括(按顺序):
Device Discovery:通过Wi-Fi P2P来查找附近支持Wi-Fi P2P的设备。
Device Selection:当设备A发现设备B后,A设备需要提示用户。用户可根据需要选
择是否和设备B配对。
Connection Setup:Source和Display设备之间通过Wi-Fi P2P建立连接。根据Wi-Fi
Direct技术规范,这个步骤包括建立一个Group Owner和一个Client。此后,这两个设备将
建立一个TCP连接,同时一个用于RTSP协议的端口将被创建用于后续的Session管理和控制
工作。
Capability Negotiation:在正式传输视音频数据前,Source和Display设备需要交换一
些Miracast参数信息,如双方所支持的视音频格式,二者协商成功后,才能继续后面的流程。
Session Establishment and Streaming:上一步工作完成后,Source和Display设备将建
立一个Miracast Session,而后就可以开始传输视音频数据。Source端的视音频数据将经由
MPEG2TS编码后通过RTP协议传给Display设备,Display设备将解码收到的数据并显示出来。
User Input back channel setup:这是一个可选步骤,主要用于在传输过程中处理用户
发起的一些控制操作,这些控制数据通过TCP在Source和Display设备之间传递。
Payload Control:传输过程中,设备可根据无线信号的强弱,甚至设备的电量状况来
动态调整传输数据和格式。可调整的内容包括压缩率,视音频格式,分辨率等内容。
Session teardown:停止整个Session。
综上所述,Miracast本质上就是一个包括服务端和客户端的基于Wi-Fi的网络应用,服
务端和客户端必须支持RTP/RTSP等网络协议和相应的编解码技术。
Miracast的Android实现涉及到系统的多个模块,包括:
MediaPlayerService及相关模块:因为Miracast本身就涉及到RTP/RTSP及相应的编
解码技术。
SurfaceFlinger及相关模块:SurfaceFlinger的作用是将各层UI数据混屏并投递到显
示设备中去显示。现在,SurfaceFlinger支持多个显示设备,而支持Miracast的远端设备也作
为一个独立的显示设备存在于系统中。
WindowManagerService及相关模块:WindowManagerService用于管理系统中各个
UI层的位置和属性。由于并非所有的UI层都会通过Miracast投递到远端设备上,例如手机
中的视频可投递到远端设备上去显示,但假如在播放过程中,突然弹出一个密码输入框(可
能是某个后台应用程序发起的),则这个密码输入框就不能投递到远端设备上去显示。所以,
WindowManagerService也需要修改以适应Miracast的需要。
DisplayManagerService及相关模块:DisplayManagerService服务是Android 4.2新
增的,用于管理系统中所有的Display设备。
1. SurfaceFlinger对Miracast的支持
相比前面的版本,Android 4.2中SurfaceFlinger的最大变化就是增加了一个名为
DisplayDevice的抽象层。相关结构如图3所示。
图3 SurfaceFlinger家族类图
Surface系统定义了一个DisplayType的枚举,其中有代表手机屏幕的
DISPLAY_PRIMARY和代表HDMI等外接设备的DISPLAY_EXTERNAL。比较有意思的是,
作为Wi-Fi Display,它的设备类型是DISPLAY_VIRTUAL。
SurfaceFlinger类,其内部有一个名为mDisplays的变量,它保存了系统中当前所有
的显示设备(DisplayDevice)。另外,SurfaceFlinger通过mCurrentState和mDrawingState来
控制显示层的状态。其中,mDrawingState用来控制当前正在绘制的显示层的状态,
mCurrentState表示当前所有显示层的状态。有这两种State显示层的原因是不论是Miracast
还是HDMI设备,其在系统中存在的时间是不确定的。例如用户可以随时选择连接一个
Miracast显示设备。为了不破坏当前正在显示的内容,这个新显示设备的一些信息将保存到
CurrentState中。等到SurfaceFlinger下次混屏前再集中处理。
mCurrentState和mDrawingState的类型都是SurfaceFlinger的内部类State。由图3
可知,State首先通过layerSortedByZ变量保存了一个按Z轴排序的显示层数组(在Android
中,显示层的基类是LayerBase),另外还通过displays变量保存了每个显示层对应的
DisplayDeviceState。
DisplayDeviceState的作用是保存对应显示层的DisplayDevice的属性以及一个
ISurfaceTexure接口。这个接口最终将传递给DisplayDevice。
DisplayDevice代表显示设备,它有两个重要的变量,一个是mFrameBufferSurface
和mNativeWindow。mFrameBufferSurace是FrameBufferSurface类型,当显示设备不属于
VIRTUAL类型的话,则该变量不为空。对于Miracast来说,显示数据是通过网络传递给真
正的显示设备的,所有在Source端的SurfaceFlinger来说,就不存在FrameBuffer。故当设
备为VIRTUAL时,其对应的mFrameBufferSurface就为空。而ANativeWindow是Android
显示系统的老员工了。该结构体在多媒体的视频I/O、OpenGL ES等地方用得较多。而在普
通的UI绘制中,ISurfaceTexture接口用得较多。不过早在Android 2.3,Google开发人员就
通过函数指针将ANativeWindow的各项操作和ISurfaceTexture接口统一起来。
作为VIRTUAL的Miracast设备是如何通过DisplayDevice这一层抽象来加入到Surface
系统中来的呢?下面这段代码对理解DisplayDevice的抽象作用极为重要。如图4所示。
图4 SurfaceFlinger代码片段
由上图代码可知:
对于非Virtual设备,DisplayDevice的FrameBufferSurface不为空。而且
SurfaceTextureClient的构造参数来自于FrameBufferSurface的getBufferQueue函数。
如果是Virtual设备,SurfaceTextureClient直接使用了State信息中携带的surface
变量。
据此,可以推测出如图5所示的DisplayDevice的作用。
图5 DisplayDevice的隔离示意图
然后再来看看SurfaceFlinger中混屏操作的实现,代码如图6所示。
图6 SurfaceFlinger的混屏操作
由上可知,SurfaceFlinger将遍历系统中所有的DisplayDevice来完成各自的混屏工作。
2. Framework对Miracast的支持
为了彻底解决多显示设备的问题,Android 4.2在Framework中新增了一个名为
DisplayManagerService的服务,用来统一管理系统中的显示设备。DisplayManagerService和
系统其它几个服务都有交互。整体结构如图7所示。
图7 DisplayManagerService及相关类图
DisplayManagerService主要实现了IDisplayManager接口。这个接口的大部分函数
都和Wi-Fi Display操作相关。
DisplayManagerService和WindowManagerService交互紧密。因为
WindowManagerService管理系统所有UI显示,包括属性,Z轴位置等等。而且,
WindowManagerService是系统内部和SurfaceFlinger交互的重要通道。
DisplayManagerService通过mDisplayAdapters来和DisplayDevice交互。每一个
DisplayDevice都对应有一个DisplayAdapter。
系统定义了四种DisplayAdapter。HeadlessDisplayAdapter和OverlayDisplayAdapter
针对的都是Fake设备。其中OverlayDisplay用于帮助开发者模拟多屏幕之用。
LocalDisplayAdapter代表主屏幕,而WifiDisplayAdapter代表Wi-Fi Display。
3. Android中Miracast动态工作流程介绍
当用户从Settings程序中选择开启Miracast并找到匹配的Device后,系统将通过
WifiDisplayController的requestConnect函数向匹配设备发起连接。代码如下所示:
Public void requestConnect(String address){
for(WifiP2pDevice device:mAvailableWifiDisplayPeers){
if((address)){
connect(device);
}
}
}
连接指定设备的connect函数中,最重要的是updateConnection函数,我们抽取其中部
分代码来看,如图8所示:
图8 updateConnection函数片段
在上述代码中,系统创建了一个RemoteDisplay,并在此Display上监听(listen)。从注释
中可知,该RemoteDisplay就是和远端Device交互的RTP/RTSP通道,而且,一旦有远端Device
连接上,还会通过onDisplayConnected返回一个Surface对象。
根据前面对SurfaceFlinger的介绍,读者可以猜测出Miracast的重头好戏就在
RemoteDisplay以及它返回的这个Surface上了。确实如此,RemoteDisplay将调用
MediaPlayerService的listenForRemoteDisplay函数,最终会得到一个Native的RemoteDisplay
对象。相关类图如图9所示,由图9可知,RemoteDisplay有三个重要成员变量:
mLooper:指向一个ALooper对象。这表明RemoteDisplay是一个基于消息派发和
处理的系统。
mNetSession:指向一个ANetWorkSession对象。ANetWorkSession提供大部分的网
络操作。
mSource:指向一个WifiDisplaySource对象。它从AHandler派生,是mLooper中消
息的处理者。注意,图中的M1、M3、M5等都是Wi-Fi Display技术规范中指定的消息名。
图9 RemoteDisplay类图
RemoteDisplay构造函数中,WifiDisplaySource的start函数将被调用。如此,一个类型
为kWhatStart的消息被加到消息队列中。该消息最终被WifiDisplaySource处理,结果是一
个RTSPServer被创建。代码如下所示:
If (err == OK) {
If (inet_aton(iface.c_str(), &mInterfaceAddr != 0) {
sp
err = mNetSession->createRTSPServer(mInterfaceAddr, port, notify, &mSessionID);
} else {
err = -EINVAL;
}
}
之后,客户端发送的数据都将通过类型为kWhatRTSPNotify的消息加入到系统中来。
而这个消息的处理核心在onReceiveClientData函数中,它囊括了设备之间网络交互的所有细
节。其核心代码如图10所示。
图10 onReceiveClientData核心代码
根据前面的背景知识介绍,设备之间的交互将由Session来管理。在代码中,Session的
概念由WifiSource的内部类PlaybackSession来表示。先来看和其相关的类图结构,如图11
所示:
图11 PlaybackSession及相关类图
PlaybackSession及其内部类Track都从AHandler派生。故它们的工作也依赖于消
Track内部通过mMediaPull变量指向一个MediaPull对象。而MediaPull对象则保
息循环和处理。Track代表视频流或音频流。
存了一个MediaSource对象。在PlaybackSession中,此MediaSource的真正类型为
SurfaceMediaSource。它表明该Media的源来自Surface。
BufferQueue从ISurfaceTexure中派生,根据前面对SurfaceFlinger的介绍,它就是
SurfaceFlinger代码示例中代表虚拟设备的State的surface变量。
当双方设备准备就绪后,MediaPull会通过kWhatPull消息处理不断调用MediaSource
的read函数。在SurfaceMediaSource实现的read函数中,来自SurfaceFlinger的混屏后的数
据经由BufferQueue传递到MediaPull中。代码如图12所示:
图12 MediaPull和SurfaceMediaSource的代码示意
从上图可知,MediaPull通过kWhatPull消息不断调用MediaSource的read函数;
SurfaceMediaSource的read函数通过mBufferQueue来读取数据。那么,mBufferQueue的数
据来自哪里呢?对,正是来自图4的SurfaceFlinger。然后,PlaybackSession拿到这些数据后
做编码,发送给远端设备。
2024年9月10日发(作者:傅业)
无线传屏技术
—— Android下的Miracast实现
AirPlay
AirPlay是苹果开发的一致无线技术,可以通过Wi-FiI将iPhone、iPad、iPodTouch等iOS
设备上的包括图片、音频、视频通过无线的方式传输到支持AirPlay设备。
AirPlay具备DLNA所没有的镜像功能(AirPlay镜像),可将iPhone或iPad上的画面无线
传输到电视上,即设备显示的是什么,电视屏幕显示的就是什么,而不仅限于图片和视频,
你可以拿着iPad来当做方向盘,看着大屏玩游戏。AirPlay镜像最牛的地方是可以实现双屏
游戏,让你的游戏有更多的交互,比如电视里显示的是游戏画面,而iPad上显示的是比赛
的路线图。
目前,AirPlay只适用于认证过的苹果设备,主要是苹果自己的设备,包括iPad、iPhone、
Apple TV等,和一些苹果授权的合作伙伴的设备,如向Pioneer和Sony提供技术授权的音响。
DLNA
DLNA:Digital Living Network Alliance,是索尼、英特尔、微软等发起的一套PC、移动设
备、消费电器之间互联互通的协议,其宗旨是“随时随地享受音乐、照片和视频”。
DLNA与AirPlay功能比较类似,协议也大体相同,他们都可以让你手机中的媒体内容投
放到电视屏幕上。不同的是手机上的DLNA并没有类似Apple TV的AirPlay镜像功能,也没
有Apple TV所支持的双屏体游戏体验。目前DLNA更多只是能将手机的照片和视频投送到大
屏幕中。
另外,在线视频也可以用DLNA模式推送到客厅电视上显示,安卓系统部分播放器就具
备DLNA功能,目前支持无线推送的视频客户端有:腾讯视频、搜狐视频、PPTV视频。前
提是你有能支持DLNA的电视或电视盒。
DLNA是基于文件的,媒体文件可能有各种各样的编码格式,播放器这端必须能够处理
这么多种编码格式,通常为了比较好的播放体验,DLNA会先缓存一小段时间。
Miracast
Miracast是由Wi-Fi联盟与2012年所制定,以Wi-Fi直连为基础的无线显示标准。支持
此标准的设备可通过无线方式分享视频画面,例如手机壳通过Miracast将影片或照片直接在
电视或其它装置播放而无需受到连接线缆长度的影响。与DLNA不同的是,Miracast也有类
似于AirPlay的镜像功能,可以将手机中屏幕内容直接投放到高清电视屏幕里,这样你也可
以通过电视屏幕来玩游戏了。Android4.2版本以后系统标配此功能(在设置或显示菜单中可
以找到,应用名称诸如:Wlan display、Wifi display、Miracast、Allshare cast、Mirroring screen、
无线显示等,只是各厂家命名不同而已)。可以将手机屏幕通过无线显示接收器将画面无线
传输到其它较大屏幕上,画面传输延迟150ms以下。
Miracast是实时的,它可以实时传输源端(Source)的输出,源端任何屏幕的操作都会
被传输到接收(Sink)端。如果源端是播放媒体文件,源端负责先对媒体文件解码,然后再
编码为H.264格式,接收端只需要做H.264的解码就可以了。相对DLNA,Miracast对于WiFi
通路的要求要更高一些。
Android下的Miracast实现
实际上,Miracast是Wi-Fi联盟(Wi-Fi Alliance)对支持Wi-Fi Display功能的设备的认证
名称。通过Miracast认证的设备将在最大程度内保持对Wi-Fi Display功能的支持和兼容。由
此可知,Miracast考察的就是Wi-Fi Display(本文后续将不再区分Miracast和Wi-Fi Display)。
而Wi-Fi Display的核心功能就是让设备之间通过Wi-Fi无线网络来分享视音频数据。图1
所示为Wi-Fi Display中使用的其它Wi-Fi技术项。
Wi-Fi Miracast
Wi-Fi Protect Setup
11n/WMM/WPA2
图1 Miracast的支撑体系结构
Wi-Fi Direct:即Wi-Fi P2P,支持在没有AP(Access Point)的情况下,两个Wi-Fi
Wi-Fi Direct
设备直连并通信。
Wi-Fi Protect Setup:用于帮助用户自动配置Wi-Fi网络、添加Wi-Fi设备等。
11n/WMM/WPA2:其中11n指802.11n协议;WMM即Wi-Fi MultiMedia,是一种
针对实时视音频数据的QoS服务;而WPA2即Wi-Fi Protected Access第二版,主要用来给传
输的数据进行加密保护。
上述技术展,绝大部分功能由硬件厂商实现,而在Android中,对Miracast来说最重要
的是两个基础技术:一是Wi-Fi Direct,该功能由Android中的WifiP2pService来管理和控制;
一是Wi-Fi MultiMedia,为了支持Miracast,Android 4.2对MultiMedia系统也进行了修改。
Miracast的拓扑结构如图2所示,所支持的视音频格式如表1。
图2 Miracast的四种拓扑结构
分辨率 17种CEA格式,分辨率从640x480到1920x1080,帧率从24到60
29种VESA格式,分辨率从800x600到1920x1080,帧率从30到60
12种手持设备格式,分辨率从640x360到960x540,帧率从30到60
视频
音频
H.264高清
必选:LPCM 16bits,48kHz采样率,双声道
可选:LPCM 16bits,44.1kHz采样率,双声道
Advanced Audio coding
Dolby Advanced Codec 3
表1 Miracast视音频格式支持
Miracast以session为单位来管理两个设备之间的交互工作,主要步骤包括(按顺序):
Device Discovery:通过Wi-Fi P2P来查找附近支持Wi-Fi P2P的设备。
Device Selection:当设备A发现设备B后,A设备需要提示用户。用户可根据需要选
择是否和设备B配对。
Connection Setup:Source和Display设备之间通过Wi-Fi P2P建立连接。根据Wi-Fi
Direct技术规范,这个步骤包括建立一个Group Owner和一个Client。此后,这两个设备将
建立一个TCP连接,同时一个用于RTSP协议的端口将被创建用于后续的Session管理和控制
工作。
Capability Negotiation:在正式传输视音频数据前,Source和Display设备需要交换一
些Miracast参数信息,如双方所支持的视音频格式,二者协商成功后,才能继续后面的流程。
Session Establishment and Streaming:上一步工作完成后,Source和Display设备将建
立一个Miracast Session,而后就可以开始传输视音频数据。Source端的视音频数据将经由
MPEG2TS编码后通过RTP协议传给Display设备,Display设备将解码收到的数据并显示出来。
User Input back channel setup:这是一个可选步骤,主要用于在传输过程中处理用户
发起的一些控制操作,这些控制数据通过TCP在Source和Display设备之间传递。
Payload Control:传输过程中,设备可根据无线信号的强弱,甚至设备的电量状况来
动态调整传输数据和格式。可调整的内容包括压缩率,视音频格式,分辨率等内容。
Session teardown:停止整个Session。
综上所述,Miracast本质上就是一个包括服务端和客户端的基于Wi-Fi的网络应用,服
务端和客户端必须支持RTP/RTSP等网络协议和相应的编解码技术。
Miracast的Android实现涉及到系统的多个模块,包括:
MediaPlayerService及相关模块:因为Miracast本身就涉及到RTP/RTSP及相应的编
解码技术。
SurfaceFlinger及相关模块:SurfaceFlinger的作用是将各层UI数据混屏并投递到显
示设备中去显示。现在,SurfaceFlinger支持多个显示设备,而支持Miracast的远端设备也作
为一个独立的显示设备存在于系统中。
WindowManagerService及相关模块:WindowManagerService用于管理系统中各个
UI层的位置和属性。由于并非所有的UI层都会通过Miracast投递到远端设备上,例如手机
中的视频可投递到远端设备上去显示,但假如在播放过程中,突然弹出一个密码输入框(可
能是某个后台应用程序发起的),则这个密码输入框就不能投递到远端设备上去显示。所以,
WindowManagerService也需要修改以适应Miracast的需要。
DisplayManagerService及相关模块:DisplayManagerService服务是Android 4.2新
增的,用于管理系统中所有的Display设备。
1. SurfaceFlinger对Miracast的支持
相比前面的版本,Android 4.2中SurfaceFlinger的最大变化就是增加了一个名为
DisplayDevice的抽象层。相关结构如图3所示。
图3 SurfaceFlinger家族类图
Surface系统定义了一个DisplayType的枚举,其中有代表手机屏幕的
DISPLAY_PRIMARY和代表HDMI等外接设备的DISPLAY_EXTERNAL。比较有意思的是,
作为Wi-Fi Display,它的设备类型是DISPLAY_VIRTUAL。
SurfaceFlinger类,其内部有一个名为mDisplays的变量,它保存了系统中当前所有
的显示设备(DisplayDevice)。另外,SurfaceFlinger通过mCurrentState和mDrawingState来
控制显示层的状态。其中,mDrawingState用来控制当前正在绘制的显示层的状态,
mCurrentState表示当前所有显示层的状态。有这两种State显示层的原因是不论是Miracast
还是HDMI设备,其在系统中存在的时间是不确定的。例如用户可以随时选择连接一个
Miracast显示设备。为了不破坏当前正在显示的内容,这个新显示设备的一些信息将保存到
CurrentState中。等到SurfaceFlinger下次混屏前再集中处理。
mCurrentState和mDrawingState的类型都是SurfaceFlinger的内部类State。由图3
可知,State首先通过layerSortedByZ变量保存了一个按Z轴排序的显示层数组(在Android
中,显示层的基类是LayerBase),另外还通过displays变量保存了每个显示层对应的
DisplayDeviceState。
DisplayDeviceState的作用是保存对应显示层的DisplayDevice的属性以及一个
ISurfaceTexure接口。这个接口最终将传递给DisplayDevice。
DisplayDevice代表显示设备,它有两个重要的变量,一个是mFrameBufferSurface
和mNativeWindow。mFrameBufferSurace是FrameBufferSurface类型,当显示设备不属于
VIRTUAL类型的话,则该变量不为空。对于Miracast来说,显示数据是通过网络传递给真
正的显示设备的,所有在Source端的SurfaceFlinger来说,就不存在FrameBuffer。故当设
备为VIRTUAL时,其对应的mFrameBufferSurface就为空。而ANativeWindow是Android
显示系统的老员工了。该结构体在多媒体的视频I/O、OpenGL ES等地方用得较多。而在普
通的UI绘制中,ISurfaceTexture接口用得较多。不过早在Android 2.3,Google开发人员就
通过函数指针将ANativeWindow的各项操作和ISurfaceTexture接口统一起来。
作为VIRTUAL的Miracast设备是如何通过DisplayDevice这一层抽象来加入到Surface
系统中来的呢?下面这段代码对理解DisplayDevice的抽象作用极为重要。如图4所示。
图4 SurfaceFlinger代码片段
由上图代码可知:
对于非Virtual设备,DisplayDevice的FrameBufferSurface不为空。而且
SurfaceTextureClient的构造参数来自于FrameBufferSurface的getBufferQueue函数。
如果是Virtual设备,SurfaceTextureClient直接使用了State信息中携带的surface
变量。
据此,可以推测出如图5所示的DisplayDevice的作用。
图5 DisplayDevice的隔离示意图
然后再来看看SurfaceFlinger中混屏操作的实现,代码如图6所示。
图6 SurfaceFlinger的混屏操作
由上可知,SurfaceFlinger将遍历系统中所有的DisplayDevice来完成各自的混屏工作。
2. Framework对Miracast的支持
为了彻底解决多显示设备的问题,Android 4.2在Framework中新增了一个名为
DisplayManagerService的服务,用来统一管理系统中的显示设备。DisplayManagerService和
系统其它几个服务都有交互。整体结构如图7所示。
图7 DisplayManagerService及相关类图
DisplayManagerService主要实现了IDisplayManager接口。这个接口的大部分函数
都和Wi-Fi Display操作相关。
DisplayManagerService和WindowManagerService交互紧密。因为
WindowManagerService管理系统所有UI显示,包括属性,Z轴位置等等。而且,
WindowManagerService是系统内部和SurfaceFlinger交互的重要通道。
DisplayManagerService通过mDisplayAdapters来和DisplayDevice交互。每一个
DisplayDevice都对应有一个DisplayAdapter。
系统定义了四种DisplayAdapter。HeadlessDisplayAdapter和OverlayDisplayAdapter
针对的都是Fake设备。其中OverlayDisplay用于帮助开发者模拟多屏幕之用。
LocalDisplayAdapter代表主屏幕,而WifiDisplayAdapter代表Wi-Fi Display。
3. Android中Miracast动态工作流程介绍
当用户从Settings程序中选择开启Miracast并找到匹配的Device后,系统将通过
WifiDisplayController的requestConnect函数向匹配设备发起连接。代码如下所示:
Public void requestConnect(String address){
for(WifiP2pDevice device:mAvailableWifiDisplayPeers){
if((address)){
connect(device);
}
}
}
连接指定设备的connect函数中,最重要的是updateConnection函数,我们抽取其中部
分代码来看,如图8所示:
图8 updateConnection函数片段
在上述代码中,系统创建了一个RemoteDisplay,并在此Display上监听(listen)。从注释
中可知,该RemoteDisplay就是和远端Device交互的RTP/RTSP通道,而且,一旦有远端Device
连接上,还会通过onDisplayConnected返回一个Surface对象。
根据前面对SurfaceFlinger的介绍,读者可以猜测出Miracast的重头好戏就在
RemoteDisplay以及它返回的这个Surface上了。确实如此,RemoteDisplay将调用
MediaPlayerService的listenForRemoteDisplay函数,最终会得到一个Native的RemoteDisplay
对象。相关类图如图9所示,由图9可知,RemoteDisplay有三个重要成员变量:
mLooper:指向一个ALooper对象。这表明RemoteDisplay是一个基于消息派发和
处理的系统。
mNetSession:指向一个ANetWorkSession对象。ANetWorkSession提供大部分的网
络操作。
mSource:指向一个WifiDisplaySource对象。它从AHandler派生,是mLooper中消
息的处理者。注意,图中的M1、M3、M5等都是Wi-Fi Display技术规范中指定的消息名。
图9 RemoteDisplay类图
RemoteDisplay构造函数中,WifiDisplaySource的start函数将被调用。如此,一个类型
为kWhatStart的消息被加到消息队列中。该消息最终被WifiDisplaySource处理,结果是一
个RTSPServer被创建。代码如下所示:
If (err == OK) {
If (inet_aton(iface.c_str(), &mInterfaceAddr != 0) {
sp
err = mNetSession->createRTSPServer(mInterfaceAddr, port, notify, &mSessionID);
} else {
err = -EINVAL;
}
}
之后,客户端发送的数据都将通过类型为kWhatRTSPNotify的消息加入到系统中来。
而这个消息的处理核心在onReceiveClientData函数中,它囊括了设备之间网络交互的所有细
节。其核心代码如图10所示。
图10 onReceiveClientData核心代码
根据前面的背景知识介绍,设备之间的交互将由Session来管理。在代码中,Session的
概念由WifiSource的内部类PlaybackSession来表示。先来看和其相关的类图结构,如图11
所示:
图11 PlaybackSession及相关类图
PlaybackSession及其内部类Track都从AHandler派生。故它们的工作也依赖于消
Track内部通过mMediaPull变量指向一个MediaPull对象。而MediaPull对象则保
息循环和处理。Track代表视频流或音频流。
存了一个MediaSource对象。在PlaybackSession中,此MediaSource的真正类型为
SurfaceMediaSource。它表明该Media的源来自Surface。
BufferQueue从ISurfaceTexure中派生,根据前面对SurfaceFlinger的介绍,它就是
SurfaceFlinger代码示例中代表虚拟设备的State的surface变量。
当双方设备准备就绪后,MediaPull会通过kWhatPull消息处理不断调用MediaSource
的read函数。在SurfaceMediaSource实现的read函数中,来自SurfaceFlinger的混屏后的数
据经由BufferQueue传递到MediaPull中。代码如图12所示:
图12 MediaPull和SurfaceMediaSource的代码示意
从上图可知,MediaPull通过kWhatPull消息不断调用MediaSource的read函数;
SurfaceMediaSource的read函数通过mBufferQueue来读取数据。那么,mBufferQueue的数
据来自哪里呢?对,正是来自图4的SurfaceFlinger。然后,PlaybackSession拿到这些数据后
做编码,发送给远端设备。