Android WifiDisplay 分析-3:RTSP 建立

RTSP server

Wi-Fi Display 协议可知,在建立 P2P 连接后,将启动RTSP会话进行双方能力的交互,即M1~M7七个消息的交互。

frameworks/base/media/java/android/media/RemoteDisplay.java
    |-- listen()

在进行参数检查后,首先创建一个 RemoteDisplay 对象,接着调用 startListening 的方法,该方法将调用 JNInativeListen 方法,此处有个 mGuardCloseGuard 对象,是一种用于显示释放资源的机制。

frameworks/base/core/jni/android_media_RemoteDisplay.cpp
    |-- nativeListen()

ServiceManager 中获取 MediaPlayerServiceBpbinder 引用,然后采用类似于设计模式中的包装模式,由传入的第二个参数 remoteDisplayObj,即RemoteDisplay对象构造一个 NativeRemoteDisplayClient 。在NativeRemoteDisplayClient 中通过JNI的反向调用,就可以直接回调 RemoteDisplay 中的一些函数,从而实现回调机制。

frameworks/base/core/jni/android_media_RemoteDisplay.cpp 
    |-- Class NativeRemoteDisplayClient

采用 JNI 回调 JAVA 函数的机制,首先在 NativeRemoteDisplayClient 的构造函数中,把 JAVA 类对象 RemoteDisplay 保存到 mRemoteDisplayObjGlobal 中,使三个 JNI 回调函数(onDisplayConnected、onDisplayDisconnected、onDisplayError),分别对应到 RemoteDisplay 类的 (notifyDisplayConnected、notifyDisplayDisconnected、notifyDisplayError)三个 JAVA 方法。
当连接成功后,将通过 android_view_Surface_createFromIGraphicBufferProducer 函数创建 surface 并通过 notifyDisplayConnected 回调给上层使用。

frameworks/base/core/jni/android_media_RemoteDisplay.cpp
    |-- nativeListen()

回到 nativeListen 中,接着调用到 MediaPlayerServicelistenForRemoteDisplay 方法去监听 socket 连接,返回一个 IRemoteDisplay 对象(即 BpRemoteDisplay )用于 Binder 调用,然后会由这个 IRemoteDisplay 对象构造一个 NativeRemoteDisplay 对象并把它的指针地址返回给上层 RemoteDisplay 使用。

frameworks/base/core/jni/android_media_RemoteDisplay.cpp 
    |-- Class NativeRemoteDisplay

NativeRemoteDisplay 类只是对 IRemoteDisplay 对象的一个封装,使上层间接调用相关函数。

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
    |-- listenForRemoteDisplay()

在权限检查之后,创建一个 RemoteDisplayC++ 对象。

这里看RemoteDisplay.cpp文件。RemoteDisplay继承于BnRemoteDisplay,并实现BnRemoteDisplay中的一些方法,有兴趣的可以去看一下IRemoteDisplay的实现。

frameworks/av/media/libmediaplayerservice/RemoteDisplay.cpp
    |-- Class RemoteDisplay

RemoteDisplay 代码简单,但是包含内容丰富。主要有三个重要元素:

  • ALooper

    ALooper 中会创建一个 Thread ,并且不断的通过Looper 循环去接收消息,并将消息 dispatchWifiDisplaySourceonMessageReceived 方法去处理。

  • ANetworkSession

    ANetworkSession 用于处理与网络请求相关的工作。

  • WifiDisplaySource

    WifiDisplaySource 继承于 AHandler ,并实现其中的 onMessageReceived 方法用于处理消息。

frameworks/av/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
    |-- frameworks/av/media/libstagefright/wifi-display/VideoFormats.cpp
        |-- Class VideoFormats

在构造函数中, 首先对mResolutionTable进行复制,其是按照 Wifi Display 规范定义好的一个3*32数组,里面的元素是 config_t 类型,包含了长、宽、帧率、隔行视频、profileH.264 level 。然后在构造函数中,将 mResolutionEnabled[] 数组全部置0, mResolutionEnabled 数组有三个元素,分别对应 CEA、VESA、HH 被选取的位,如果在 mConfigs 数组中相应的格式被选取,就会将 mResolutionEnabled 对应的位置为1,相反取消支持一种格式时,相应的位就被置为0。

frameworks/av/media/libstagefright/wifi-display/VideoFormats.cpp
    |-- Class VideoFormats
        |-- setNativeResolution()
            |-- setResolutionEnabled() 

设置 mResolutionEnabledmConfigs 中的相应的值,由默认形参我们知道,设置 mResolutionEnabled 相应 type 中的对应格式为1,并设置 mConfigs 中的 profilelevel 值为 CBPLevel 3.1 。这里设置640480 p60是因为在 Wifi Display 规范中,这个格式是必须要强制支持的,在 Miracast 认证中,这种格式也会被测试到。然后回到WifiDisplaySource的构造函数中,接下来会调用setNativeResolution去设置当前系统支持的默认格式为1280720 p30,并调用enableResolutionUpto去将1280*720 p30以上的格式都设置为支持

frameworks/av/media/libstagefright/wifi-display/VideoFormats.cpp
    |-- Class VideoFormats
        |-- enableResolutionUpto()

采用 width * height * fps * (!interlaced + 1) 的方式去计算一个 score 值,然后遍历所有的 mResolutionTable 中的值去检查是否计算到的值比当前 score 要高,如果大于当前 score 值,就将这种分辨率 enable ,并设置 mConfigs 中对应分辨率的 profileH.264 levelCHPLevel 3.2

frameworks/av/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
    |-- start()
        |-- PostAndAwaitResponse()
            |-- frameworks/av/media/libstagefright/foundation/AMessage.cpp
                |-- PostAndAwaitResponse()
                    |-- frameworks/av/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
                        |-- onMessageReceived()
                            |-- case kWhatStart
                                |-- frameworks/av/media/libstagefright/foundation/ANetworkSession.cpp
                                    |-- createRTSPServer()
                                        |-- createClientOrServer()

回到 RemoteDisplay 构造函数中,将调用 WifiDisplaySource 类的 start 方法,发送 kWhatStart 消息,通过消息机制发送并自身接收,主要是为了避开主线程处理的繁杂事物,将其他任务都放在 Thread 中去执行。
此处最终调用 createRTSPServer 去创建一个 RTSP Server ,直接调用 createClientOrServer ,第一个参数是 kModeCreateRTSPServer 表示要创建一个 RTSP server 。代码中首先创建一个 socket,然后设置一下 reuseno-block 属性,接着 bind 到指定的 IPport 上,之后开始 listen 。接下来置当前 ANetworkSession 的状态是 LISTENING_RTSP 。然后创建一个 Session 会话对象,在构造函数中会传入 notify 作为参数, notify 是一个 kWhatRTSPNotifyAMessag。然后添加到 mSessions 数组当中。接着调用 interrupt 方法,让 ANetworkSessionNetworkThread 线程跳出 select 语句,并重新计算 readFdwriteFd 用于 select 监听的文件句柄。
interrupt 方法向 pipe 中写入一个空消息,前面我们已经介绍过 threadLoop 了,这里就会把刚刚创建的 socket 加入到监听的 readFd 中。

ANetworkSession 中可创建和处理多种连接类型,包括 RSTP/UDP/TCP 多种 C/S 模式。

参考 https://www.jianshu.com/p/3709f6b0734a

上一篇:# python 常用库


下一篇:PodfileKit summarizes the common iOS (Swift) third-party frameworks on GitHub, and classifies the fr