1. 概述
開放多媒體加速層(Open Media Acceleration斑芜,縮寫為OpenMAX).一個免費的跨平臺抽象軟件層肩刃,用于加速在嵌入式和移動設備上的多媒體應用程序中捕獲和呈現(xiàn)音頻、視頻和圖像杏头∮基于其強大的可移植性,在手持式設備和嵌入式設備的開發(fā)上醇王,OpenMax 被廣泛地采用呢燥。
它是由Khronos Group提出的標準,也由他們來維持寓娩,目標在于創(chuàng)造一個統(tǒng)一的接口叛氨,加速大量多媒體資料的處理。
1.1 作用
- 加速跨OS和silicon平臺的多媒體組件的開發(fā)棘伴、整合和編程
- 使library和codec實現(xiàn)者能夠快速有效的利用新silicon的潛在的加速功能寞埠,而不關心下層的硬件結(jié)構
1.2 分層
-
OpenMAX AL (Application Layer)
應用層,當前最新版本1.1.- The OpenMAX AL 1.1 Reference Guide.
- OpenMAX-AL提供了應用程序和多媒體中間件之間的標準化接口焊夸,其中多媒體中間件提供了執(zhí)行預期API功能所需的服務仁连。
- OpenMAX-AL為多媒體接口提供了應用程序的可移植性。
-
OpenMAX IL (Integration Layer)
集成層阱穗,當前最新版本1.1- OpenMax IL component sample
- OpenMAX IL 1.0 Specification
- OpenMAX IL(集成層)API定義了一個標準化的媒體組件接口饭冬,使開發(fā)人員和平臺提供商能夠與硬件或軟件中實現(xiàn)的多媒體編解碼器進行集成和通信。
- 它使應用程序和媒體框架能夠以統(tǒng)一的方式與多媒體編解碼器和支持組件(即源和匯)接口揪阶。編解碼器本身可以是硬件或軟件的任何組合伍伤,并且對用戶完全透明。
- IL的主要目標是使用一系列專門的特性遣钳,為編解碼器提供一定程度的系統(tǒng)抽象扰魂,這些特性經(jīng)過磨練,可以解決許多迥然不同的媒體系統(tǒng)之間的可移植性問題蕴茴。
-
OpenMAX DL (Development Layer)
開發(fā)層劝评,當前最新版本是1.0.2- OpenMAX-DL定義的API,包含一套全面的音頻倦淀、視頻和圖像功能蒋畜,這些功能可以由芯片供應商在新處理器上實現(xiàn)和優(yōu)化,然后由編解碼器供應商用來編寫各種編解碼器功能撞叽。
- 它包括音頻信號處理功能(如fft和濾波器)姻成、圖像處理(如色域轉(zhuǎn)換)和視頻處理插龄,以實現(xiàn)諸如MPEG-4、H.264科展、MP3均牢、AAC和JPEG等編解碼器的優(yōu)化實現(xiàn)。
目前普遍使用的是 IL 層才睹。IL 層作為在嵌入式和移動設備中使用的 audio徘跪、video、image codecs 的底層接口琅攘,使得應用和多媒體框架可以用統(tǒng)一的方式訪問多媒體 codec 等相關組間垮庐。每一個 component 可以是軟件和硬件加速的任意組合,對用戶透明坞琴。用戶只需要按照 OpenMax 規(guī)定的 API 來調(diào)用即可哨查。
2. 結(jié)構
OpenMAX IL API致力于為媒體編解碼器提供跨平臺陣列的可移植性。該接口抽象了系統(tǒng)的硬件和軟件體系結(jié)構剧辐。每個編解碼器和相關轉(zhuǎn)換都封裝在組件接口中寒亥。OpenMAX IL API允許用戶加載、控制浙于、連接和卸載各個組件护盈。這種靈活的核心架構允許集成層輕松實現(xiàn)幾乎任何媒體用例,并與現(xiàn)有的基于圖形的媒體框架相結(jié)合羞酗。
IL包含了一個專門的功能庫腐宋,這些功能經(jīng)過磨練,可以解決許多迥然不同的媒體系統(tǒng)之間的可移植性問題檀轨。這些特征包括:
- 靈活的基于組件的API核心
- 能夠輕松外掛新的編解碼器
- 在保持由Khronos集團和單個供應商可以簡單進行擴展的同時胸竞,覆蓋目標領域(音頻、視頻和圖像)
- 不管是靜態(tài)庫還是動態(tài)庫都可以被實現(xiàn)
- 保留父軟件(如媒體框架)所需的關鍵功能和配置選項
- 客戶端和編解碼器之間以及編解碼器之間的通信更加方便
2.1 框架
OpenMax IL API 的軟件結(jié)構:
OpenMAX IL-API是一種基于組件的媒體API参萄,由兩個主要部分組成:核心API和組件API卫枝。
2.2 核心API
OpenMax IL Core 用來動態(tài)加載和卸載組件,并促進組件通信讹挎。
- 一旦加載校赤,用戶可以直接與組件通信,從而消除了很多復雜命令的高消耗筒溃。
- 一旦加載马篮,用戶可以在兩個組件中間建立溝通隧道,兩個組件直接通信
2.2 組件API
OpenMax IL Component 做為一個單元怜奖,一個組件實現(xiàn)一個功能浑测,可以作為以下角色:
- sources
- sinks
- codecs
- filters
- splitters
- mixers或者其他的數(shù)據(jù)處理模塊
具體取決于其實現(xiàn)。但是在我們的多媒體處理當中歪玲,一個組件迁央,很可能是某個硬件掷匠,軟件編解碼器,處理器或者以上組合岖圈。 系統(tǒng)組件概述讹语。 參數(shù)描述包括 Buffer狀態(tài),錯誤幅狮,一系列的回調(diào)函數(shù)等募强。組件之間的通信接口株灸,稱之為Port崇摄,代表組件和數(shù)據(jù)流之間的鏈接,以及保持鏈接所需要維護的buffers慌烧。
組件的結(jié)構如下:
例如一個需要實現(xiàn)四個多媒體處理功能的系統(tǒng)逐抑,這些功能表示為F1、F2屹蚊、F3和F4厕氨。這些功能可能來自不同的供應商,也可能由內(nèi)部開發(fā)汹粤,但由組織內(nèi)的不同團隊開發(fā)命斧。
對于安裝和拆卸,每種可能有不同的要求嘱兼。每種方法都有不同的方法來促進配置和數(shù)據(jù)傳輸国葬。OpenMAX IL-API提供了一種將這些函數(shù)單獨或以邏輯組封裝到組件中的方法。
該API包括一個標準協(xié)議芹壕,該協(xié)議允許可能來自不同供應商/組的兼容組件彼此交換數(shù)據(jù)并可交換使用汇四。
在包含四個多媒體處理函數(shù)F1、F2踢涌、F3和F4的系統(tǒng)中通孽,系統(tǒng)實現(xiàn)者可以為每個函數(shù)提供標準的OpenMAX接口。實現(xiàn)者可以很容易地選擇函數(shù)的任何組合睁壁。此功能的劃分基于端口背苦。
這些功能的可能組合如下:
2.3 端口
與組件之間的數(shù)據(jù)通信接口。
- 表示組件到數(shù)據(jù)流的連接
- 表示維護連接所需的緩沖區(qū)
- 可以通過輸入端口向組件發(fā)送數(shù)據(jù)
- 可以通過輸出端口接收數(shù)據(jù)
2.4 隧道
通信隧道通過將一個組件的輸出端口連接到另一個組件的類似格式的輸入端口潘明,可以建立兩個組件之間的關系行剂。
如下圖,可以看到OpenMax所支持的各種類型的通信:
OpenMAL IL的客戶端钉疫,通過調(diào)用四個OpenMAL IL組件硼讽,實現(xiàn)了一個功能。四個組件分別是Source組件牲阁、Host組件固阁、Accelerator組件和Sink組件壤躲。Source組件只有一個輸出端口;而Host組件有一個輸入端口和一個輸出端口;Accelerator組件具有一個輸入端口,調(diào)用了硬件的編解碼器备燃,加速主要體現(xiàn)在這個環(huán)節(jié)上碉克。Accelerator組件和Sink組件通過私有通訊方式在內(nèi)部進行連接,沒有經(jīng)過明確的組件端口并齐。
3. 工作流
3.1 狀態(tài)機
- 初始狀態(tài)unloaded
- 通過調(diào)用core 進入loaded
- 其他狀態(tài)的轉(zhuǎn)換可以通過與組件直接通信實現(xiàn)
- 使用無效數(shù)據(jù)進行狀態(tài)轉(zhuǎn)換時漏麦,進入invalid 狀態(tài)
- 進入invalide 狀態(tài)后,只有卸載組件况褪,進入unloaded狀態(tài)撕贞,才能退出invalid狀態(tài)
- 從idle到loaded的狀態(tài),將導致諸如通信緩沖區(qū)之類的操作資源丟失
- 從executing 或者 paused 到 idle 將造成處理緩沖區(qū)的上下文丟失
3.2 數(shù)據(jù)流
3.2.1 交互
- 當我們從OpenMax Core獲得句柄之后测垛,對組件進行相關配置,并完成
- 可以與組件進行數(shù)據(jù)通信捏膨,該通信是非阻塞的異步回調(diào)
- 輸入端口總是從帶有OMX_EmptyThisBuffer的IL客戶端調(diào)用
- 輸出端口總是通過OMX_FillThisBuffer從IL客戶端調(diào)用
- 在上下文內(nèi)實現(xiàn)中,將在返回結(jié)果之前回調(diào)OMX_EmptyBufferDone或OMX_FillBufferDone食侮。
3.2.2 Buffer Allocation and share
-
嚴格的來說号涯,組件只需要根據(jù)外部調(diào)用的要求,進行如下操作:
- Provide buffers on all of its supplier ports.
- Accurately communicate buffer requirements on its ports.
- Pass a buffer from an output port to an input port with an OMX_EmptyThisBuffer call.
- Return a buffer from an input port to an output port with an OMX_FillThisBuffer call.
-
如果組件想要共享緩沖區(qū)锯七,可以執(zhí)行以下操作:
- Provide re-used buffers on some supplier ports.
- Account for the needs of shared ports when communicating buffer requirements on ports.
- Internally pass a buffer from an input port to an output port between an OMX_EmptyThisBuffer call and its corresponding OMX_EmptyBufferDone call.
3.2.3 端口重新連接
- 端口重新連接使隧道組件可以替換為另一個隧道組件链快,而不必拆下周圍的組件
- 通過disable和enable可以實現(xiàn)組件的拆卸和安裝
- 通過各個組件的拆卸和安裝可以實現(xiàn)多個組件的協(xié)同工作
3.2.4 隊列
- 獨立的命令行隊列,使組件可以刷新沒有處理的buffers眉尸,并返回給客戶端或是通信端口
- 如下圖:組件使用客戶端分配的buffers域蜗,在收到flush命令前收到5個buffers,并已經(jīng)處理了兩個的數(shù)據(jù)效五,收到flush命令之后地消,組件按照原來的順序返回了所有包括未處理的buffers,并觸發(fā)一個結(jié)束的event畏妖,而客戶端會在收到這個event之后再做卸載組件的處理
3.2.5 Events and Callbacks
- 組件會發(fā)送6種evenets給客戶端
- Error events are enumerated and can occur at any time
- Command complete notification events are triggered upon successful execution of a command.
- Marked buffer events are triggered upon detection of a marked buffer by a component.
- A port settings changed notification event is generated when the component changes its port settings.
- A buffer flag event is triggered when an end of stream is encountered.
- A resources acquired event is generated when a component gets resources that it has been waiting for.
3.2.6 Buffer Payload
- buffers填充數(shù)據(jù)的方式通常有三種
- 每個緩沖區(qū)全部或部分填充
- 每個緩沖區(qū)只填充完整的壓縮數(shù)據(jù)幀
- 每個緩沖區(qū)只填充一幀壓縮數(shù)據(jù)
3.3 數(shù)據(jù)結(jié)構
3.3.1 header files
- API被定義在如下頭文件:
- OMX_Types.h: Data types used in the OpenMAX IL
- OMX_Core.h: OpenMAX IL core API
- OMX_Component.h: OpenMAX component API
- OMX_Audio.h: OpenMAX audio domain data structures
- OMX_IVCommon.h: OpenMAX structures common to image and video domains
- OMX_Video.h: OpenMAX video domain data structures
- OMX_Image.h: OpenMAX image domain data structures
- OMX_Other.h: OpenMAX other domain data structure(includes A/V synchronization)
- OMX_Index.h: Index of all OpenMAX-defined data structures
3.3.2 Types
3.3.3 Methods
更多詳細內(nèi)容可以在頭文件中進一步確認
3.4 調(diào)用隊列
-
初始化
- OMX_GetHandle
- SetCallbacks
- in OMX_StateLoaded
- OMX_SetParameter
- OMX_AllocateBuffer/ OMX_UseBuffer
- reveive command
-
數(shù)據(jù)流
- OMX_EmptyThisBuffer
- OMX_FillThisBuffer
- OMX_FillBufferDone
- OMX_EmptyBufferDone
-
卸載
- switch to OMX_StateIdle state
- OMX_FreeBuffer
- OMX_FreeHandle
-
端口的禁用(disable)
- 狀態(tài)切換到OMX_StateLoaded狀態(tài)
- 緩沖區(qū)返還客戶端
- 釋放所有被禁用端口分配的緩沖區(qū)
- 若端口在OMX_StateLoaded時被禁用脉执,則后續(xù)操作會受到影響無法繼續(xù)
-
端口的啟用(enable)
- 退出OMX_StateLoaded狀態(tài)
- 開始交換緩沖區(qū)
- 開始獲取緩沖區(qū)
-
動態(tài)配置
- 視頻解碼器解析序列頭發(fā)現(xiàn)輸出文件的幀大小與設置的大小不匹配,輸出端口buffer需要重新配置時
- 音頻流的參數(shù)動態(tài)變化時
- 過程
- 視頻解碼器收到一個輸入流戒劫,當前在OMX_StateExecuting狀態(tài)半夷,輸入輸出端口還沒有配置
- 解碼器收到序列頭,解析出文件的幀大小等信息迅细,并配置給端口
- 通知客戶端通過OMX_PortSettingsChanged
- 客戶端收到通知后巫橄,disable input和output 端口
- 客戶端通過OMX_GetConfig獲取當前新的配置
- 通過OMX_UseBuffer 申請新的buffer
- 通過OMX_SetConfig 設置新的配置
- 重新enable input 和 output 端口
-
資源管理器
- 資源管理器作為內(nèi)部接口,IL客戶端可以不做關注
4. 應用
4.1 Video編碼(AVC)
- 初始化
result = OMX_Init(); result = OMX_GetHandle(&m_Handle, (OMX_STRING)"OMX.qcom.video.encoder.avc", NULL, &callbacks);
- 配置
- SetFrameScale
Scale和crop設置需要在InputPort和OutputPort配置之前,若cropFrame的配置寬高不為0茵典,則scale的寬高要與crop的寬高相同if (crop_width != 0 && crop_height != 0) { width = crop_width; height = crop_height; } QOMX_INDEXDOWNSCALAR downscalar; OMX_INIT_STRUCT(&downscalar, QOMX_INDEXDOWNSCALAR); downscalar.nPortIndex = (OMX_U32)PORT_INDEX_OUT; downscalar.bEnable = OMX_TRUE; downscalar.nOutputWidth = (OMX_U32)width; downscalar.nOutputHeight = (OMX_U32)height; result = OMX_SetParameter(m_Handle, (OMX_INDEXTYPE)OMX_QcomIndexParamVideoDownScalar, (OMX_PTR)&downscalar);
- CropFrame
打開cropframe的配置開關湘换,具體參數(shù)隨入幀一起輸送給omxOMX_ERRORTYPE result = OMX_ErrorNone; QOMX_INDEXEXTRADATATYPE e; // OMX_QcomIndexParamIndexExtraDataType OMX_INIT_STRUCT(&e, QOMX_INDEXEXTRADATATYPE); e.nPortIndex = (OMX_U32)PORT_INDEX_IN; e.nIndex = (OMX_INDEXTYPE)OMX_ExtraDataFrameDimension; e.bEnabled = OMX_TRUE; result = OMX_SetParameter(m_Handle, (OMX_INDEXTYPE)OMX_QcomIndexParamIndexExtraDataType, (OMX_PTR)&e);
- SetInPortParameters
OMX_INIT_STRUCT(&portdef, OMX_PARAM_PORTDEFINITIONTYPE); portdef.nPortIndex = (OMX_U32) PORT_INDEX_IN; // input result = OMX_GetParameter(m_Handle, OMX_IndexParamPortDefinition, &portdef); portdef.format.video.nFrameWidth = nWidth; portdef.format.video.nFrameHeight = nHeight; portdef.format.video.eColorFormat = (OMX_COLOR_FORMATTYPE)nFormat; FractionToQ16(portdef.format.video.xFramerate, static_cast<int>(nFrameRate * 2), 2); result = OMX_SetParameter(m_Handle, OMX_IndexParamPortDefinition, &portdef);
- SetOutPortParameters
OMX_INIT_STRUCT(&portdef, OMX_PARAM_PORTDEFINITIONTYPE); result = OMX_GetParameter(m_Handle, OMX_IndexParamPortDefinition, &portdef); portdef.nPortIndex = (OMX_U32) PORT_INDEX_OUT; portdef.format.video.nFrameWidth = nWidth; portdef.format.video.nFrameHeight = nHeight; portdef.format.video.nBitrate = nBitRate; FractionToQ16(portdef.format.video.xFramerate, static_cast<int>(nFrameRate * 2), 2); result = OMX_SetParameter(m_Handle, OMX_IndexParamPortDefinition, &portdef);
- ConfigAVC
OMX_INIT_STRUCT(&level, OMX_VIDEO_PARAM_PROFILELEVELTYPE); level.nPortIndex = (OMX_U32) PORT_INDEX_OUT; level.eProfile = userProfile; level.eLevel = eLevel; result = OMX_SetParameter(m_Handle, OMX_IndexParamVideoProfileLevelCurrent, &profileLevel); OMX_INIT_STRUCT(&avcdata, OMX_VIDEO_PARAM_AVCTYPE); avcdata.nPortIndex = (OMX_U32)PORT_INDEX_OUT; result = OMX_GetParameter(m_Handle, OMX_IndexParamVideoAvc, &avcdata); avcdata.nPFrames = AVC_DEFAULT_P_FRAME_NUM; // update by user avcdata.nBFrames = AVC_DEFAULT_B_FRAME_NUM; // update by user avcdata.bUseHadamard = OMX_FALSE; avcdata.nRefFrames = AVC_DEFAULT_REF_FRAME_NUM; ...... avcdata.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterDisable; result = OMX_SetParameter(m_Handle, OMX_IndexParamVideoAvc, &avcdata);
- SetBitRate
OMX_INIT_STRUCT(&bitrate, OMX_VIDEO_PARAM_BITRATETYPE); bitrate.nPortIndex = (OMX_U32)PORT_INDEX_OUT; result = OMX_GetParameter(m_Handle, OMX_IndexParamVideoBitrate, (OMX_PTR)&bitrate); if (bSetEControlRate) { bitrate.eControlRate = (OMX_VIDEO_CONTROLRATETYPE)eControlRate; // ControlRate } bitrate.nTargetBitrate = nBitRate; result = OMX_SetParameter(m_Handle, OMX_IndexParamVideoBitrate, (OMX_PTR)&bitrate);
- ......
- SetFrameScale
- 狀態(tài)遷移
- 不需要等待
result = OMX_SendCommand(m_Handle, OMX_CommandStateSet,OMX_StateIdle, NULL);
- 配置buffer
- 這里OMX提供了兩個接口,根據(jù)自己的需求選擇。
- OMX_UseBuffer
- buffer由客戶端申請管理彩倚,并通過該接口交換給OMX
for (i = 0; i < m_InputBufferCount; i++) { result = OMX_UseBuffer(m_Handle, &m_InputBufferHeaders[i], (OMX_U32) PORT_INDEX_IN, NULL, m_InputBufferSize, m_InputBuffer + m_InputBufferSize * i); } for (i = 0; i < m_OutputBufferCount; i++) { result = OMX_UseBuffer(m_Handle, &m_OutputBufferHeaders[i], (OMX_U32) PORT_INDEX_OUT, NULL, m_OutputBufferSize, m_OutputBuffer + m_OutputBufferSize * i); CHECK_RESULT("use output buffer failed", result); }
- 如果涉及到extradata筹我,MetaMode一定不要為它們申請整塊的緩存,不利于后續(xù)extradata的靈活配置
- allocate buffer manager 需要與hardware 適配帆离,ION/GBM等選擇要慎重靈活
- OMX_AllocateBuffer
- 由OMX進行buffer申請和管理
for (i = 0; i < m_InputBufferCount; i++) { result = OMX_AllocateBuffer(m_Handle, &m_InputBufferHeaders[i], (OMX_U32) PORT_INDEX_IN, NULL, m_InputBufferSize); } for (i = 0; i < m_OutputBufferCount; i++) { result = OMX_AllocateBuffer(m_Handle, &m_OutputBufferHeaders[i], (OMX_U32) PORT_INDEX_OUT, NULL, m_OutputBufferSize); CHECK_RESULT("use output buffer failed", result); }
- 狀態(tài)遷移
- 需要等待
result = OMX_SendCommand(m_Handle, OMX_CommandStateSet,OMX_StateExecuting, NULL);
- 編碼
-
啟動兩個線程完成Input和Output的工作
-
- 編碼結(jié)束蔬蕊,狀態(tài)遷移
result = OMX_SendCommand(m_Handle, OMX_CommandStateSet,OMX_StateIdle, NULL);
- 資源釋放
若是客戶端申請的buffer,由客戶端主動釋放for (i = 0; i < m_InputBufferCount; i++) { if (m_InputBufferHeaders[i] != NULL) { result = OMX_FreeBuffer(handle, PORT_INDEX_IN, m_InputBufferHeaders[i]); CHECK_RESULT("free input buffer header failed", result); } } for (i = 0; i < m_OutputBufferCount; i++) { if (m_OutputBufferHeaders[i] != NULL) { result = OMX_FreeBuffer(handle, PORT_INDEX_OUT, m_OutputBufferHeaders[i]); CHECK_RESULT("free output buffer header failed", result); } }
- 卸載
if (m_Handle != NULL) { OMX_FreeHandle(m_Handle); m_Handle = NULL; }