第十七篇 --ANDROID DisplayManager 服务解析一
ANDROID从版本4.2开始提供了一个显示管理服务DisplayManagerService,支持多种显示类型的多个显示器的镜像显示,包括内建的显示类型(本地)、HDMI显示类型以及支持WIFI Display 协议( MIRACAST),实现本地设备在远程显示器上的镜像显示。
整个架构类图如下:
显示管理服务通过DisplayManager提供对外接口,提供的接口包括如下几个:
1 、public Display getDisplay(int displayId)
根据displayId参数获得一个逻辑显示器的信息
2、 public Display[] getDisplays()
获得当前所有有效的逻辑显示器列表
3、public void registerDisplayListener(DisplayListener listener, Handler handler)
登记一个显示监听对象,用来监听显示器的新增、去除或改变通知事件。
4、public void unregisterDisplayListener(DisplayListener listener)
取消先前登记的一个显示监听对象
5、scanWifiDisplays()
启动WIFI显示器的扫描。
6、 public void connectWifiDisplay(String deviceAddress)
根据设备地址连接WIFI显示器
7、public void disconnectWifiDisplay()
断开WIFI显示器
8、 public void renameWifiDisplay(String deviceAddress, String alias)
为WIFI显示器命名
9、public void forgetWifiDisplay(String deviceAddress)
取消先前记忆的WIFI显示器
10、public WifiDisplayStatus getWifiDisplayStatus()
得到当前的WIFI显示器的状态
显示管理系统还与其它系统交互,实现WIFI显示器的发现、WIFI显示器在窗口系统中的登记、窗口内容在WIFI显示器的显示(源端镜像数据的获取、加密、编码,SINK端接收的镜像数据的解码和播放等)等功能。
通过WifiP2pManager接口与WifiP2pService交互,通过WIFI-DIRECT来实现WIFI显示器的自动发现。
窗口管理服务是显示管理服务的监听对象,窗口管理服务通过DisplayManager接口向DisplayManagerService进行登记,当WIFI显示器被发现和连接成功后以及WIFI显示器断开和状态改变后,都会通过回调向窗口管理服务发送事件,窗口管理服务的相应回调函数onDisplayAdded、onDisplayChanged、onDisplayRemoved被调用,用来在窗口系统中进行WIFI显示器的登记以及取消登记、状态改变等处理。
另外DisplayManagerService服务还通过WindowManagerFuncs窗口管理功能接口直接调用窗口管理服务(WindowManagerService是该接口的实现)的函数,实现窗口内容的刷新。同样DisplayManagerService服务还通过InputManagerFuncs接口直接调用输入管理服务的函数setDisplayViewports,用来设置输入系统需要的显示器的显示视图信息。
显示管理系统还通过IMediaPlayerService接口与MediaPlayerService服务交互。
如调用MediaPlayerService服务的listenForRemoteDisplay函数,用来在媒体服务中实例化一个远端显示器的本地代理对象RemoteDisplay,显示系统通过IRemoteDisplay接口调用媒体服务,目前IRemoteDisplay接口只有dispose一个接口函数,用来断开远端显示器,停止监听新的连接。
显示管理系统的WifiDisplaySource对象还调用MediaPlayerService服务的makeHDCP函数来实例化一个HDCP对象并返回给显示系统一个IHDCP接口,用来实现HDCP加密服务。
显示系统的SINK端的TunnelRenderer对象在其initPlayer中还调用MediaPlayerService服务的create函数来创建一个MediaPlayer对象,并返回一个IMediaPlayer接口给显示系统使用,用来实现SINK端接收的镜像数据的播放。
显示管理系统源端获取的镜像数据经过音视频编码(H264),然后进行HDCP加密和PES packetization及TS流化(转换为TS流)后 ,最后打包成RTP包经过UDP通道发送到SINK端,SINK端要经过相反的处理过程,从UDP通道接收RTP包,然后进行TS解析和PES去packetization化和HDCP解密,最后送给解码器进行解码。解码后的数据送给播放器的呈现器进行呈现。
源端和SINK端的音视频编解码都通过IOMX接口与底层的多媒体框架交互,实现音视频编解码功能。IOMX接口对应的对象OMX也是在MediaPlayerService服务端实例化的,在客户端对象OMXClient的connect函数中通过调用MediaPlayerService服务的getOMX函数返回OMX对应的IOMX接口。OMX对象是对多媒体框架OPENOMX的封装。
源端的音视频编码、TS流化、HDCP加密、RTP打包发送的流程都有PlaybackSession线程类管理和调度,PlaybackSession类初始化时实例化一个SurfaceMediaSource对象,SurfaceMediaSource对象内部实例化一个BufferQueue对象(BufferQueue从ISurfaceTexure中派生)。在与SINK端建立连接后,通过IRemoteDisplayClient接口的回调函数onDisplayConnected把BufferQueue对象传给JAVA层,JAVA层的WifiDisplayAdapter对象收到onDisplayConnected事件后调用Surface类的createDisplay函数在SurfaceFlinger服务中登记一个虚拟显示器,并调用Surface类的setDisplaySurface函数把BufferQueue传给SurfaceFlinger服务虚拟显示器对应的DisplayDeviceState变量中.。因此PlaybackSession可以使用BufferQueue对象从SurfaceFlinger服务读取要镜像的数据。
SINK端WifiDisplaySink对象接收的数据经RTP解码(由RTPSink对象负责)后,送给TunnelRenderer对象进进行呈现, TunnelRenderer对象在initPlayer函数中实例化一个PlayerClient播放客户端,并通过IMediaPlayerService接口调用MediaPlayerService服务的create函数创建一个MediaPlayer对象并返回TunnelRenderer对象IMediaPlayer接口, TunnelRenderer对象使用IMediaPlayer接口对接收到的镜像数据进行播放和呈现, TunnelRenderer对象还在initPlayer函数中通过SurfaceComposerClient对象实例化和获得一个Surface对象,并调用其getSurfaceTexture函数获得Surface对象对应的ISurfaceTexture,并调用IMediaPlayer接口的setVideoSurfaceTexture函数把ISurfaceTexture赋值给播放器,从而实现播放器解码后的显示数据送给SurfaceFlinger显示服务进行显示.
SINK端的WifiDisplaySink对象和源端WifiDisplaySource的对象负责WIFI Display 交互协议的处理,两个对象都包含一个ANetworkSession对象负责两者之间的网络交互会话过程。
如下是WIFI Display 协议的框架图。
ANDROID4.2的开源代码提供了WIFI Display 协议的具体实现,但对SINK端只是作为一个本地测试命令来执行,并且没有提供图中的用户输入功能,而对于WFD Source端则提供了完整的实现代码,这主要是ANDROID系统主要是作为手机平板操作系统使用的缘故。
ANDROID4.2的WIFIDisplay的实现包括JAVA部分和C++部分。JAVA部分的功能主要对应DisplayManagerService服务,DisplayManagerService服务对于WIFIDisplay实例化和登记一个WifiDisplayAdapter对象(派生自DisplayAdapter),用来与对应的显示设备建立连接,每个DisplayAdapter与一个显示设备DisplayDevice类一一对应 。每个显示设备在DisplayManagerService服务中 对应一个LogicalDisplay对象。
DisplayManager发起的对WIFI Display的请求都由DisplayManagerService服务转发给WifiDisplayAdapter对象处理,为了实现异步交互和避免死锁,WifiDisplayAdapter对象对于每个请求都启动一个线程来实际完成请求处理,并实例化一个WifiDisplayController对象来封装这些请求的调用。WifiDisplayAdapter对象内部也实例化一个WifiDisplayController.Listener对象用来监听请求的状态。 WifiDisplayController对象内部实例化一个WifiP2pManager对象用来通过其接口向WifiP2pService服务发起WI-FI DIRECT请求。在与WIFI Display连接时通过实例化一个RemoteDisplay 对象来启动监听底层WIFI Display的连接事件。
C++ 部分位于媒体框架层,处于libstagefright目录的wifi-display目录下,wifi-display目录包含两个sink和source两个子目录(分别对应wifi display的sink和source 对应的程序),wifi-display目录还包含source和SINK公用的程序。
主要包含如下文件:
Sink目录主要包含WifiDisplaySink.cpp、RTPSink.cpp、TunnelRenderer.cpp等C++类文件和相应的头文件,分别负责SINK端的协议协议,RTP接收和镜像数据的呈现过程。而TS解析和PES去packetization化及解码过程都有播放器NuPlayer完成,由于Sink端只是一个测试例程,因此暂没有提供HDCP解码流程。
Source目录主要包括WifiDisplaySource.cpp、PlaybackSession.cpp、MediaPuller.cpp、Converter.cpp、TSPacketizer.cpp、Sender.cpp等C++类文件和相应的头文件,分别负责Source端的协议交互,会话过程管理、镜像媒体读取、编码、TS打包及RTP打包发送等过程。Converter 对象中包括一个MediaCodec对象具体负责编码过程,MediaCodec对象实际通过ACodec对象调用IOMX接口使用OPENOMX媒体框架完成镜像数据的编码。
sink和source使用的公共类文件主要是ANetworkSession.cpp和对应头文件以及一个 启动WifiDisplaySink的主程序wfd.cpp,用来 生成命令wfd。
整个协议主要的流程包括WIFI Display显示设备的连接过程和镜像数据的打包发送和接收流程,具体流程在下一篇博文中阐述。