1. WifiDisplay簡介
Wi-Fi Display經常和Miracast聯系在一起。實際上,Miracast是Wi-Fi聯盟(Wi-Fi Alliance)對支持Wi-Fi Display功能的設備的認證名稱。通過Miracast認證的設備將在最大程度內保持對Wi-Fi Display功能的支持和兼容。
Miracast的Android實現涉及到系統的多個模塊,包括:
原因很明顯,因為Miracast本身就牽扯到RTP/RTSP及相應的編解碼技術。
SurfaceFlinger的作用是將各層UI數據混屏并投遞到顯示設備中去顯示。現在,SurfaceFlinger將支持多個顯示設備。而支持Miracast的遠端設備也做為一個獨立的顯示設備存在于系統中。
3. WindowManagerService及相關模塊
WindowManagerService用于管理系統中各個UI層的位置和屬性。由于并非所有的UI層都會通過Miracast投遞到遠端設備上。例如手機中的視頻可投遞到遠端設備上去顯示,但假如在播放過程中,突然彈出一個密碼輸入框(可能是某個后臺應用程序發起的),則這個密碼輸入框就不能投遞到遠端設備上去顯示。所以,WindowManagerService也需要修改以適應Miracast的需要。
4. DisplayManagerService及相關模塊
DisplayManagerService服務是Android 4.2新增的,用于管理系統中所有的Display設備。
5. WifiService及相關模塊
WifiDisplay協議的實現建立在WifiP2P的基礎上,其中涉及的Wifi技術包括WiFi-Direct(WiFi P2P)、Wi-Fi Protected Setup(Wifi網絡自動配置及添加網絡)、11n/WMM/WPA2(11n就是802.11n協議,它將11a和11g提供的Wi-Fi傳輸速率從56Mbps提升到300甚至600Mbps。WMM是Wi-Fi Multimedia的縮寫,是一種針對實時視音頻數據的QoS服務。而WPA2意為Wi-Fi Protected Acess第二版,主要用來給傳輸的數據進行加密保護)。
圖1 給出了WFD涉及的技術及協議框圖,基于WifiP2P網絡技術,利用RTSP作為音頻及視頻流控制協議,涉及了流媒體的傳輸、控制、加密、解密、編碼及解碼等技術流程。
WFD中涉及的技術層面比較多,相關的協議也比較多,包括了WIFI P2P技術、RTSP及RTP技術、流媒體技術以及音視頻編解碼相關的技術,如果要對WFD有比較深入的了解,還需要花費較多的時間去研究相關的技術細節。
圖1 WFD涉及的技術及協議框圖
2. WifiDisplay協議流程
建立WifiDisplay主要步驟如下:
1. WFD Device Discovery(WFD設備發現)
2. WFD Service Discovery (Optional)(WFD服務發現(可選))
3. Device Selection(設備選擇)
4. WFD Connection Setup(WFD連接)
5. WFD Capability Negotiation(WFD能力協商)
6. WFD Session Establishment(WFD會話建立)
7. User Input Back Channel Setup (Optional)(UIBC反向控制)
8. Link Content Protection Setup (Optional)(內容保護,即數據加密)
9. Payload Control(負載控制)
10. WFD Source and WFD Sink standby (Optional)
11. WFD Session Teardown(會話終止)
WFD設備通過wifiP2P連接后,Sink端與Source端建立TCP連接,Sink端為Client而Source端為Server。默認端口為7236,執行的協議為RTSP協議。建立連接后進行RTSP協商。步驟6,協商成功后建立會話;步驟7,UIBC通道建立,用于Sink端反向控制Source端,該步驟為可選實現;步驟8,對與傳輸的內容做加密保護(HDCP),步驟9,開始音頻及視頻流的傳輸與控制,Payload Control:傳輸過程中,設備可根據無線信號的強弱,甚至設備的電量狀況來動態調整傳輸數據和格式。可調整的內容包括壓縮率,視音頻格式,分辨率等內容。步驟11,會話終止。
圖 2 會話建立及協商過程圖
RTSP M1和M2主要協商Source和Sink都支持的RTSP methods。
RTSP M3和M4主要協商Source和Sink在會話中使用的參數。
圖3 RTSP協議控制圖
當RTSP M7的請求和響應消息成功交換完成,WFD Source及WFD Sink之間就建立了會話。
RTSP協議控制中主要有以下幾種狀態SETUP、PLAY、PAUSE、TEARDOWN
通過下面命令抓取了WifiDisplay相關的協議包,主要是RTSP控制流相關的協議包。
tcpdump -i any -w /savePath
具體的協議包相關的內容如圖4所示,協議中相關的流程及步驟和圖1、2中的交互流程是一致的,具體包括以下幾個主要步驟OPTIONS、GET_PARAMETER、SET_PARAMETER、 SETUP、PLAY、TEARDOWN等,這些都是RTSP中相關的協議內容。
當Source與Sink設備完成PLAY的交互后,Source端便開始傳輸音頻及視頻流給Sink端,Sink端作為被動接收端,只需要在P2P interface的19000(默認的RTP數據傳輸端口)綁定監聽接收來自Source端的數據流對相關的音視頻流做處理即可。圖5給出了音視頻流的協議包,可以看到音視頻的傳輸通過MPEG TS、MPEG PES等相關協議作為傳輸載體。
圖4 完整RTSP協商流程,協商的內容主要是視頻流的控制方法及支持的音頻及視頻格式
圖5 完成協商后開始傳輸音頻及視頻流,即TS包及PES包
對于WifiDisplay會話管理有以下模型可供參考,該結構大致分為四個層次, UI、 Session Policy Management、協議實現層及基于Wifi的網絡傳輸層。在協議實現層中主要分為幾個模塊WFD Ddiscovery、WFD Link Establishment、 UIBC、Capability Negotiation、Session/Stream Control等。
圖6 WFD設備會話管理的模型
圖7 音頻及視頻流控制模型
實時流協議RTSP是一個應用層協議,用于控制具有實時特性的數據(例如多媒體流)的傳送。RTSP協議一般與RTP/RTCP和RSVP等底層協議一起協同工作,提供基于Internet的整套的流服務。它可以選擇發送通道(例如:UDP、組播UDP和TCP)和基于RTP的發送機制。它可以應用于組播和點播。RTP, RTCP,RSVP 定義如下:
1. 實時傳輸協議RTP(Real-time Transport protocol)
2. 實時傳輸控制協議RTCP(Real-time Transport Control protocol)
3. 實時流協議RTSP(Real Time Streaming protocol)
4. 資源預留協議RSVP(Resource Reserve Protocol)
客戶端與服務器運行實時流控制協議RTSP,以對該流進行各種VCR控制信號的交換,如播放(PLAY)、停止(PAUSE)、快進、快退等。當服務完畢,客戶端提出拆線(TEARDOWN)請求。服務器使用RTP/UDP協議將媒體數據傳輸給客戶端,一旦數據抵達客戶端,客戶端應用程序即可播放輸出。在流式傳輸中,使用RTP/RTCP/UDP和RTSP/TCP兩種不同的通信協議在客戶端和服務器間建立聯系。
3. WifiDisplay顯示框架實現
為了實現WifiDisplay google在Android現有顯示系統的基礎上加入的虛擬設備的支持,下圖給出了Android顯示系統的架構圖。
圖8 DisplayDevice的隔離示意圖
圖9 Android圖層示意圖
1.WindowManagerService
管理窗口之間的關系。包括位置、優先級等。
2.SurfaceFlinger
負責對各個Surface按照Z-order進行Merge,然后把Merge之后的Buffer顯示到Display上。
3.DisplayManagerService
管理在系統中加載的各個Display。包括管理各個Display的生命周期,并且對各個Display進行配置。當Display狀態變化的時候向系統和應用程序發送通知。
void SurfaceFlinger::doComposition() { ATRACE_CALL(); const bool repaintEverything = android_atomic_and(0, &mRepaintEverything); for (size_t dpy=0 ; dpy& hw(mDisplays[dpy]); if (hw->isDisplayOn() && (hw->getDisplayType() != DisplayDevice::DISPLAY_EXTERNAL)) { // transform the dirty region into this screen's coordinate space const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); // repaint the framebuffer (if needed) doDisplayComposition(hw, dirtyRegion); hw->dirtyRegion.clear(); hw->flip(hw->swapRegion); hw->swapRegion.clear(); } // inform the h/w that we're done compositing hw->compositionComplete(); } postFramebuffer(); }
SurfaceFlinger將遍歷系統中所有的DisplayDevice來完成各自的混屏工作。
圖10 WifiDisplay顯示模型
作為Wifi-Diplay Source的設備建立RTSP Server,將SurfaceFlinger merge的Buffer數據stream到Wifi-Diplay的peer端。作為Wifi-Diplay Sink的設備是一個RTSP的Client端,從peer端讀取數據,在built-in的Display上顯示。
4. Android WifiDisplay實現
4.1 Source端實現
基于Android6.0 代碼Source端入口在原生Settings->設備->顯示->投射,這個功能如果正常使用時,需要更改一個配置項。
該配置項路徑為
frameworks/base/core/res/res/values/config.xml
該入口的主要作用是掃描并發現sink設備。
4.1.1 設備掃描及發現
圖11 設備發現流程圖
當用戶點擊了optionMenu中enable wifi display選項時,會觸發相關的設備掃描及更新操作,在WifiDisplaySettings和WifiDisplayController都有注冊ContentObserver來監控這個值的變化。觸發設備掃描的是在WifiDisplayController中通過updateWfdEnableState()進行的,最終通過WifiP2pManager.requestPeers來完成設備的掃描工作,獲取掃描到的設備列表是在WifiDisplaySettings通過update(int changes)進行的。對于設備連接狀態的管理主要通過updateConnection()來進行。由于設備的連接過程是一個異步過程,所以在設備操作相關的過程中會反復調用updateConnection() 來判定設備狀態及更新連接操作。
private void update(int changes) { boolean invalidateOptions = false; // Update settings. if ((changes & CHANGE_SETTINGS) != 0) { mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(), Settings.Global.WIFI_DISPLAY_ON, 0) != 0; mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(), Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0; mWpsConfig = Settings.Global.getInt(getContentResolver(), Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID); // The wifi display enabled setting may have changed. invalidateOptions = true; } // Update wifi display state. if ((changes & CHANGE_WIFI_DISPLAY_STATUS) != 0) { mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus(); // The wifi display feature state may have changed. invalidateOptions = true; } // Rebuild the routes. final PreferenceScreen preferenceScreen = getPreferenceScreen(); preferenceScreen.removeAll(); // Add all known remote display routes. final int routeCount = mRouter.getRouteCount(); for (int i = 0; i < routeCount; i++) { MediaRouter.RouteInfo route = mRouter.getRouteAt(i); if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) { preferenceScreen.addPreference(createRoutePreference(route)); } } // Additional features for wifi display routes. if (mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) { // Add all unpaired wifi displays. for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) { if (!display.isRemembered() && display.isAvailable() && !display.equals(mWifiDisplayStatus.getActiveDisplay())) { preferenceScreen.addPreference(new UnpairedWifiDisplayPreference( getActivity(), display)); } } // Add the certification menu if enabled in developer options. if (mWifiDisplayCertificationOn) { buildCertificationMenu(preferenceScreen); } } // Invalidate menu options if needed. if (invalidateOptions) { getActivity().invalidateOptionsMenu(); } }
圖12 Source端 RTSP連接流程圖
下圖是Source端設備建立連接的流程圖,主要建立RSTP協議的Socket連接,通過接收Sink端的協議信息解析相關操作,代碼流程如下圖所示。
socket的建立主要在WidiSourceRtsp.cpp的prepareListenSocket函數中實現,并在這個socket上監聽是否有客戶端的連接請求, rtsp消息處理在cbHandleParserEvent中處理。
在WidiSession.cpp中RSTP及media event處理主要通過WorkHandler處理,相關消息處理在函數onMessageReceived中處理。
4.2 Sink端的實現
圖13 給出了sink端的實現框架圖,從框架圖可以看出APP主要和Sink API交互,Sink API和框架服務中的Wifi server 及 mediaserver交互,APP通過Control interface進行WFD相關的控制操作,底層狀態的接收則通過Events interface,也就是一些相關的回調方法來處。
圖13 Sink端 實現框架結構圖
圖 14 RTSP會話流程圖
圖15 WFD控制流程圖
圖14給出了Intel實現的sink端的RTSP會話管理流程圖,RTSP的協議實現主要通過C++實現,對于協商后相關的狀態反饋通過回調函數完成,如果想進一步了解相關的流程,請查看相關的代碼。
圖15 給出了WFD會話管理的流程圖,WFD中除了RTSP的實現,還包括連接認證(Connection Auth)、視頻流加密及解密(HDCP)、UIBC實現等。
4.2.1 設備如何被發現
init P2P channel
mP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE); Looper looper = mHandlerThread.getLooper(); if (mP2pManager == null || looper == null) { throw new NullPointerException("Failed to get P2P_SERVICE or widi_receiver thread looper"); } mChannel = mP2pManager.initialize(context, looper, mChannelListener);
advertise Sink
WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo(); wfdInfo.setWfdEnabled(true); wfdInfo.setDeviceType(WifiP2pWfdInfo.PRIMARY_SINK); wfdInfo.setSessionAvailable(true); wfdInfo.setControlPort(7236); wfdInfo.setMaxThroughput(50); Log.d(TAG,"advertise sink"); mP2pManager.setWFDInfo(mChannel, wfdInfo, mActionListener); mHandler.post(mDiscoverPeers); mCallbackHandler.post(mSendEnabled); discover peers public void run() { mP2pManager.discoverPeers(mChannel, mActionListener); mHandler.postDelayed(this, DISCOVER_INTERVAL_MS); }
經過這三個階段的工作,其他設備就可以發現這臺設備了
4.2.2 設備p2p連接后如何建立RTSP連接
P2P連接上之后可以從相關Intent中獲取到Source端傳遞過來的建立RTSP連接的ip和端口,Sink端根據這些信息主動去連接。
else if (netInfo.getState() == NetworkInfo.State.CONNECTED) { if (mRtsp != null) { Log.i(TAG, "Ignoring extra CONNECTED event"); return; } WifiP2pInfo p2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO); if (p2pInfo == null) { Log.i(TAG, "WIFI_P2P_INFO is not available"); return; } if (p2pInfo.groupFormed) { mHandler.removeCallbacks(mDiscoverPeers); WifiP2pGroup group = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP); if (group == null) { Log.i(TAG, "WIFI_P2P_GROUP is not availble"); return; } mP2pInterface = group.getInterface(); mSourceRtspPort = 7236; if (p2pInfo.isGroupOwner) { Collectiondevices = group.getClientList(); if (devices != null) { if (devices.isEmpty()) Log.i(TAG, "Device list empty!"); else { for (WifiP2pDevice device : devices) { if (sReflectionInited) { // use reflection instead of: // mSourceMacAddress = device.interfaceAddress; try { mSourceMacAddress = (String) sIfAddr.get(device); } catch (Exception e) { Log.e(TAG, "Failed to get interfaceAddress field" + " by reflection: " + e); } } else Log.e(TAG, "Reflection not initialiazed," + "can't get interfaceAddress content"); if (device.wfdInfo != null) { mSourceRtspPort = device.wfdInfo.getControlPort(); Log.i(TAG, "mSourceRtspPort obtained is " + mSourceRtspPort); } else Log.i(TAG, "Using default RTSP Port=" + mSourceRtspPort); } } } } else { mSourceIp = p2pInfo.groupOwnerAddress.getHostAddress(); if (group.getOwner().wfdInfo != null) { mSourceRtspPort = group.getOwner().wfdInfo.getControlPort(); Log.i(TAG, "mSourceRtspPort obtained is " + mSourceRtspPort); } else Log.i(TAG, "Using default RTSP Port=" + mSourceRtspPort); } if (mSourceIp != null && mSourceRtspPort > 0 && mSourceRtspPort <= 65535) { mHandler.post(new ConnectRtsp(mSourceIp, mSourceRtspPort)); } } } }
4.2.3 RTSP連接實現
執行完下面的代碼后,RTSP連接就建立起來了,相關的視頻流通過mediaplay來完成處理。
public void run() { Log.i(TAG, "Connect RTSP " + sourceIp + "/" + sourceRtspPort); mConnectorTypeRequested = false; mDisplayManager.registerDisplayListener(mDisplayListener, null); try { Log.d(TAG,"media reset begin"); mMediaPlayer.reset(); Log.d(TAG,"media reset over"); mMediaPlayer.setDataSource("intel_rtp://" + mP2pInterface + ":" + RTP_LISTEN_PORT); mMediaPlayer.prepare(); } catch (Exception e) { Log.e(TAG, "Exception trying to play media", e); } mSourceIp = sourceIp; mRtsp = new WidiSinkRtsp(); if (mRtsp.init(sourceIp + ":" + sourceRtspPort, mSinkListener) == 0) mRtsp.start(); } } 編輯:黃飛
-
WIFI
+關注
關注
81文章
5296瀏覽量
203578 -
無線信號
+關注
關注
2文章
263瀏覽量
20444 -
RTSP
+關注
關注
0文章
13瀏覽量
12138 -
視頻編解碼
+關注
關注
2文章
54瀏覽量
11748 -
Miracast
+關注
關注
0文章
6瀏覽量
18707
原文標題:WifiDisplay(Miracast)技術原理及實現
文章出處:【微信號:哆啦安全,微信公眾號:哆啦安全】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論