RTSP server
由 Wi-Fi Display 协议可知,在建立 P2P 连接后,将启动RTSP会话进行双方能力的交互,即M1~M7七个消息的交互。
frameworks/base/media/java/android/media/RemoteDisplay.java
|-- listen()
在进行参数检查后,首先创建一个 RemoteDisplay 对象,接着调用 startListening 的方法,该方法将调用 JNI 的 nativeListen 方法,此处有个 mGuard 是 CloseGuard 对象,是一种用于显示释放资源的机制。
frameworks/base/core/jni/android_media_RemoteDisplay.cpp
|-- nativeListen()
从 ServiceManager 中获取 MediaPlayerService 的 Bpbinder 引用,然后采用类似于设计模式中的包装模式,由传入的第二个参数 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 中,接着调用到 MediaPlayerService 的 listenForRemoteDisplay 方法去监听 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()
在权限检查之后,创建一个 RemoteDisplay 的 C++ 对象。
这里看RemoteDisplay.cpp文件。RemoteDisplay继承于BnRemoteDisplay,并实现BnRemoteDisplay中的一些方法,有兴趣的可以去看一下IRemoteDisplay的实现。
frameworks/av/media/libmediaplayerservice/RemoteDisplay.cpp
|-- Class RemoteDisplay
RemoteDisplay 代码简单,但是包含内容丰富。主要有三个重要元素:
-
ALooper
ALooper 中会创建一个 Thread ,并且不断的通过Looper 循环去接收消息,并将消息 dispatch 给 WifiDisplaySource 的 onMessageReceived 方法去处理。
-
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 类型,包含了长、宽、帧率、隔行视频、profile 和 H.264 level 。然后在构造函数中,将 mResolutionEnabled[] 数组全部置0, mResolutionEnabled 数组有三个元素,分别对应 CEA、VESA、HH 被选取的位,如果在 mConfigs 数组中相应的格式被选取,就会将 mResolutionEnabled 对应的位置为1,相反取消支持一种格式时,相应的位就被置为0。
frameworks/av/media/libstagefright/wifi-display/VideoFormats.cpp
|-- Class VideoFormats
|-- setNativeResolution()
|-- setResolutionEnabled()
设置 mResolutionEnabled 和 mConfigs 中的相应的值,由默认形参我们知道,设置 mResolutionEnabled 相应 type 中的对应格式为1,并设置 mConfigs 中的 profile 和 level 值为 CBP 和 Level 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 中对应分辨率的 profile 和 H.264 level 为 CHP 和 Level 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,然后设置一下 reuse 和 no-block 属性,接着 bind 到指定的 IP 和 port 上,之后开始 listen 。接下来置当前 ANetworkSession 的状态是 LISTENING_RTSP 。然后创建一个 Session 会话对象,在构造函数中会传入 notify 作为参数, notify 是一个 kWhatRTSPNotify 的 AMessag。然后添加到 mSessions 数组当中。接着调用 interrupt 方法,让 ANetworkSession 的 NetworkThread 线程跳出 select 语句,并重新计算 readFd 和 writeFd 用于 select 监听的文件句柄。
interrupt 方法向 pipe 中写入一个空消息,前面我们已经介绍过 threadLoop 了,这里就会把刚刚创建的 socket 加入到监听的 readFd 中。
在 ANetworkSession 中可创建和处理多种连接类型,包括 RSTP/UDP/TCP 多种 C/S 模式。
参考 https://www.jianshu.com/p/3709f6b0734a