1. 程式人生 > >Android Multimedia框架總結(十一)CodeC部分之AwesomePlayer到OMX服務

Android Multimedia框架總結(十一)CodeC部分之AwesomePlayer到OMX服務

前言:上篇文《Android Multimedia框架總結(十)》總結了音視訊的輸出過程,從今天開始分析Codec部分,今天分析的是AwesomePlayer到OMX服務過程,也就是開啟OpenMax準備相關。

先看下今天的Agenda:

  • 一張圖看清OMX在stagefright中的位置
  • 一張圖看清OpenMax與Stagefright層級的關係
  • OMX初始化流程
  • OMX服務之NodeInstance列表的管理
  • OMX服務之NodeInstance節點的操作
  • 總結AwesomePlayer到OMX服務過程

背景: Android系統中用OpenMAX來做編解碼,Android向上抽象了一層OMXCodec,提供給上層播放器AwesomePlayer用。播放器中音視訊解碼器mVideosource、mAudiosource都是OMXCodec的例項。
OMXCodec::Create是解碼器初始化的入口。
OMXCodec通過IOMX 依賴binder機制 獲得OMX服務,OMX服務才是OpenMAX在Android中實現

一張圖看清OMX在stagefright中的位置:

這裡寫圖片描述

一張圖看清OpenMax與Stagefright層級的關係:

這裡寫圖片描述

OMX初始化流程

AwesomePlayer是如何獲得OMX服務的?

  • 在AwesomePlayer初始化的時候,會呼叫AwesomePlayer::onPrepareAsyncEvent。
  • 繼而呼叫AwesomePlayer::initVideoDecoder以及AwesomePlayer::initAudioDecoder。
  • 然後開始正式的進入OMX以及硬體解碼器的初始化工作。

之前的AwesomePlayer初始化的一些工作都是在小打小鬧。當OMX開始初始時,才是開始真正核心的初始化工作。我們知道,android中的元件都是在提供服務,有server,有client,大多是C/S模型,前面文章已介紹。
AwesomePlayer 中有個變數 OMXClient mClient;
先了解OMXClient.cpp,如下:
這裡寫圖片描述

OMXClient 有個IOMX的變數mOMX ,這個就是和OMX服務進行binder通訊的。

在AwesomePlayer的建構函式中會呼叫:
CHECK_EQ(mClient.connect(), (status_t)OK);
OMXClient中程式碼如下:

這裡寫圖片描述

OMXClient::connect函式是通過binder機制 獲得到MediaPlayerService,然後通過MediaPlayerService來建立OMX的例項。這樣OMXClient就獲得到了OMX的入口,接下來就可以通過binder機制來獲得OMX提供的服務。
也就是說OMXClient 是android中 openmax 的入口。

在建立音視訊解碼mVideoSource、mAudioSource的時候會把OMXClient中的sp mOMX的例項 傳給mVideoSource、mAudioSource來共享使用這個OMX的入口。
也就是說一個AwesomePlayer對應著 一個IOMX 變數,AwesomePlayer中的音視訊解碼器共用這個IOMX變數來獲得OMX服務。

這裡寫圖片描述

以上分為1,2兩個步驟,先看下1中OMXCodec::Create函式

這裡寫圖片描述

每個AwesomePlayer例項只有一個OMX服務的入口,但是AwesomePlayer不一定就只需要1種解碼器。音視訊都要有,部分場景下還有多路音訊,或者多路視訊。
這個時候OMX那邊需要建立不同的解碼器的元件來對應著AwesomePlayer中不同的解碼需求。

OMX中非常重要的2個成員: OMXMaster 和 OMXNodeInstance。

  • OMX通過這倆個成員來建立和維護不同的openmax 解碼器元件,為AwesomePlayer中不同解碼需求提供服務。
    OMXNodeInstance 負責建立並維護不同的例項,這些例項是根據實際的解碼需求建立的,以node_id作為唯一標識。
  • 這樣解碼器元件中每個OMXCodec在OMX服務端都對應有了自己的OMXNodeInstance例項。AwesomePlayer就可以根據這個OMXNodeInstance來操作相應解碼器
  • OMXMaster 維護底層軟硬體解碼庫,是對解碼器元件的一個大管家,根據OMXNodeInstance中想要的解碼器來建立解碼實體元件。

所以我們要追蹤下OMXMaster和OMXNodeInstance。
OMX建構函式中會進行初始化。
OMX.cpp中

這裡寫圖片描述

OMXMaster.cpp中

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

到這裡,我們就明白了AwesomePlayer是如何利用具體的硬體平臺上的硬體解碼器。

那麼針對不同的檔案格式,如何選擇具體的解碼器元件呢?
繼續順著前面的OMXCodec::Create介紹。看一下allocateNode
在OMX.cpp中

這裡寫圖片描述

會呼叫makeComponentInstance函式

這裡寫圖片描述

這樣就實現了根據檔案編碼格式,對具體解碼器的連線
接下來了解下OMXcodec如何註冊和初始化OMX所需要的回撥函式。
OMX服務主要完成三個任務: NodeInstance列表的管理,NodeInstance的操作, 事件的處理。

NodeInstance列表的管理

  • OMX對解碼器元件component的使用,是通過OMXNodeInstance來實現的。
    OMXNodeInstance自身的動作包括NodeInstance的生成(allocateNode)和刪除(freeNode)。
    其實就是對mDispatchers和 mNodeIDToInstance進行新增和刪除。
  • mNodeIDToInstance就是一個key為node_id,value為 NodeInstance的名值對列表。
    而mDispatchers就是一個key為node_id,value為 OMX::CallbackDispatcher的名值對列表。
    並且,一個NodeInstance都擁有一個 OMX::CallbackDispatcher。
  • CallbackDispatcher的作用主要是解碼器元件component發出回撥動作後,將message分發給對應的OMXcodec客戶端。

NodeInstance節點的操作

OMXNodeInstance主要成員函式如下:

這裡寫圖片描述

這些方法執行時,都是先通過findInstance在mNodeIDToInstance列表中找到對應的NodeInstance,然後呼叫NodeInstance對應的方法。

  • OMXCodec對具體的component方法的操作,是通過OMXNodeInstance來實現的。
    如fillBuffer,emptybuffer,sendCommand等,都是通過OMX_Core.h中的巨集定義間接呼叫OMX_Component.h中的
  • OMX_COMPONENTTYPE這個struct中的相應函式指標來完成。OMX_Core.h和 OMX_Component.h都是OpenMAX標準標頭檔案。
    在OMXNodeInstance.cpp中的這樣一段程式碼:

這裡寫圖片描述

  • 它把三個OMXNodeInstance類的靜態方法註冊給了kCallbacks。
  • kCallbacks實際就是struct OMX_COMPONENTTYPE和struct OMX_CALLBACKTYPE的具體實現。
  • 而這兩者就是在OMX_Core.h和 OMX_Component.h中定義的。

kCallbacks在哪裡使用呢?看一下OMX.cpp中的allocateNode方法中的程式碼:

這裡寫圖片描述

事件處理函式傳給了元件ComponentInstance。也就是傳給了具體晶片平臺相關的OMX IL 層。
當元件有事件發生時,就會呼叫OMXNodeInstance中這幾個註冊過的事件處理函式:

這裡寫圖片描述

而這幾個函式又會去呼叫OMX中對應的函式,也就是下面這三個:

這裡寫圖片描述

總結以上程式碼如下:

  • 這幾個函式都採用相同的方式:
  • 根據node_id找到CallbackDispatcher,並把事件資訊post過去。
    也就是:findDispatcher(node)->post(msg)。

進一步,須要瞭解CallbackDispatcher的實現機制。它內部開啟了一個執行緒,使用了訊號量機制(signal)。
findDispatcher(node)->post(msg)是一個非同步操作,只把msg給POST過去,不會等待事件處理完畢就返回了。

問題來了,那麼CallbackDispatcher是怎麼處理接收到的msg呢?
看以下程式碼:

這裡寫圖片描述

這樣事件最終還是跨Binder又傳到了OMXCodec裡面去,交給OMXCodecObserver了。也就是交給了呼叫OMX service的client端了。
將回調的發生,從service端傳送到了client端。

最後,總結一下前面:

  • 1、AwesomePlayer初始化過程中,中通過initVideoDecoder / init AudioDecoder來建立video/Audio解碼器mVideoSource/mAudioSource
  • 2、mVideoSource 中通過mVideoTrack來demux 媒體檔案,從中獲得檔案編碼格式,繼而得到需要的解碼器型別,通過型別呼叫omx->allocateNode 建立OMX node例項與編碼格式對應。以後都是通過node例項來操作實際的硬體解碼器。
  • 3、MediaPlayerService物件初始化的時候會建立OMX物件,OMX物件的建構函式會建立mMaster,mMaster負責獲得與管理硬體平臺的硬體解碼器元件庫。
  • 4、在 omx->allocateNode中 通過mMaster->makeComponentInstance 來建立真正對應的解碼器元件。這個解碼器元件是完成之後實質的解碼工作的。
  • 5、在建立mMaster->makeComponentInstance過程中,也是通過上面mVideoTrack 過來的解碼器型別名,找到相對應的解碼器的庫,然後例項化。
  • 6、解碼Component通過輸入Port和輸出Port來進行互動,通過和OMXCodec共享buffer來進行編解碼。
  • 7、AwesomePlayer中包含了mVideoSource,初始化時指向OMXCodec的實際物件。OMXCodec使用了Binder機制,實現了對OMX服務的遠端呼叫,其中IOMX作為介面類定義了OMX的大部分介面函式。
  • 8、OMX的具體實現時,OMXMaster類用於管理OMX的外掛,OMXNodeInstance類代表OMX的具體例項,完成和Component的呼叫和互動,
  • 9、CallbackDispatcher用於排程處理回撥函式傳回的訊息。OMXNodeInstance和CallbackDispatcher一一對應,協同工作,完成不同例項的訊息處理。
  • 10、OMXNodeInstance是OMX端的概念,是service端的概念。其service端與OMX在一個程序空間中。
  • 11、OMXObserver是OMXCodec端的概念,是client端的概念。其service端與OMXCodec在一個程序空間中。其Bn,Bp 方向和OMX,OMXNodeInstance相反。主要是用來反向通知onMessage訊息
    到此,我們就介紹完了awesomeplayer是如何對OMX初始化,以及如何關聯到對應硬體平臺上的HW decoder的。

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

這裡寫圖片描述

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