轉自:http://blog.csdn.net/lilian0118/article/details/23168531java
這一章中咱們來看Wifi Display鏈接過程的創建,包含P2P的部分和RTSP的部分,首先來大體看一下Wifi Display規範相關的東西。android
HIDC: Human Interface Device Class (遵循HID標準的設備類)
UIBC: User Input Back Channel (UIBC分爲兩種,一種是Generic,包含鼠標、鍵盤等;另外一種是HIDC,HID是一個規範,只有遵循HID的標準,均可以叫作HID設備,包含USB鼠標、鍵盤、藍牙、紅外等)
PES: Packetized Elementary Stream (數字電視基本碼流)
HDCP: High-bandwidth Digital Content Protection (加密方式,用於加密傳輸的MPEG2-TS流)
MPEG2-TS: Moving Picture Experts Group 2 Transport Stream (Wifi display之間傳輸的是MPEG2-TS流)
RTSP: Real-Time Streaming Protocol (Wifi display經過RTSP協議來交互兩邊的能力)
RTP: Real-time Transport Protocol (Wifi display經過RTP來傳輸MPEG2-TS流)
Wi-Fi P2P: Wi-Fi Direct
TDLS: Tunneled Direct Link Setup (另外一種方式創建兩臺設備之間的直連,與P2P相似,但要藉助一臺AP)git
另外一種比較重要的概念是在Wifi Display中分爲Source和Sink兩種角色,以下圖。Source是用於encode並輸出TS流;Sink用於decode並顯示TS流。至關於Server/Client架構中,Source就是Server,用於提供服務;Sink就是Client。固然,咱們這篇文章主要介紹在Android上Wifi display Source的流程。設計模式
從上面的架構圖咱們能夠看到,Wifi display是創建在TCP/UDP上面的應用層協議,L2鏈路層是經過P2P和TDLS兩種方式創建,TDLS是optional的。在L2層創建鏈接後,Source就會在一個特定的port上listen,等待client的TCP鏈接。當與Client創建了TCP鏈接後,就會有M1~M7七個消息的交互,用戶獲取對方設備的能力,包括視頻編碼能力、Audio輸出能力、是否支持HDCP加密等等。在獲取這些能力以後,Source就會選擇一種視頻編碼格式以及Audio格式用於此次會話當中。當一個RTSP會話創建後,雙方就會決定出用於傳輸TS流的RTP port,RTP協議是基於UDP的。當這些都準備好後,Sink設備就會發送M7消息,也就是Play給Source,雙方就能夠開始傳輸數據了。數組
關於M1~M7是什麼,咱們後面再來介紹。首先咱們來介紹在Android WifiDisplay中如何創建P2P的鏈接。網絡
WifiDisplay之P2P的創建
經過咱們之間關於Wifi display的service啓動以及enable的分析,咱們知道當掃描到可用的設備後,就會顯示在WifiDisplaySettings這個頁面上,當咱們選擇其中一個後,就會開始P2P的創建了,首先到WifiDisplaySettings中的代碼分析:
- private void pairWifiDisplay(WifiDisplay display) {
- if (display.canConnect()) {
- mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
- }
- }
WifiDisplaySettings經過AIDL調用到DisplayManagerService的connectWifiDisplay方法,關於AIDL的調用過程這裏不講了,直接到DisplayManagerService的connectWifiDisplay方法來看:
- public void connectWifiDisplay(String address) {
- if (address == null) {
- throw new IllegalArgumentException("address must not be null");
- }
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CONFIGURE_WIFI_DISPLAY,
- "Permission required to connect to a wifi display");
-
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mSyncRoot) {
- if (mWifiDisplayAdapter != null) {
- mWifiDisplayAdapter.requestConnectLocked(address);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
首先作參數的檢查,即MAC地址不能爲空,而後作權限檢查,調用這個方法的application必需要在manifest中聲明有CONFIGURE_WIFI_DISPLAY權限,最後直接調用WifiDisplayAdapter的requestConnectLocked方法:
- public void requestConnectLocked(final String address) {
- if (DEBUG) {
- Slog.d(TAG, "requestConnectLocked: address=" + address);
- }
-
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- if (mDisplayController != null) {
- mDisplayController.requestConnect(address);
- }
- }
- });
- }
這裏比較簡單,直接調用WifiDisplayController的requestConnect方法。前面都是直接的調用,最終作事情的仍是WifiDisplayController。
- public void requestConnect(String address) {
- for (WifiP2pDevice device : mAvailableWifiDisplayPeers) {
- if (device.deviceAddress.equals(address)) {
- connect(device);
- }
- }
- }
-
- private void connect(final WifiP2pDevice device) {
- if (mDesiredDevice != null
- && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
- if (DEBUG) {
- Slog.d(TAG, "connect: nothing to do, already connecting to "
- + describeWifiP2pDevice(device));
- }
- return;
- }
-
- if (mConnectedDevice != null
- && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
- && mDesiredDevice == null) {
- if (DEBUG) {
- Slog.d(TAG, "connect: nothing to do, already connected to "
- + describeWifiP2pDevice(device) + " and not part way through "
- + "connecting to a different device.");
- }
- return;
- }
-
- if (!mWfdEnabled) {
- Slog.i(TAG, "Ignoring request to connect to Wifi display because the "
- +" feature is currently disabled: " + device.deviceName);
- return;
- }
-
- mDesiredDevice = device;
- mConnectionRetriesLeft = CONNECT_MAX_RETRIES;
- updateConnection();
- }
requestConnect先從mAvaiableWifiDsiplayPeers中經過Mac地址找到全部鏈接的WifiP2pDevice,而後調用connect方法,在connect方法中會作一系列的判斷,看首先是否有正在鏈接中或者斷開中的設備,若是有就直接返回;再看有沒有已經鏈接上的設備,若是有,也直接返回,而後賦值mDesiredDevice爲此次要鏈接的設備,最後調用updateConnection來更新鏈接狀態併發起鏈接。updateConnection的代碼比較長,咱們分段來分析:
- private void updateConnection() {
- n style="white-space:pre"> </span>
- updateScanState();
-
- n style="white-space:pre"> </span>
- if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
-
- }
-
-
- if (mDisconnectingDevice != null) {
- return;
- }
- if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
-
- }
-
-
- if (mCancelingDevice != null) {
- return;
- }
- if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
-
- }
-
-
- if (mDesiredDevice == null) {
-
- }
-
-
- if (mConnectedDevice == null && mConnectingDevice == null) {
- Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
-
- mConnectingDevice = mDesiredDevice;
- WifiP2pConfig config = new WifiP2pConfig();
- WpsInfo wps = new WpsInfo();
- if (mWifiDisplayWpsConfig != WpsInfo.INVALID) {
- wps.setup = mWifiDisplayWpsConfig;
- } else if (mConnectingDevice.wpsPbcSupported()) {
- wps.setup = WpsInfo.PBC;
- } else if (mConnectingDevice.wpsDisplaySupported()) {
- wps.setup = WpsInfo.KEYPAD;
- } else {
- wps.setup = WpsInfo.DISPLAY;
- }
- config.wps = wps;
- config.deviceAddress = mConnectingDevice.deviceAddress;
- config.groupOwnerIntent = WifiP2pConfig.MIN_GROUP_OWNER_INTENT;
-
- WifiDisplay display = createWifiDisplay(mConnectingDevice);
- advertiseDisplay(display, null, 0, 0, 0);
-
- final WifiP2pDevice newDevice = mDesiredDevice;
- mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
- @Override
- public void onSuccess() {
- Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
-
- mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
- }
-
- @Override
- public void onFailure(int reason) {
- if (mConnectingDevice == newDevice) {
- Slog.i(TAG, "Failed to initiate connection to Wifi display: "
- + newDevice.deviceName + ", reason=" + reason);
- mConnectingDevice = null;
- handleConnectionFailure(false);
- }
- }
- });
- return;
- }<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
這段函數比較長,咱們先看咱們須要的,剩下的後面再來分析。首先賦值給mConnectingDevice表示當前正在鏈接的設備,而後構造一個WifiP2pConfig對象,這個對象包含此次鏈接的設備的Mac地址、wps方式以及咱們本身的GROUP_OWNER intent值,而後調用advertieseDisplay方法來通知WifiDisplayAdapter相關狀態的改變,WifiDisplayAdapter會發送相應的broadcast出來,這是WifiDisplaySettings能夠接收這些broadcast,而後在UI上更新相應的狀態。關於advertieseDisplay的實現,咱們後面再來分析。
接着看updateConnection,調用WifiP2pManager的connect方法去實現兩臺設備的P2P鏈接,具體過程能夠參考前面介紹的P2P鏈接的文章。這裏的onSuccess()並非表示P2P已經創建成功,而只是表示這個發送命令到wpa_supplicant成功,因此在這裏設置了一個鏈接超時的timeout,爲30秒。當鏈接成功後,會發送WIFI_P2P_CONNECTION_CHANGED_ACTION的廣播出來,接着回到WifiDisplayController看如何處理鏈接成功的broadcast:
- } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
- NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
- WifiP2pManager.EXTRA_NETWORK_INFO);
- if (DEBUG) {
- Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="
- + networkInfo);
- }
-
- handleConnectionChanged(networkInfo);
-
- private void handleConnectionChanged(NetworkInfo networkInfo) {
- mNetworkInfo = networkInfo;
- if (mWfdEnabled && networkInfo.isConnected()) {
- if (mDesiredDevice != null || mWifiDisplayCertMode) {
- mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
- @Override
- public void onGroupInfoAvailable(WifiP2pGroup info) {
- if (DEBUG) {
- Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));
- }
-
- if (mConnectingDevice != null && !info.contains(mConnectingDevice)) {
- Slog.i(TAG, "Aborting connection to Wifi display because "
- + "the current P2P group does not contain the device "
- + "we expected to find: " + mConnectingDevice.deviceName
- + ", group info was: " + describeWifiP2pGroup(info));
- handleConnectionFailure(false);
- return;
- }
-
- if (mDesiredDevice != null && !info.contains(mDesiredDevice)) {
- disconnect();
- return;
- }
-
- if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
- Slog.i(TAG, "Connected to Wifi display: "
- + mConnectingDevice.deviceName);
-
- mHandler.removeCallbacks(mConnectionTimeout);
- mConnectedDeviceGroupInfo = info;
- mConnectedDevice = mConnectingDevice;
- mConnectingDevice = null;
- updateConnection();
- }
- }
- });
- }
- }
當WifiDisplayController收到WIFI_P2P_CONNECTION_CHANGED_ACTION廣播後,會調用handleConnectionChanged來獲取當前P2P Group相關的信息,若是獲取到的P2P Group信息裏面沒有mConnectingDevice或者mDesiredDevice的信息,則表示鏈接出錯了,直接退出。若是當前鏈接信息與前面設置的mConnectingDevice一直,則表示鏈接P2P成功,這裏首先會移除前面設置的鏈接timeout的callback,而後設置mConnectedDevice爲當前鏈接的設備,並設置mConnectingDevice爲空,最後調用updateConnection來更新鏈接狀態信息。咱們又回到updateConnection這個函數了,但此次進入的分支與以前鏈接請求的分支又不一樣了,咱們來看代碼:
- private void updateConnection() {
-
- updateScanState();
-
-
- if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
-
- }
-
-
- if (mDisconnectingDevice != null) {
- return;
- }
- if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
-
- }
-
-
- if (mCancelingDevice != null) {
- return;
- }
- if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
-
- }
-
-
- if (mDesiredDevice == null) {
-
- }
-
-
- if (mConnectedDevice == null && mConnectingDevice == null) {
-
- }
-
-
- if (mConnectedDevice != null && mRemoteDisplay == null) {
- Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
- if (addr == null) {
- Slog.i(TAG, "Failed to get local interface address for communicating "
- + "with Wifi display: " + mConnectedDevice.deviceName);
- handleConnectionFailure(false);
- return;
- }
-
- mWifiP2pManager.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE);
-
- final WifiP2pDevice oldDevice = mConnectedDevice;
- final int port = getPortNumber(mConnectedDevice);
- final String iface = addr.getHostAddress() + ":" + port;
- mRemoteDisplayInterface = iface;
-
- Slog.i(TAG, "Listening for RTSP connection on " + iface
- + " from Wifi display: " + mConnectedDevice.deviceName);
-
- mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
- @Override
- public void onDisplayConnected(Surface surface,
- int width, int height, int flags, int session) {
- if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) {
- Slog.i(TAG, "Opened RTSP connection with Wifi display: "
- + mConnectedDevice.deviceName);
- mRemoteDisplayConnected = true;
- mHandler.removeCallbacks(mRtspTimeout);
-
- if (mWifiDisplayCertMode) {
- mListener.onDisplaySessionInfo(
- getSessionInfo(mConnectedDeviceGroupInfo, session));
- }
-
- final WifiDisplay display = createWifiDisplay(mConnectedDevice);
- advertiseDisplay(display, surface, width, height, flags);
- }
- }
-
- @Override
- public void onDisplayDisconnected() {
- if (mConnectedDevice == oldDevice) {
- Slog.i(TAG, "Closed RTSP connection with Wifi display: "
- + mConnectedDevice.deviceName);
- mHandler.removeCallbacks(mRtspTimeout);
- disconnect();
- }
- }
-
- @Override
- public void onDisplayError(int error) {
- if (mConnectedDevice == oldDevice) {
- Slog.i(TAG, "Lost RTSP connection with Wifi display due to error "
- + error + ": " + mConnectedDevice.deviceName);
- mHandler.removeCallbacks(mRtspTimeout);
- handleConnectionFailure(false);
- }
- }
- }, mHandler);
-
-
- int rtspTimeout = mWifiDisplayCertMode ?
- RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS;
-
- mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000);
- }
- }
到這裏P2P的鏈接就算創建成功了,接下來就是RTSP的部分了
WifiDisplay之RTSP server的建立
這裏首先設置MiracastMode,博主認爲這部分應該放在enable WifiDisplay時,不知道Google爲何放在這裏? 而後從GroupInfo中取出對方設備的IP地址,利用默認的CONTROL PORT構建mRemoteDisplayInterface,接着調用RemoteDisplay的listen方法去listen指定的IP和端口上面的TCP鏈接請求。最後會設置Rtsp的鏈接請求的timeout,當用於Miracast認證時是120秒,正常的使用中是30秒,若是在這麼長的時間內沒有收到Sink的TCP請求,則表示失敗了。下面來看RemoteDisplay的listen的實現:
- public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
- if (iface == null) {
- throw new IllegalArgumentException("iface must not be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("listener must not be null");
- }
- if (handler == null) {
- throw new IllegalArgumentException("handler must not be null");
- }
-
- RemoteDisplay display = new RemoteDisplay(listener, handler);
- display.startListening(iface);
- return display;
- }
這裏首先進行參數的檢查,而後建立一個RemoteDisplay對象(這裏不能直接建立RemoteDisplay對象,由於它的構造函數是private的),接着調用RemoteDisplay的startListening方法:
- private void startListening(String iface) {
- mPtr = nativeListen(iface);
- if (mPtr == 0) {
- throw new IllegalStateException("Could not start listening for "
- + "remote display connection on \"" + iface + "\"");
- }
- mGuard.open("dispose");
- }
nativeListen會調用JNI中的實現,相關代碼在android_media_RemoteDisplay.cpp中。注意上面的mGuard是CloseGuard對象,是一種用於顯示釋放一些資源的機制。
- static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
- ScopedUtfChars iface(env, ifaceStr);
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
- sm->getService(String16("media.player")));
- if (service == NULL) {
- ALOGE("Could not obtain IMediaPlayerService from service manager");
- return 0;
- }
-
- sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
- sp<IRemoteDisplay> display = service->listenForRemoteDisplay(
- client, String8(iface.c_str()));
- if (display == NULL) {
- ALOGE("Media player service rejected request to listen for remote display '%s'.",
- iface.c_str());
- return 0;
- }
-
- NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
- return reinterpret_cast<jint>(wrapper);
- }
上面的代碼中先從ServiceManager中獲取MediaPlayerService的Bpbinder引用,而後由傳入的第二個參數remoteDisplayObj,也就是RemoteDisplay對象構造一個NativeRemoteDisplayClient,在framework中,咱們常常看到像這樣的用法,相似於設計模式中的包裝模式,例如在framework中對Java層的BnBinder也是作了一層封裝JavaBBinder。在NativeRemoteDisplayClient中經過JNI的反向調用,就能夠直接回調RemoteDisplay中的一些函數,實現回調方法了,下面來看它的實現:
- class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
- public:
- NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
- mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
- }
-
- protected:
- ~NativeRemoteDisplayClient() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
- }
-
- public:
- virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,
- uint32_t width, uint32_t height, uint32_t flags, uint32_t session) {
- env->CallVoidMethod(mRemoteDisplayObjGlobal,
- gRemoteDisplayClassInfo.notifyDisplayConnected,
- surfaceObj, width, height, flags, session);
- }
-
- virtual void onDisplayDisconnected() {
-
- }
-
- virtual void onDisplayError(int32_t error) {
-
- }
-
- private:
- jobject mRemoteDisplayObjGlobal;
-
- static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
-
- }
- }
- };
在NativeRemoteDisplayClient的構造函數中,把RemoteDisplay對象先保存到mRemoteDisplayObjGlobal中,能夠看到上面主要實現了三個回調函數,onDisplayConnected、onDisplayDisconnected、onDisplayError,這三個回調函數對應到RemoteDisplay類的notifyDisplayConnected、notifyDisplayDisconnected和notifyDisplayError三個方法。接着回到nativeListen中,接着會調用MediaPlayerService的listenForRemoteDisplay方法去監聽socket鏈接,這個方法是返回一個RemoteDisplay對象,固然通過binder的調用,最終返回到nativeListen的是BpRemoteDisplay對象,而後會由這個BpRemoteDisplay對象構造一個NativeRemoteDisplay對象並把它的指針地址返回給上層RemoteDisplay使用。
- class NativeRemoteDisplay {
- public:
- NativeRemoteDisplay(const sp<IRemoteDisplay>& display,
- const sp<NativeRemoteDisplayClient>& client) :
- mDisplay(display), mClient(client) {
- }
-
- ~NativeRemoteDisplay() {
- mDisplay->dispose();
- }
-
- void pause() {
- mDisplay->pause();
- }
-
- void resume() {
- mDisplay->resume();
- }
-
- private:
- sp<IRemoteDisplay> mDisplay;
- sp<NativeRemoteDisplayClient> mClient;
- };
來看一下這時Java層的RemoteDisplay和Native層RemoteDisplay之間的關係:
WifiDisplayController經過左邊的一條線路關係去控制WifiDisplaySource,而WifiDisplaySource又經過右邊一條線路關係去回調WifiDisplayController的一些方法。
接着來看MediaPlayerService的listenForRemoteDisplay方法:
- sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
- const sp<IRemoteDisplayClient>& client, const String8& iface) {
- if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
- return NULL;
- }
-
- return new RemoteDisplay(client, iface.string());
- }
首先進行權限的檢查,而後建立一個RemoteDisplay對象(注意如今已經在C++層了),這裏看RemoteDisplay.cpp文件。RemoteDisplay繼承於BnRemoteDisplay,並實現BnRemoteDisplay中的一些方法,有興趣的能夠去看一下IRemoteDisplay的實現。接下來來看RemoteDisplay的構造函數:
- RemoteDisplay::RemoteDisplay(
- const sp<IRemoteDisplayClient> &client,
- const char *iface)
- : mLooper(new ALooper),
- mNetSession(new ANetworkSession) {
- mLooper->setName("wfd_looper");
-
- mSource = new WifiDisplaySource(mNetSession, client);
- mLooper->registerHandler(mSource);
-
- mNetSession->start();
- mLooper->start();
-
- mSource->start(iface);
- }
RemoteDisplay類包含三個比較重要的元素:ALooper、ANetworkSession、WifiDisplaySource。首先來看一下在Native層的類圖:
ALooper中會建立一個Thread,而且不斷的進行Looper循環去收消息,並dispatch給WifiDisplaySource去處理消息。首先來看它的構造函數和setName以及registerHandler這三個方法:
- ALooper::ALooper()
- : mRunningLocally(false) {
- }
-
- void ALooper::setName(const char *name) {
- mName = name;
- }
-
- ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
- return gLooperRoster.registerHandler(this, handler);
- }
這三個方法都比較簡單,咱們看LooperRoster的registerHandler方法:
- ALooper::handler_id ALooperRoster::registerHandler(
- const sp<ALooper> looper, const sp<AHandler> &handler) {
- Mutex::Autolock autoLock(mLock);
-
- if (handler->id() != 0) {
- CHECK(!"A handler must only be registered once.");
- return INVALID_OPERATION;
- }
-
- HandlerInfo info;
- info.mLooper = looper;
- info.mHandler = handler;
- ALooper::handler_id handlerID = mNextHandlerID++;
- mHandlers.add(handlerID, info);
-
- handler->setID(handlerID);
-
- return handlerID;
- }
這裏爲每個註冊的AHandler分配一個handlerID,而且把註冊的AHandler保存在mHandlers列表中,後面使用時,就能夠快速的經過HandlerID找到對應的AHandler以及ALooper了。注意這裏HandlerInfo結構中的mLooper和mHander都是是wp,是一個弱引用,在使用中必須調用其promote方法獲取sp指針才能使用。再回到RemoteDisplay的構造函數中看ALooper的start方法:
- status_t ALooper::start(
- bool runOnCallingThread, bool canCallJava, int32_t priority) {
- if (runOnCallingThread) {
-
- }
-
- Mutex::Autolock autoLock(mLock);
-
- mThread = new LooperThread(this, canCallJava);
-
- status_t err = mThread->run(
- mName.empty() ? "ALooper" : mName.c_str(), priority);
- if (err != OK) {
- mThread.clear();
- }
-
- return err;
- }
這裏的runOnCallingThread會根據默認形參爲false,因此會新建一個LooperThread來不斷的作循環,LooperThread是繼承於Thread,並實現它的readyToRun和threadLoop方法,在threadLoop方法中去調用ALooper的loop方法,代碼以下:
- virtual bool threadLoop() {
- return mLooper->loop();
- }
-
- ALooper::loop() {
- Event event;
-
- {
- Mutex::Autolock autoLock(mLock);
-
- if (mEventQueue.empty()) {
- mQueueChangedCondition.wait(mLock);
- return true;
- }
- int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
- int64_t nowUs = GetNowUs();
-
- if (whenUs > nowUs) {
- int64_t delayUs = whenUs - nowUs;
- mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
-
- return true;
- }
-
- event = *mEventQueue.begin();
- mEventQueue.erase(mEventQueue.begin());
- }
-
- gLooperRoster.deliverMessage(event.mMessage);
-
- return true;
在loop方法中,不斷的從mEventQueue取出消息,並dispatch給LooperRoster處理,mEventQueue是一個list鏈表,其元素都是Event結構,Event結構又包含消息處理的時間以及消息自己AMessage。再來看ALooperRoster的deliverMessage方法:
- void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
- sp<AHandler> handler;
-
- {
- Mutex::Autolock autoLock(mLock);
-
- ssize_t index = mHandlers.indexOfKey(msg->target());
-
- if (index < 0) {
- ALOGW("failed to deliver message. Target handler not registered.");
- return;
- }
-
- const HandlerInfo &info = mHandlers.valueAt(index);
- handler = info.mHandler.promote();
-
- if (handler == NULL) {
- ALOGW("failed to deliver message. "
- "Target handler %d registered, but object gone.",
- msg->target());
-
- mHandlers.removeItemsAt(index);
- return;
- }
- }
-
- handler->onMessageReceived(msg);
- }
這裏首先經過AMessage的target找到須要哪一個AHandler處理,而後調用這個AHandler的onMessageReceived去處理這個消息。注意前面的info.mHandler.promote()用於當前AHandler的強引用指針,也能夠用來判斷當前AHandler是否還存活在。由前面的知識咱們知道,這裏會調用到WifiDisplaySource的onMessageReceived方法,至於這些消息如何被處理,咱們後面再來分析。再回到RemoteDisplay的構造函數中,ANetworkSession用於處理與網絡請求相關的工做,好比建立socket,從socket中收發數據,固然這些工做都是由WifiDisplaySource控制的,咱們先來看ANetworkSession的構造方法和start方法:
- ANetworkSession::ANetworkSession()
- : mNextSessionID(1) {
- mPipeFd[0] = mPipeFd[1] = -1;
- }
-
- status_t ANetworkSession::start() {
- if (mThread != NULL) {
- return INVALID_OPERATION;
- }
-
- int res = pipe(mPipeFd);
- if (res != 0) {
- mPipeFd[0] = mPipeFd[1] = -1;
- return -errno;
- }
-
- mThread = new NetworkThread(this);
-
- status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);
-
- if (err != OK) {
- mThread.clear();
-
- close(mPipeFd[0]);
- close(mPipeFd[1]);
- mPipeFd[0] = mPipeFd[1] = -1;
-
- return err;
- }
-
- return OK;
- }
在start方法中,首先建立一個管道,這裏建立的管理主要用於讓ANetworkSession不斷的作select循環,當有事務要處理時,就從select中跳出來處理,咱們後面會分析到具體的代碼。接着建立一個NetworkThread,NetworkThread也是繼承於Thread,並實現threadLoop方法,在threadLoop方法中只是簡單的調用ANetworkSession的threadLoop方法,咱們來分析threadLoop方法:
- void ANetworkSession::threadLoop() {
- fd_set rs, ws;
- FD_ZERO(&rs);
- FD_ZERO(&ws);
-
- FD_SET(mPipeFd[0], &rs);
- int maxFd = mPipeFd[0];
-
- {
- Mutex::Autolock autoLock(mLock);
-
- for (size_t i = 0; i < mSessions.size(); ++i) {
- const sp<Session> &session = mSessions.valueAt(i);
-
- int s = session->socket();
-
- if (s < 0) {
- continue;
- }
-
- if (session->wantsToRead()) {
- FD_SET(s, &rs);
- if (s > maxFd) {
- maxFd = s;
- }
- }
-
- if (session->wantsToWrite()) {
- FD_SET(s, &ws);
- if (s > maxFd) {
- maxFd = s;
- }
- }
- }
- }
-
- int res = select(maxFd + 1, &rs, &ws, NULL, NULL
-
- if (res == 0) {
- return;
- }
-
- if (res < 0) {
- if (errno == EINTR) {
- return;
- }
-
- ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
- return;
- }
-
- }
這個函數比較長,咱們分段來看,首先看select前半段部分,首先將mPipeFd[0]做爲select監聽的一個fd。而後循環的從mSessions中取出各個子Session(Session即爲一個回話,在RTSP中當雙方鏈接好TCP鏈接,並交互完Setup之後,就表示一個回話創建成功了,在RTSP中,能夠在一對Server & Client之間創建多個回話,用於傳輸不一樣的數據),並經過socket類型添加到ReadFd和WirteFd中,最後調用select去等待是否有可讀或者可寫的事件發生。mSessions是一個KeyedVector,保存全部的Session及其SessionID,方便查找。關於Session什麼時候建立,如何建立,咱們後面再來分析。
接着回到RemoteDisplay的構造函數,再來分析WifiDisplaySource,WifiDisplaySource繼承於AHandler,並實現其中的onMessageReceived方法用於處理消息。先來看WifiDisplaySource的構造函數:
- WifiDisplaySource::WifiDisplaySource(
- const sp<ANetworkSession> &netSession,
- const sp<IRemoteDisplayClient> &client,
- const char *path)
- : mState(INITIALIZED),
- mNetSession(netSession),
- mClient(client),
- mSessionID(0),
- mStopReplyID(0),
- mChosenRTPPort(-1),
- mUsingPCMAudio(false),
- mClientSessionID(0),
- mReaperPending(false),
- mNextCSeq(1),
- mUsingHDCP(false),
- mIsHDCP2_0(false),
- mHDCPPort(0),
- mHDCPInitializationComplete(false),
- mSetupTriggerDeferred(false),
- mPlaybackSessionEstablished(false) {
- if (path != NULL) {
- mMediaPath.setTo(path);
- }
-
- mSupportedSourceVideoFormats.disableAll();
-
- mSupportedSourceVideoFormats.setNativeResolution(
- VideoFormats::RESOLUTION_CEA, 5);
-
-
- mSupportedSourceVideoFormats.enableResolutionUpto(
- VideoFormats::RESOLUTION_CEA, 5,
- VideoFormats::PROFILE_CHP,
- VideoFormats::LEVEL_32);
- }
首先給一些變量出初始化處理,由默認形參咱們知道path爲空。接着去清空VideoFormats中全部的設置,並把1280*720p以上的全部分辨率打開。VideoFormats是用於與Sink回覆的M3做比對用的,能夠快速找出咱們和Sink支持的分辨率以及幀率,做爲回覆M4消息用,也用做後續傳輸TS數據的格式。首先來看VideoFormats的構造函數:
- VideoFormats::VideoFormats() {
- memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
-
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- mResolutionEnabled[i] = 0;
- }
-
- setNativeResolution(RESOLUTION_CEA, 0);
- }
mResolutionTable是按照Wifi Display 規範定義好的一個3*32數組,裏面的元素是config_t類型:
- struct config_t {
- size_t width, height, framesPerSecond;
- bool interlaced;
- unsigned char profile, level;
- };
config_t包含了長、寬、幀率、隔行視頻、profile和H.264 level。而後在構造函數中,對mResolutionEnabled[]數組所有置爲0,mResolutionEnabled數組有三個元素,分別對應CEA、VESA、HH被選取的位,若是在mConfigs數組中相應的格式被選取,就會置mResolutionEnabled對應的位爲1;相反取消支持一種格式時,相應的位就被置爲0。在來看setNativeResolution:
- void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- mNativeType = type;
- mNativeIndex = index;
-
- setResolutionEnabled(type, index);
- }
首先作參數檢查,檢查輸入的type和index是否合法,而後調用setResolutionEnabled去設置mResolutionEnabled和mConfigs中的相應的值:
- void VideoFormats::setResolutionEnabled(
- ResolutionType type, size_t index, bool enabled) {
- CHECK_LT(type, kNumResolutionTypes);
- CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
-
- if (enabled) {
- mResolutionEnabled[type] |= (1ul << index);
- mConfigs[type][index].profile = (1ul << PROFILE_CBP);
- mConfigs[type][index].level = (1ul << LEVEL_31);
- } else {
- mResolutionEnabled[type] &= ~(1ul << index);
- mConfigs[type][index].profile = 0;
- mConfigs[type][index].level = 0;
- }
- }
這裏首先仍是作參數的檢查,由默認形參咱們知道,enable是true,則設置mResolutionEnabled相應type中的對應格式爲1,並設置mConfigs中的profile和level值爲CBP和Level 3.1。這裏設置640*480 p60是由於在Wifi Display規範中,這個格式是必需要強制支持的,在Miracast認證中,這種格式也會被測試到。而後回到WifiDisplaySource的構造函數中,接下來會調用setNativeResolution去設置當前系統支持的默認格式爲1280*720 p30,並調用enableResolutionUpto去將1280*720 p30以上的格式都設置爲支持:
- void VideoFormats::enableResolutionUpto(
- ResolutionType type, size_t index,
- ProfileType profile, LevelType level) {
- size_t width, height, fps, score;
- bool interlaced;
- if (!GetConfiguration(type, index, &width, &height,
- &fps, &interlaced)) {
- ALOGE("Maximum resolution not found!");
- return;
- }
- score = width * height * fps * (!interlaced + 1);
- for (size_t i = 0; i < kNumResolutionTypes; ++i) {
- for (size_t j = 0; j < 32; j++) {
- if (GetConfiguration((ResolutionType)i, j,
- &width, &height, &fps, &interlaced)
- && score >= width * height * fps * (!interlaced + 1)) {
- setResolutionEnabled((ResolutionType)i, j);
- setProfileLevel((ResolutionType)i, j, profile, level);
- }
- }
- }
- }
這裏採用width * height * fps * (!interlaced + 1)的方式去計算一個score值,而後遍歷全部的mResolutionTable中的值去檢查是否計算到的值比當前score要高,若是大於當前score值,就將這種分辨率enable,並設置mConfigs中對應分辨率的profile和H.264 level爲CHP和Level 3.2。到這裏WifiDisplaySource的構造函數分析完了,接着回到RemoteDisplay構造函數中,它會調用WifiDisplaySource的start方法,參數是的"ip:rtspPort":
- status_t WifiDisplaySource::start(const char *iface) {
- CHECK_EQ(mState, INITIALIZED);
-
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("iface", iface);
-
- sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
- }
-
- static status_t PostAndAwaitResponse(
- const sp<AMessage> &msg, sp<AMessage> *response) {
- status_t err = msg->postAndAwaitResponse(response);
-
- if (err != OK) {
- return err;
- }
-
- if (response == NULL || !(*response)->findInt32("err", &err)) {
- err = OK;
- }
-
- return err;
- }
在start函數中,構造一個AMessage,消息種類是kWhatStart,id()返回在ALooperRoster註冊的handlerID值,ALooperRoster經過handlerID值能夠快速找到對應的AHandler,咱們知道,這裏的id()返回WifiDisplaySource這個AHander的id值,這個消息最終也會被WifiDisplaySource的onMessageReceived方法處理。首先來看AMessage的postAndAwaitResponse方法:
- status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
- return gLooperRoster.postAndAwaitResponse(this, response);
- }
這裏直接調用LooperRoster的postAndAwaitResponse方法,這裏比較重要的是gLooperRoster在這裏只是被extern引用:extern ALooperRoster gLooperRoster,其最終的聲明和定義是在咱們前面講到的ALooper中。接着去看LooperRoster的postAndAwaitResponse方法:
- status_t ALooperRoster::postAndAwaitResponse(
- const sp<AMessage> &msg, sp<AMessage> *response) {
- Mutex::Autolock autoLock(mLock);
-
- uint32_t replyID = mNextReplyID++;
-
- msg->setInt32("replyID", replyID);
-
- status_t err = postMessage_l(msg, 0
-
- if (err != OK) {
- response->clear();
- return err;
- }
-
- ssize_t index;
- while ((index = mReplies.indexOfKey(replyID)) < 0) {
- mRepliesCondition.wait(mLock);
- }
-
- *response = mReplies.valueAt(index);
- mReplies.removeItemsAt(index);
-
- return OK;
- }
首先會爲每一個須要reply的消息賦予一個replyID,後面會根據這個replyID去mReplies找到對應的response。再來看postMessage_l的實現:
- status_t ALooperRoster::postMessage_l(
- const sp<AMessage> &msg, int64_t delayUs) {
- ssize_t index = mHandlers.indexOfKey(msg->target());
-
- if (index < 0) {
- ALOGW("failed to post message '%s'. Target handler not registered.",
- msg->debugString().c_str());
- return -ENOENT;
- }
-
- const HandlerInfo &info = mHandlers.valueAt(index);
-
- sp<ALooper> looper = info.mLooper.promote();
-
- if (looper == NULL) {
- ALOGW("failed to post message. "
- "Target handler %d still registered, but object gone.",
- msg->target());
-
- mHandlers.removeItemsAt(index);
- return -ENOENT;
- }
-
- looper->post(msg, delayUs);
-
- return OK;
- }
首先從mHandler數組中找到當前AMessage對應的ALooper,而後調用ALooper的post方法,來看一下實現:
- void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
- Mutex::Autolock autoLock(mLock);
-
- int64_t whenUs;
- if (delayUs > 0) {
- whenUs = GetNowUs() + delayUs;
- } else {
- whenUs = GetNowUs();
- }
-
- List<Event>::iterator it = mEventQueue.begin();
- while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
- ++it;
- }
-
- Event event;
- event.mWhenUs = whenUs;
- event.mMessage = msg;
-
- if (it == mEventQueue.begin()) {
- mQueueChangedCondition.signal();
- }
-
- mEventQueue.insert(it, event);
- }
delayUs用於作延時消息使用,會加上當前時間做爲消息應該被處理的時間。而後依次比較mEventQueue鏈表中的全部消息,並把當前消息插入到比whenUs大的前面一個位置。若是這是mEventQueue中的第一個消息,則發出一個signal通知等待的線程。前面咱們知道在ALooper的loop方法中會循環的從mEventQueue獲取消息並dispatch出去給WifiDisplaySource的onMessageReceived去處理,咱們接着來看這部分的實現。這裏繞這麼大一圈,最後WifiDisplaySource發送的消息仍是給本身處理,主要是爲了避開主線程處理的事務太多,經過消息機制,讓更多的繁雜的活都在Thread中去完成。
- void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- uint32_t replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
- AString iface;
- CHECK(msg->findString("iface", &iface));
-
- status_t err = OK;
-
- ssize_t colonPos = iface.find(":");
-
- unsigned long port;
-
- if (colonPos >= 0) {
- const char *s = iface.c_str() + colonPos + 1;
-
- char *end;
- port = strtoul(s, &end, 10);
-
- if (end == s || *end != '\0' || port > 65535) {
- err = -EINVAL;
- } else {
- iface.erase(colonPos, iface.size() - colonPos);
- }
- } else {
- port = kWifiDisplayDefaultPort;
- }
-
- if (err == OK) {
- if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
- sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
-
- err = mNetSession->createRTSPServer(
- mInterfaceAddr, port, notify, &mSessionID);
- } else {
- err = -EINVAL;
- }
- }
-
- mState = AWAITING_CLIENT_CONNECTION;
-
- sp<AMessage> response = new AMessage;
- response->setInt32("err", err);
- response->postReply(replyID);
- break;
- }
首先經過AMessage獲取到replayID和iface,而後把iface分割成ip和port,分別保存在mInterfaceAddr和port中。在調用ANetSession的createRTSPServer去建立一個RTSP server,最後構造一個response對象並返回。咱們先來看createRTSPServer方法:
- status_t ANetworkSession::createRTSPServer(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> ¬ify, int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateRTSPServer,
- &addr,
- port,
- NULL
- 0
- notify,
- sessionID);
- }
-
- status_t ANetworkSession::createClientOrServer(
- Mode mode,
- const struct in_addr *localAddr,
- unsigned port,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID) {
- Mutex::Autolock autoLock(mLock);
-
- *sessionID = 0;
- status_t err = OK;
- int s, res;
- sp<Session> session;
-
- s = socket(
- AF_INET,
- (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
- 0);
-
- if (s < 0) {
- err = -errno;
- goto bail;
- }
-
- if (mode == kModeCreateRTSPServer
- || mode == kModeCreateTCPDatagramSessionPassive) {
- const int yes = 1;
- res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- }
-
- err = MakeSocketNonBlocking(s);
-
- if (err != OK) {
- goto bail2;
- }
-
- struct sockaddr_in addr;
- memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
- addr.sin_family = AF_INET;
- } else if (localAddr != NULL) {
- addr.sin_addr = *localAddr;
- addr.sin_port = htons(port);
-
- res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
-
- if (res == 0) {
- if (mode == kModeCreateRTSPServer
- || mode == kModeCreateTCPDatagramSessionPassive) {
- res = listen(s, 4);
- } else {
-
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
-
- Session::State state;
- switch (mode) {
-
- case kModeCreateRTSPServer:
- state = Session::LISTENING_RTSP;
- break;
-
- default:
- CHECK_EQ(mode, kModeCreateUDPSession);
- state = Session::DATAGRAM;
- break;
- }
-
- session = new Session(
- mNextSessionID++,
- state,
- s,
- notify);
-
-
- mSessions.add(session->sessionID(), session);
-
- interrupt();
-
- *sessionID = session->sessionID();
-
- goto bail;
-
- bail2:
- close(s);
- s = -1;
-
- bail:
- return err;
- }
createRTSPServer直接調用createClientOrServer,第一個參數是kModeCreateRTSPServer表示要建立一個RTSP server。createClientOrServer的代碼比較長,上面是精簡後的代碼,其它沒看到的代碼咱們之後遇到的過程當中再來分析。上面的代碼中首先建立一個socket,而後設置一下socket的reuse和no-block屬性,接着bind到指定的IP和port上,而後再此socket上開始listen。接下來置當前ANetworkSession的狀態是LISTENING_RTSP。而後建立一個Session會話對象,在構造函數中會傳入notify做爲參數,notify是一個kWhatRTSPNotify的AMessag,後面會看到如何使用它。而後添加到mSessions數組當中。接着調用interrupt方法,讓ANetworkSession的NetworkThread線程跳出select語句,並從新計算readFd和writeFd用於select監聽的文件句柄。
- void ANetworkSession::interrupt() {
- static const char dummy = 0;
-
- ssize_t n;
- do {
- n = write(mPipeFd[1], &dummy, 1);
- } while (n < 0 && errno == EINTR);
-
- if (n < 0) {
- ALOGW("Error writing to pipe (%s)", strerror(errno));
- }
- }
interrupt方法向pipe中寫入一個空消息,前面咱們已經介紹過threadLoop了,這裏就會把剛剛建立的socket加入到監聽的readFd中。到這裏,關於WifiDisplay鏈接的創建就講完了,後面會再從收到Sink的TCP鏈接請求開始講起。最後貼一份從WifiDisplaySettings到ANetworkSession如何建立socket的時序圖: