1. 程式人生 > >Android Multimedia框架總結(七)C++中MediaPlayer的C/S架構補充及MediaService介紹

Android Multimedia框架總結(七)C++中MediaPlayer的C/S架構補充及MediaService介紹

前面一篇主要介紹c++中MediaPlayer的C/S架構中和Client相關部分,並中間穿插了mediaplayerservice的部分。但是對於這塊C/S部分,沒有放大去分析。《Android Multimedia框架總結(四)MediaPlayer中從Java層到C++層類關係及prepare及之後其他過程》是從整體上看的,今天我們把這塊C/S模型放大去看下。同樣先看下Agenda:

  • C/S模型中的三角關係流程圖
  • MediaPlayerService相關聯的類圖
  • MediaPlayerService的產生過程
  • MediaPlayerService新增服務過程
  • MediaPlayerService通過BinderDriver和MediaPlayer通訊過程

C/S模型中的三角關係流程圖

這裡寫圖片描述

上圖總結如下幾點:

  • MediaPlayer是客戶端,也就是我們說的C/S中的C端

  • MediaPlayerService和MediaPlayerService::Client是伺服器端。也就是我們說的C/S中的S端。

  • MediaPlayerService實現IMediaPlayerService定義的業務邏輯,其主要功能是根據MediaPlayer::setDataSource輸入的URL呼叫create函式建立對應的Player.

  • MediaPlayerService::Client實現IMediaPlayer定義的業務邏輯,其主要功能包括start, stop, pause, resume…,其實現方法是呼叫MediaPlayerService create的Player中的對應方法來實現具體功能。

  • 此前在第四篇那個圖中已經畫了個整體,今天再把MediaPlayerService,MediaPlayerService::Client,MediaPlayer放大看下他們在實際業務中互動關係。

MediaPlayerService相關聯的類圖

這裡寫圖片描述

以上類關係圖,總結如下幾點:

  • 在一個BnXXX或BpXXX都派生於兩個類,具體情況如下:

    • class BpXXX : public IXXX, public BpRefBase
    • class BnXXX : public IXXX, public BBinder
  • BpXXX和BnXXX都派生於IXXX,哪IXXX又是做什麼的呢?這裡可以理解為,定義業務邏輯,我們此前分析IMediaPlayerClient在作用時,也說過。但在BpXXX與BnXXX中的實現方式不同:

    • 在BpXXX中,把對應的binder_transaction_data打包之後通過BpRefBase中的mRemote(BpBinder)傳送出去,並等待結果
    • 在BnXXX中,實現對應的業務邏輯,通過呼叫BnXXX派生類中的方法來實現,如MediaPlayerService::Client
  • 從上圖可以看出,IBinder是用來進行程序間通訊用的。

    • BpRefBase中有一個mRemote(BpBinder)用來與Binder驅動互動用的。
    • Binder是用來從Binder驅動中接收相關請求,並進行相關處理的。

MediaPlayerService的產生過程

在瞭解MediaPlayerService之前,先了解下IMediaPlayerService.cpp,
6.0原始碼中是在frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中:

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

可以看出這裡定義一些常規業務相關,接下來開始瞭解MediaPlayerService
先找到入口,在frameworks/base/media/mediaserver/main_mediaserver.cpp

這裡寫圖片描述

首先看下defaultServiceManager函式,如下:

這裡寫圖片描述

用的是一個單例,每個程序只需要一個BpServiceManager代理,ProcessState::self()->getContextObject(NULL),接下來看下getContextObject(NULL)函式,
接著看看ProcessState::self()->getContextObject(NULL)

這裡寫圖片描述

以上程式碼總結為:根據傳入的控制代碼handle值為0,表示ServiceManager,new一個BpBinder所以現在相當於:
gDefaultServiceManager = interface_cast(new BpBinder(0));
然後我們看看interface_cast做了什麼操作?
位於frameworks/base/include/binder/IInterface.h中,有如下程式碼:

這裡寫圖片描述

繼續我們跟到IServiceManager裡面去:
位於frameworks/base/include/binder/IServiceManager.h中,有如下程式碼:

這裡寫圖片描述

總結上述程式碼:根據控制代碼handle(0)建立一個new BpBinder(0),根據這個BpBinder建立了一個BpServiceManager代理。
下面來看看BpServiceManager代理:

這裡寫圖片描述

這裡BpInterface是一個模板類,表示這裡BpServiceManager同時繼承與BpInterface和IServiceManager類

這裡寫圖片描述

呼叫了基類BpInterface建構函式:

這裡寫圖片描述

MediaPlayerService::instantiate();//例項化MediaPlayerService
frameworks/base/media/libmediaplayerservice/MediaPlayerService.cpp

這裡寫圖片描述

