Android 4.2wifidisplay采集


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的連接建立嗎?

wpsCD0B.tmp

其是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的數據保存為本地文件,主要是修改了此的初始化流程。將之前需要真正有客戶連接才啟動整個采集,編碼,傳輸的流程,修改為直接調用

wpsCD2C.tmp

wpsCD4C.tmp

wpsCD5C.tmp

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數據保存為本地文件的時候,具體是需要了此類當中的代碼

wpsCD7D.tmp

2. 由start進行初始化操作,在play當中啟動。

A. 要建立起SOCKET連接成功,且雙方約定好傳輸多媒體格式。

wpsCD8D.tmp

創建雙向約定協議的 sender,且在PlaybackSession約定了一個AMessage與Sender進行通信。

wpsCDAD.tmp

wpsCDBE.tmpwpsCDCF.tmpwpsCDDF.tmp

wpsCDFF.tmp

wpsCE10.tmp

wpsCE30.tmp

建立成功之后,向上層進程調用連接成功的通知。

【display當中的數據怎么樣讀取與采集】

數據來源之屏幕

wpsCE41.tmp

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

Stagefright 之 Buffer傳輸流程

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 概要分析系列 (一)

http://blog.csdn.net/andyhuabing/article/details/7258113


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM