Android 4.2wifidisplay采集
1. 顯示設置display,其是最終顯示的地方,其的數據格式,其的緩存結構
2. Display其的數據來源,又怎么樣操作這些Display當中的數據如截個圖,其與SurfaceFlinger有關系,在wifidisplay其的數據來源surfaceMediaSource
3. Android平板當中其是有多個顯示設備,如平板的顯示屏,HDMI, wifidisplay. 其之間的並存的關系,每個顯示設備又有不同的特點,如用戶在輸入密碼的時候,可能希望只在顯示屏上顯示,不在其他屏幕上顯示,這就是安全性。
4. 多個顯示設備則需要有一個顯示設備類,用來描述顯示設備,要一個顯示設備管理類來統一管理顯示設備的創建,刪除,數據的讀取。
5. 多個顯示設備是並存顯示,android平板當中是有多個activity,android通過surfaceFlinger來混合,之后依次輸出到各個顯示設備當中。出於安全考慮,其應該是:先得到顯示設備的特點,將特性輸出給surfaceflinger, surfaceflinger要求windowManagerService按要求提供activity集合,如:有安全要求,則不要將有安全特性的activity發給surfaceflinger混合。
6. 用戶層通過相應的java層接口來操作display,創建與刪除,截個圖。
Android frameworks,其是有DisplayManagerService,提供用戶來操作display, 內部則是於java層向JNI,層,然后調用c++層native service,SurfaceFlinger ,此處是native的binder通信,是JVM進程與surfaceFlinger進程之間通過binder通信。
a) 在android當中截圖有三種方式,A,通過直接讀取framebuffer,B.通過display來截個圖,首先通過
b) ScreenshotClient screenshot;
c) // 根據ID得到顯示設備的操作接口,其的內部:
d) surfaceComposerClient,SurfaceFlinger,DisplayDevice,其進程之間的關系,應用進程與surfaceFlinger進程。
e) sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
f) if (display != NULL && screenshot.update(display) == NO_ERROR) {
g) base = screenshot.getPixels();//得到display緩存的地址方式
h) w = screenshot.getWidth();
i) h = screenshot.getHeight();
j) f = screenshot.getFormat();//ARGB是888還是565的位格式
k) size = screenshot.getSize();//緩存的大小
l) // screenshot.update其會引起surfaceFlinger來進行混屏操作。
C.還是display,但其直接讀取surfaceflinger混合之后的數據,不會引起surfaceflinger再次混合,wifidisplay當中的surfaceMediaPlay就是讀取此當中的數據。
7.java層:
8.frameworks\base\media\java\android\media\RemoteDisplay.java
上層的核心類RemoteDisplay, Remote + Display,Remote體現在遠程,如在wifidisplay當中顯示在另一台機器當中。Display其是一個顯示設備。其native層的so庫與native服務是什么。
首先查看其的JNI層代碼
frameworks\base\core\jni\android_media_RemoteDisplay.cpp,其當中有幾個類:
1. class NativeRemoteDisplayClient : public BnRemoteDisplayClient
1. 其是native層進程之間通信的服務提供層,其被哪個進程所有調用,應該是MediaPlayerService進程(media service)。Display怎么樣與media service有關系呢。
流程:1.創建一個客戶端IRemoteDisplayClient,然后傳遞media service,media Service就使用這個對象與服務端通信,主要用於將MediaService進程通知wifidisplay進程客戶端的連接成功,連接失敗,連接異常。
2. 此類主要是remoteDisplay建立連接,關閉連接,連接異常的回調。其將數據信息由native層調用JAVA層的回調接口。連接主要是哪個與哪個的連接,本地顯示與另一台機器顯示之間的連接嗎。連接的基礎是RTSP的連接建立嗎?
其是mediaplayerservice進程當中,當一個客戶端連接到wifidisplaySource充當的RTSP服務器當中,mediaplayerservice通過IRemoteDisplayClient的binder通信機制通過上層應用進程。
3. NativeRemoteDisplay類的作用,其是原生代碼當中,在保存文件當中,並沒有使用些類,而是使用一個類似作用的類:NativeRemoteDisplayEx,其兩者的作用均是相同的,在native層保存與mediaplayerService進程的關鍵變量,暫將變量的地址保存在java層。
2.class NativeRemoteDisplay {
3.private:
4. sp<IRemoteDisplay> mDisplay;
5. sp<NativeRemoteDisplayClient> mClient;
6.}
其包含兩個主要接口,其沒有什么業務,只一個數據結構。
sp<IRemoteDisplay> mDisplay其是進程之間的客戶端,是哪幾個進程之間通信,SurfaceFlinger進程,其是操作Display的客戶端接口。
sp<NativeRemoteDisplayClient> mClient其是進程之間的服務端,與media service進程之間通信。
其為什么需要這樣一個包裝的數據結構呢?主要是為了下面的native層處理當中創建一個C++對象,然后將此對象的地址發送到上層疊java service當中保存。
4.NativeRemoteDisplayEx與NativeRemoteDisplay的作用相同
2.NativeRemoteDisplayEx{
3. sp<IRemoteDisplay> mDisplay;
4. sp<RemoteDisplayClient> mClient;
5.}
其與NativeRemoteDisplay一樣的,其的mClient對象類型是:RemoteDisplayClient,在保存文件當中,其是使用這個對象,其的作用只是一個對象的wrapper,供上java service使用。
3.RemoteDisplayClient : public BnRemoteDisplayClient
其與NativeRemoteDisplayClient均是用於進程之間的通信,bind的服務提供端。其當中的連接成功方法:
// 當客戶端連接到服務器,上層應用進程通過binder接口調用surfaceFlinger進程創建一個虛擬顯示設備,根據每一個連接顯示的要求的不同創建不同的顯示設備。
void RemoteDisplayClient::onDisplayConnected(
const sp<ISurfaceTexture> &surfaceTexture,
uint32_t width,uint32_t height,uint32_t flags) {
// 虛擬顯示設備的數據來源,其來源於surfacemediaSource
mSurfaceTexture = surfaceTexture;
// 根據是否有安全性要求,通過surfaceflinger進程創建一個顯示設備。
mDisplayBinder = mComposerClient->createDisplay(String8("foo"), false /* secure */);
// 設置顯示設備的參數
SurfaceComposerClient::openGlobalTransaction();
mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
//Rect layerStackRect(1280, 720); // XXX fix this.
Rect layerStackRect(width, height); // XXX fix this.
//Rect displayRect(1280, 720);
Rect displayRect(width, height);
mComposerClient->setDisplayProjection(
mDisplayBinder, 0 /* 0 degree rotation */,
layerStackRect,
displayRect);
SurfaceComposerClient::closeGlobalTransaction();
}
4.供RemoteDisplay.java使用native方法
nativeStartRecord:
static jint nativeStartRecord(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
ScopedUtfChars iface(env, ifaceStr);
ALOGI("### nativeStartRecord ###");
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(binder);
CHECK(service.get() != NULL);
// 音頻策略起用remote submix方案
enableAudioSubmix(true /* enable */);
// 創建一個網絡連接狀態接口的回調,其是mediaserviceplay進程調用上層應用進程。
sp<RemoteDisplayClient> client = new RemoteDisplayClient(env, remoteDisplayObj);
// 通過調用mediaserviceplay進程創建一個remoteDisplay對象,其集成了屏幕采集,語音采集,音視頻單獨格式編碼壓縮,再按時間同步合成TS數據流,建立網絡連接通信的基礎,利用網絡連接發送到客戶端,或保存到本地文件當中,所以其本質是一個流媒體播放器,也就解釋了remotedisplay其的代碼是frameworks\av\media\libmediaplayerservice路徑當中。這個是多媒體播放器的目錄:在流媒體錄制與播放,本地多媒體的錄制與播放器。
注意remoteDisplay其並不是真正的在surfaceflinger進程當中創建了一個虛擬顯示設備,RemoteDisplayClient::onDisplayConnected當中完成的創建一個虛擬顯示設備,而remoteDisplay其只是創建一個多媒體播放器。
結論:一台android設備當中的相關進程關系
1. 上層java進程,如Setting進程,錄屏幕進程
2. 多媒體進程,mediaplayerservice進程,其創建一個流媒體屏幕錄制直播對象
3. 顯示進程,surfaceflinger進程,其創建一個虛擬顯示設備
// 通過mediaplayerservice進程創建一個流媒體屏幕錄制直播模塊對象
sp<IRemoteDisplay>display=service->listenForRemoteDisplay(client,String8(iface.c_str()));
// 將兩個進程之間的bind接口保存起來。
NativeRemoteDisplayEx* wrapper = new NativeRemoteDisplayEx(display, client);
// 返回給java保存
return reinterpret_cast<jint>(wrapper);
}
nativePauseRecord
nativeResumeRecord,nativeStopRecord.
其均是控制mediaplayerservice進程當中屏幕直播模塊的狀態。如暫時直播,重新直播,關閉直播。
frameworks\av\media\libmediaplayerservice\RemoteDisplay.cpp
RemoteDisplay其是核心當中的核心,其處於libmediaplayerservice.so庫,其處於的進程是
Mediaplayerservice進程。其的作用:流媒體屏幕直播模塊,在此框架當中其應該有的部件:
1. 網絡通信框架,負責創建服務器,負責接受客戶端的socket連接,管理所有的客戶端socket數據接收與發送,這是一個線程
2. 單獨一個直播源的數據發送與命令控制
3. 采集數據模塊,有視頻采集模塊,語音采集模塊。這是一個線程
4. 數據壓縮模塊:將各種多媒體數據壓縮如視頻壓縮成h264,語音壓縮成AAC,這是一個線程
5. 多媒體數據合成模塊:將多媒體數據按時間同步的方法合成為ts數據流,這是一個線程
6. 將多媒體數據按網絡數據合成發送:在網絡當中發送,以RTP協議格式通過socket發送到客戶端。
注意:所有這些部件均在同一個進程當中,線程之間通信使用ALooper,AHandle,AMessager所組成的異步通信框架,其類似於android java層的looper, handler,Message
1. RemoteDisplay
屏幕流媒體直播服務器的入口,
a.創建了網絡通信框架ANetworkSession,整體的業務框架WifiDisplaySource等對象。
b.繼承了BnRemoteDisplay對象,負責上層進程與屏幕流媒體直播服務器進程的狀態控制。
2.ANetworkSession
網絡通信框架,負責創建服務器,負責接受客戶端的socket連接,管理所有的客戶端socket數據接收與發送,這是一個線程當中,不斷的接收客戶端的連接,不斷的管理所有客戶端的業務數據的發送與接收。所有客戶端連接socket模型是select方式,沒有一個連接一個線程,也沒有使用epoll,所以其一個線程就搞定了。
其的類有:
A. ANetworkSession:負責創建服務器監控連接,管理所有session
B. NetworkThread:其繼承一個thread就是一個線程,在線程回調方法threadLoop當中,調用ANetworkSession.threadLoop方法來接收客戶端連接與客戶端數據的讀取與發送。
C. Session:負責與一個客戶端的數據讀取與發送。
3.WifiDisplaySource
各種業務的管理類,直播器的狀態具體控制。將狀態反饋到上層進程當中。
A. 調用onSetupRequestEx模擬一個客戶端的連接,創建一個PlaybackSession,且傳遞其一個AMessage(kWhatPlaybackSessionNotify, id())給PlaybackSession,用於PlaybackSession異步地將消息傳遞給wifiDisplaySource,調用PlaybackSession::initEx()方法,在改造將wifidisplay的數據保存為本地文件,主要是修改了此的初始化流程。將之前需要真正有客戶連接才啟動整個采集,編碼,傳輸的流程,修改為直接調用
B. 調用PlaybackSession::initEx()方法,確定視頻采集者:SurfaceMediaSource其是原始數據,RepeaterSource:以固定采集頻率從SurfaceMediaSource當中讀取數據,音頻的流程也類似。
C. 在音頻,視頻數據采集者的基礎之上,統一出一個公共類以統一的接口為上層服務,
MediaPuller具體將音視頻數據格式從MediaBuffer統一封裝成ABuffer。
D. 對於從MediaPuller當中讀取過來的數據需要進行壓縮編碼(codec),Converter具體負責對於音視頻數據編碼,然后將結果以Message:kWhatConverterNotify發送給PlaybackSession,音頻的流程也類似,Converter轉換之后的數據,已經是裸數據,如裸H264,AAC,我們就是直接在此處將數據通過LocalSocket發送到上層進程當中。
E. 將采集MediaPuller與編碼Converter封裝到統一的Track結構當中,方便上層處理,視頻數據一個Track,音頻數據一個Track.上層在將Track合成Ts數據流的時候很方便。此過程:Converter編碼數據之后,發送消息給WifiDisplaySource,WifiDisplaySource根據數據類型調用相應的Track,判斷是否需要HDCP加密,根據音視頻數據時間同步要求,最終數據調用Sender通過socket發送數據到另一台機器的客戶端上面。
F. 對於TS數據在網絡上面傳遞,雙向需要約定網絡傳輸協議,在Sender當中具體實現,其是使用RTSP/RTP協議,我們在將TS數據保存為本地文件的時候,具體是需要了此類當中的代碼
2. 由start進行初始化操作,在play當中啟動。
A. 要建立起SOCKET連接成功,且雙方約定好傳輸多媒體格式。
創建雙向約定協議的 sender,且在PlaybackSession約定了一個AMessage與Sender進行通信。
建立成功之后,向上層進程調用連接成功的通知。
【display當中的數據怎么樣讀取與采集】
數據來源之屏幕
SurfaceMediaSource其相當於fremebuffer,其的讀取與寫入需要surfaceflinger實時地將混合的圖片數據寫入到虛擬顯示設備當中,但現在還沒有創建虛擬顯示設備,簡單地創建一個SurfaceMediaSource是沒有作用的,其需要surfaceflinger實時將混合的界面數據寫入到SurfaceMediaSource當中。
A.創建一個虛擬顯示設備
mDisplayBinder = mComposerClient->createDisplay(String8("foo"), false /* secure */);
B.虛擬顯示設備與surface相關,surface的兩個buffer與虛擬顯示設備關聯,當surfaceflinger實時地將混合的界面數據寫入虛擬顯示設備當中,其本質就是寫了surface的buffer當中,所以用戶只要讀取surface當中的數據就是讀取屏幕當中的數據,也解決了為什么讀取一個虛擬顯示設備的display不會影響平板性能,而讀取主顯示設備會影響平板性能。因為虛擬顯示設備其讀取surfaceflinger混合之后的數據,而主顯示設備會觸發surfaceflinger進行混合從而影響到了性能。
mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
C.用戶讀取surface當中的數據
// BufferQueue是繼承BnSurfaceTexture
sp<BufferQueue> mBufferQueue;
sp<SurfaceMediaSource> source = new SurfaceMediaSource(1280, 720);
mBufferQueue = source->getBufferQueue();
2. 虛擬顯示設備與屏幕源結合
【Surface系統】
直接從native層看surface層怎么樣操作,向surfaceflinger分配空間,得到surface的graphicbuffer的起始地址,向其中寫入數據,通知surfaceflinger進行刷新顯示。
frameworks\native\services\surfaceflinger\tests\resize.cpp作為一個最佳的分析入口,其沒有JAVA層的activity.
進程之間的關系,上層應用進程,surfaceflinger進程,兩者通過binder進行通信。服務端有兩個binder,得到binder服務端的接口方法:通過serviceManager對象傳入服務名稱,得到客戶端binder接口。
A.BnSurfaceComposerClient
B.BnSurfaceComposer
SurfaceComposerClient:
其封裝了操作surfaceFlinger的方法,其不是Bn端,其組合Bn端,其最終調用Bn端來進行與surfaceFlinger通信。Bn端是Client.
Client : public BnSurfaceComposerClient
SurfaceControl
:
Surface
SurfaceFlinger
ISurfaceComposer
1. Surface的內部管理
A. GraphicBuffer封裝了與硬件相關的操作,為上層應用程序提供了統一的圖形內存分配接口。每一個Activity的Surface需要分配一個GraphicBuffer,這些GraphicBuffer怎么樣統一的管理,怎么樣統一的數據讀取與寫入。
2. Surface數據的讀取
3. BufferQueue與GraphicBuffer之間的關系
WifiDisplaySource : public AHandler
有一個集成管理數據來源的管理類:采集,編碼,其也是放置在一個線程當中。
同一個線程當中,使用線程的發送與接收數據
同一個進程當中的不同線程,數據的發送與接收
// 生成一個AMessage的時候綁定一個線程A的id,發送給B線程,B修改此message,如放置數據,B發送此消息,通過消息框架A線程就能夠接收到數據。同一個進程的不同線程的內存空間的共享的就可以直接使用。
sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id());
notify->setInt32("playbackSessionID", playbackSessionID);
// 創建一個線程,且將此線程集成到異步通信框架當中,能夠接收異步數據
looper()->registerHandler(playbackSession);
RemoteDisplay其的作用是什么。其的流程是什么呢。
ANetworkSession:其是一個socket service的線程,負責循環等待客戶端連接,管理所有客戶端連接的數據讀寫。如果只是保存文件則不需要此類的功能
WifiDisplaySource:其管理所有數據來源。
1. wifiDisplaySource調用playbackSession::initEx,創建一個PlaybackSession線程來創建一個音視頻數據來源,視頻數據來源於SurfaceMediaSource,音頻來源於AudioSource。
2. 添加視頻源SurfaceMediaSource,其是怎么樣與顯示設備所建立關聯的,其讀取的是surfaceFlinger之后,其怎么樣與surfaceFlinger建立關聯的。還是之前的數據,其數據的格式與大小。
3. 需要一個類以一定的幀率在surfaceMediaSource當中讀取數據,其應該是一個線程且異步讀取數據,其使用AHandler,但其沒有繼承AHandle而是組合使用,因為其繼承了MediaSource,其就是RepeaterSource。其使用了裝飾者設計模式。
4. 需要一個類以音視頻時間同步的方式從視頻,音頻的數據提供者當中讀取數據。
5. 數據的發送:先音頻,音頻按相應格式組合,如視頻按H264,音頻按AAC,這個是Track的工作,
6. 其將音視頻數據混合且添加音視頻時間同步的信息,拼成TS數據TSPacketizer,
7. 將數據按相應的協議發送,其是Sender負責按RTSP,RTP協議發送,其利用ANetworkSession基礎設備最終發送數據。
8. SurfaceMediaSource其的數據來源最終來源於Gralloc Buffers
【流程打印】
相關連接
Android截屏淺析
http://blog.sina.com.cn/s/blog_69a04cf4010173fz.html
IGraphicBufferProducer
Android 4.4(KitKat)中的設計模式-Graphics子系統
http://blog.csdn.net/jinzhuojun/article/details/17427491
Android中的GraphicBuffer同步機制-Fence
http://blog.csdn.net/jinzhuojun/article/details/39698317
Android 4.4(KitKat)中的設計模式-Graphics子系統
http://blog.csdn.net/jinzhuojun/article/details/17427491
http://blog.csdn.net/ayuppie/article/details/8668462
ScreenMirror buffer ->codec編碼傳輸流程(1/3)
http://www.codes51.com/article/detail_341988.html
SurfaceFlinger GraphicBuffer內存共享緩沖區機制
http://blog.csdn.net/andyhuabing/article/details/7489776
雙方設備准備就緒后,MediaPull會通過kWhatPull消息處理不斷調用MediaSource的read函數。在SurfaceMediaSource實現的read函數中,來自SurfaceFlinger的混屏后的數據經由BufferQueue傳遞到MediaPull中
http://blog.csdn.net/innost/article/details/8474683
Android -- SurfaceFlinger 概要分析系列 (一)