defaultServiceManager()返回的是剛建立的BpServiceManager,呼叫add函式。
BpMediaPlayService作為服務代理端,那麼BnMediaPlayerService一定是實現端,MediaPlayerService繼承於BnMediaPlayerService,實現了真正的業務函式,用於處理客戶端傳遞的資訊。

MediaPlayerService新增服務過程

來看看BpServiceManager的addService()函式:

這裡寫圖片描述

這裡remote()就是前面建立的BpBinder(0)物件。

這裡寫圖片描述

接著看一個有意思的名字,talkWithDriver的實現,顧名思義,和driver談話:

這裡寫圖片描述

IPCThreadState::joinThreadPool(), ProcessState::self()->startThreadPool()
進入執行緒迴圈talkWithDriver 等待客戶端Client請求,從Binder讀取命令請求進行處理。

到現在為止MediaPlayerService的服務端已經向服務總管ServiceManager註冊了。

MediaPlayerService通過BinderDriver和MediaPlayer通訊過程

下面我們看看客戶端是如何獲得服務的代理並和服務端通訊的。
我們以MediaPlayer的業務函式decode解析播放一個網路視訊的url為例

這裡寫圖片描述

這裡我們主要分析getMediaPlayerService,客戶端是如何向ServiceManager總管查詢服務並獲得代理的。

這裡寫圖片描述

  • 首先獲得BpServiceManager的代理,然後呼叫getService()函式向服務總管ServiceManager查詢名叫String16(“media.player”)的服務。
    位於frameworks/base/libs/binder/IServiceManager.cpp中:

這裡寫圖片描述

  • 這裡首先將請求打包成Parcel各式,然後呼叫remote()->transact()函式,前面我們分析過BpServiceManager::remote()返回
    的就是前面new BpBinder(0)對應控制代碼為ServiceManager。繼續去BpBinder中尋找實現程式碼:
    在frameworks/base/libs/binder/BpBinder.cpp中

這裡寫圖片描述

  • 最後呼叫的IPCThreadState的transact()函式,IPCThreadState是專門提供通過Binder程序間通訊的介面的。

這裡寫圖片描述

在這一步,首先通過writeTransactionData函式來填充mOut結構體,mOut裡面內容為:

這裡寫圖片描述

這裡binder_transaction_data tr內容為:

這裡寫圖片描述

tr.data內容為:

這裡寫圖片描述

這個waitForResponse()函式是等待ProcessState返回資訊:

這裡寫圖片描述

最後返回的是:return reply.readStrongBinder();進入到Parcel的readStrongBinder()函式

這裡寫圖片描述

這裡flat->type是BINDER_TYPE_HANDLE,所以呼叫ProcessState::getStrongProxyForHandle()函式

這裡寫圖片描述

這裡的handle就是ServiceManager內維護的MediaPlayerService對應的Binder控制代碼,這個ProcessState根據這個控制代碼
new 了一個BpBinder,並將其儲存起來,這樣下次需要從ServiceManager請求獲取到相同控制代碼的時候就可以直接返回了。
最後根據這個返回的BpBinder獲得MediaPlayerService的代理:
sMediaPlayerService = interface_cast(binder);
根據前面ServiceManager一樣,最後呼叫的是IMediaPlayerService的asInterface()巨集函式

這裡寫圖片描述

這樣就獲得了一個代理BpMediaPlayerService物件,它的remote()為BpBinder(handle),這個handle就是向服務總共ServiceManager
查詢到的MediaPlayerService對應的Binder控制代碼。

最後總結下:

  • 實際業務中,如MediaPlayer::setDataSource返回時,會建立一個與MediaPlayerService::Client對應的BpMediaPlayer,用於獲取MediaPlayerService::Client的各項功能。
  • MediaPlayer又是如何找到MediaPlayerService::Client的呢? 只有MediaPlayerService才向ServiceManager進行了註冊,所以MediaPlayer必須先獲取BpMediaPlayerService,然後通過BpMediaService的管理功能create,來建立一個MediaPlayerService::Client.
  • 為什麼不直接定義一個MediaPlayer向ServiceManager註冊呢?
    MediaPlayerService包含的功能不只是Client, 還有AudioOutput,AudioCache,MediaConfigClient功能。MediaPlayerService就是一個媒體服務的視窗(Driver有點類似一個場地,在這個場地,溝通好資訊),MediaPlayerService把生意談好,合同籤回來,再根據合同上的要求,安排不同的開人發員去做。

第一時間獲得部落格更新提醒,以及更多android乾貨,原始碼分析,歡迎關注我的微信公眾號,掃一掃下方二維碼或者長按識別二維碼,即可關注。

這裡寫圖片描述

如果你覺得好,隨手點贊,也是對筆者的肯定,也可以分享此公眾號給你更多的人,原創不易