一、OpenMax簡介
OpenMAX是一個多媒體應用程式的标準。由NVIDIA公司和Khronos™在2006年推出。
它是無授權費的、跨平台的C語言程式接口序列,這些接口對音頻、視訊、靜态圖檔的常用操作進行封裝。
它包括三層,分别是應用層(AI)、內建層(IL)和開發層(DL)。其中IL層已經成為了事實上的多媒體架構标準。嵌入式處理器或者多媒體編解碼子產品的硬體生産者,通常提供标準的OpenMax IL層的軟體接口,這樣軟體的開發者就可以基于這個層次的标準化接口進行多媒體程式的開發。
二、OpenMAX/IL: OMX IL - 結構架構
OpenMAX IL 層 API 旨在為媒體元件提供跨平台的可移植能力。這些接口将系統的軟硬體結構進行抽象化。每個元件及其相關的轉換都被封裝在元件接口的内部。OpenMAX IL API 允許使用者去加載,控制,連接配接以及解除安裝各獨立的元件。這種極具靈活性的核心結構使得 Intergration Layer 能夠很容易的實作幾乎所有的多媒體應用情形,并且能夠很好的與現有的基于圖像的多媒體架構相結合。
1. 主要的功能和優點
OpenMAX IL API 能夠在應用程式、多媒體架構和編解碼庫,以及其支援的元件(比如,sources 和 sinks)之間建立統一的接口。對于使用者來說,元件自身及其内部的軟硬體結合情況都是完全透明的。其主要功能如下:
• A flexible component-based API core;
• Ability to easily plug in new components ;
• Coverage of targeted domains (audio, video, and imaging) while remaining easily extensible by both the Khronos Group and individual vendors;
• Capable of being implemented as either static or dynamic libraries;
• Retention of key features and configuration options needed by parent software (such as media frameworks);
• Ease of communication between the client and the components and between components themselves;
• Standardized definition of key components so all implementations of such “standard components” expose the same external interface (i.e. same inputs, outputs, and controls).
2. OpenMAX IL 軟體結構
OpenMax的主要概念
用戶端(Client):通路IL core或IL component的軟體層,可能是位于GUI應用程式的下層,如GStreamer。IL client是一個典型的功能塊,如filter graph multimedia framework, OpenMAX AL, 或application都可以調用它。IL client與OpenMAX IL core進行互動,利用IL core加載和解除安裝元件、在元件間建立直接通信以及獲得元件方法的入口。
core:相關平台的代碼,具有将IL component載入主存儲器的功能,當應用程式不再需要某元件時,IL core将負責把該元件從存儲器卸去。一般來說,元件一旦載入存儲器,IL core将不在參與應用程式與元件之間的通信。
端口(Port):元件的輸入輸出接口
元件(Component):OpenMax IL的單元,每一個元件實作一種功能。元件按照端口可分類為Source(隻有一個輸出端口)、Sink(隻有一個輸入端口)和Host元件(一個輸入端口和一個輸出端口),此外有一個Accelerator元件,它具有一個輸入端口,調用了硬體的編解碼器,加速主要展現在這個環節上。
隧道化(Tunneled):讓兩個元件直接連接配接的方式。通過隧道化可以将不同的元件的一個輸入端口和一個輸出端口連接配接到一起,在這種情況下,兩個元件的處理過程合并,共同處理。尤其對于單輸入和單輸出的元件,兩個元件将作為類似一個使用。
3. OpenMAX IL 接口
core API:負責動态加載和解除安裝元件,協助元件間通信。一旦加載元件,API 允許使用者直接與元件進行通信,類似的,core API 允許使用者在元件之間建立tunnel通信通道,一旦建立,core API 不再被需要,其通信直接發生在兩個元件之間。
component API:在IL層,元件代表了獨立的功能子產品。一個元件可以是sources, sinks, codecs, filters, splitters, mixers, or any other data operator.各個元件的參數可以通過一組相關的資料結構,枚舉類型和接口來設定或者擷取。buffer狀态,錯誤資訊,以及其他的時間敏感資訊會通過回調函數轉發給應用程式。與元件進行資料的交換是通過端口(ports)完成的。類似哦,元件間的tunnel通道也是通過将一個元件的輸出端口連接配接到另一個元件的輸入端口來建立的。
4. System 元件
OpenMAX IL 定義了三種通信方式:
1)Non-tunneled:用于client 與 component 之間交換data buffers;
2)Tunneling:用于元件之間互相交換data buffers的标準機制;
3)Proprietary communication:用于兩個元件之間直接資料交換的專屬機制,并且可以作為tunneling的備選機制。
openMAX IL的用戶端,通過調用四個OpenMAX IL元件,實作了一個功能。四個元件分别是Source元件、Host元件、Accelerator元件和Sink元件。
· Source元件隻有一個輸出端口;
· Host元件有一個輸入端口和一個輸出端口;
· Accelerator元件具有一個輸入端口,調用了硬體的編解碼器,加速主要展現在這個環節上。Accelerator元件和Sink元件通過私有通訊方式在内部進行連接配接,沒有經過明确的元件端口。
OpenMAL IL在使用的時候,其資料流也有不同的處理方式:既可以經由用戶端,也可以不經由用戶端。 圖中,Source元件到Host元件的資料流就是經過用戶端的; 而Host元件到Accelerator元件的資料流就沒有經過用戶端,使用了隧道化的方式; Accelerator元件和Sink元件甚至可以使用私有的通訊方式。
OpenMAL IL的元件是OpenMax IL實作的核心内容,一個元件以輸入、輸出端口為接口,端口可以被連接配接到另一個元件上。外部對元件可以發送指令,還進行設定/擷取參數、配置等内容。元件的端口可以包含緩沖區(Buffer)的隊列。 元件的處理的核心内容是:通過輸入端口消耗Buffer,通過輸出端口填充Buffer,由此多元件相聯接可以構成流式的處理。
5. Component 架構
OpenMAL IL中一個元件的結構如圖
元件的功能和其定義的端口類型密切相關,通常情況下:
隻有一個輸出端口的,為Source元件; 隻有一個輸入端口的,為Sink元件; 有多個輸入端口,一個輸出端口的為Mux元件; 有一個輸入端口,多個輸出端口的為DeMux元件; 輸入輸出端口各一個元件的為中間處理環節,這是最常見的元件。
端口具體支援的資料也有不同的類型。例如,對于一個輸入、輸出端口各一個元件,其輸入端口使用MP3格式的資料,輸出端口使用PCM格式的資料,那麼這個元件就是一個MP3解碼元件。
隧道化(Tunneled)是一個關于元件連接配接方式的概念。通過隧道化可以将不同的元件的一個輸入端口和一個輸出端口連接配接到一起,在這種情況下,兩個元件的處理過程合并,共同處理。尤其對于單輸入和單輸出的元件,兩個元件将作為類似一個使用。
7. Client與component之間的通信
client通過OMX_EmptyThisBuffer來調用component的輸入端口;
client通過OMX_FillThisBuffer來調用component的輸出端口。
8. Tunneled buffer allocation
對于tunnel的兩個端口,supplier端口會調用UseBuffer函數來要求鄰接的端口來處理buffers;non-supplier端口會接受UseBuffer調用。Component需要遵循以下規則:
1)supplier端口都要提供buffers;
2)在端口上可靠的傳輸buffer配置;
3)通過OMX_EmptyThisBuffer調用将buffer從輸出端口傳遞到另一component的輸入端口;
4)通過OMX_Fill_This_Buffer調用将buffer從輸入端口傳回給component的輸出端口。
9. Buffer payload
一般情況下,buffer中可用資料的起始點和範圍由定義在buffer頭中的 pBuffer,nOffset 和 nFilledLen 三個參數來決定。pBuffer指向buffer的起始位址;nOffset代表了buffer起始位址與實際可用資料位址之間的偏移量;nFilledLen表示buffer中連續可用的資料的大小。是以,buffer中可用資料的起始範圍分别為pBuffer + nOffset 和 pBuffer + nOffset + nFilledLen 。
在buffer中資料的存放方式有三種:
1)每個buffer要麼填滿,要麼部分填滿;
2)每個buffer中存放的壓縮資料都是以完整的幀為機關的;
3)每個buffer中隻存放一幀的壓縮資料。
前兩種都要求解碼器在解碼的之前對每幀資料進行解析,第三種情況則不需要解析。
10. Buffer flags and timestamps
Buffer flags 是存放在buffer中的表示特定屬性的資料,比如資料流結束;
Timestamps 是以微秒為機關的存放在buffer中的資料,用來在播放時确定各buffer的播放時刻。
三、接口與頭檔案
1. OpenMAX IL 層的接口定義是由若幹個頭檔案的形式給出的,在頭檔案中定義了一些結構體和需要開發者實作的接口函數,包括:
· OMX_Types.h:OpenMax Il的資料類型定義
· OMX_Core.h:OpenMax IL核心的API
· OMX_Component.h:OpenMax IL 元件相關的 API
· OMX_Audio.h:音頻相關的常量和資料結構
· OMX_IVCommon.h:圖像和視訊公共的常量和資料結構
· OMX_Image.h:圖像相關的常量和資料結構
· OMX_Video.h:視訊相關的常量和資料結構
· OMX_Other.h:其他資料結構(包括A/V 同步)
· OMX_Index.h:OpenMax IL定義的資料結構索引
· OMX_ContentPipe.h:内容的管道定義
提示:OpenMax标準隻有頭檔案,沒有标準的庫,設定沒有定義函數接口。對于實作者,需要實作的主要是包含函數指針的結構體
2. 在OMX_Core.h中定義了Core的API函數,應用程式通過它可以進行初始化、處理handle等操作,具體内容如下:
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void); // 初始化OMX Core,且應該是OMX中第一個被調用的函數;
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void); // 反初始化OMX Core,且應該是OMX中最後一個被;
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( // 列出系統中所有可用component的名稱;
OMX_OUT OMX_STRING cComponentName,
OMX_IN OMX_U32 nNameLength,
OMX_IN OMX_U32 nIndex);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( // 根據名稱查找component,并調用component的方法來執行個體化component;
OMX_OUT OMX_HANDLETYPE* pHandle,
OMX_IN OMX_STRING cComponentName,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_CALLBACKTYPE* pCallBacks);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle(
OMX_IN OMX_HANDLETYPE hComponent);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( // 在兩個component之間建立tunnel連接配接
OMX_IN OMX_HANDLETYPE hOutput,
OMX_IN OMX_U32 nPortOutput,
OMX_IN OMX_HANDLETYPE hInput,
OMX_IN OMX_U32 nPortInput);
OMX_API OMX_ERRORTYPE OMX_GetContentPipe(
OMX_OUT OMX_HANDLETYPE *hPipe,
OMX_IN OMX_STRING szURI);
OMX_API OMX_ERRORTYPE OMX_GetComponentsOfRole (
OMX_IN OMX_STRING role,
OMX_INOUT OMX_U32 *pNumComps,
OMX_INOUT OMX_U8 **compNames);
OMX_API OMX_ERRORTYPE OMX_GetRolesOfComponent (
OMX_IN OMX_STRING compName,
OMX_INOUT OMX_U32 *pNumRoles,
OMX_OUT OMX_U8 **roles);
當應用程式需要使用某個component的功能時,其首先需要調用OMX_Init()來對OMX Core進行初始化,然後通過OMX_GetHandle()來執行個體化component,取得相應的handle。handle實際上是一個指向component對象的void類型指針,其在OMX_Type.h中定義如下,
typedef void* OMX_HANDLETYPE;
OMX_SetupTunnel()用來在兩個component之間建立tunnel連接配接。
3. 在OMX_Core.h中還定義了資料類型OMX_BUFFERHEADERTYPE,其對象存放在buffer内用來描述該buffer的特性。
具體内容及各字段注釋如下:
typedef struct OMX_BUFFERHEADERTYPE
{
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
OMX_U8* pBuffer;
OMX_U32 nAllocLen;
OMX_U32 nFilledLen;
OMX_U32 nOffset;
OMX_PTR pAppPrivate;
OMX_PTR pPlatformPrivate;
OMX_PTR pInputPortPrivate;
OMX_PTR pOutputPortPrivate;
OMX_HANDLETYPE hMarkTargetComponent;
OMX_PTR pMarkData;
OMX_U32 nTickCount;
OMX_TICKS nTimeStamp;
OMX_U32 nFlags;
OMX_U32 nOutputPortIndex;
OMX_U32 nInputPortIndex;
} OMX_BUFFERHEADERTYPE;
4. OMX_Component.h
1)OMX_COMPONENTTYPE類型資料結構,OMX IL 用它來描述一個component,其中包含了可供調用的函數方法。
[cpp] view plain copy
typedef struct OMX_COMPONENTTYPE
{
OMX_U32 nSize;
OMX_VERSIONTYPE nVersion;
OMX_PTR pComponentPrivate;
OMX_PTR pApplicationPrivate;
OMX_ERRORTYPE (*GetComponentVersion)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STRING pComponentName,
OMX_OUT OMX_VERSIONTYPE* pComponentVersion,
OMX_OUT OMX_VERSIONTYPE* pSpecVersion,
OMX_OUT OMX_UUIDTYPE* pComponentUUID);
OMX_ERRORTYPE (*SendCommand)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_COMMANDTYPE Cmd,
OMX_IN OMX_U32 nParam1,
OMX_IN OMX_PTR pCmdData);
OMX_ERRORTYPE (*GetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nParamIndex,
OMX_INOUT OMX_PTR pComponentParameterStructure);
OMX_ERRORTYPE (*SetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentParameterStructure);
OMX_ERRORTYPE (*GetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_INOUT OMX_PTR pComponentConfigStructure);
OMX_ERRORTYPE (*SetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentConfigStructure);
OMX_ERRORTYPE (*GetExtensionIndex)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_STRING cParameterName,
OMX_OUT OMX_INDEXTYPE* pIndexType);
OMX_ERRORTYPE (*GetState)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STATETYPE* pState);
OMX_ERRORTYPE (*ComponentTunnelRequest)(
OMX_IN OMX_HANDLETYPE hComp,
OMX_IN OMX_U32 nPort,
OMX_IN OMX_HANDLETYPE hTunneledComp,
OMX_IN OMX_U32 nTunneledPort,
OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup);
OMX_ERRORTYPE (*UseBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes,
OMX_IN OMX_U8* pBuffer);
OMX_ERRORTYPE (*AllocateBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes);
OMX_ERRORTYPE (*FreeBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*FillThisBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*SetCallbacks)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCallbacks,
OMX_IN OMX_PTR pAppData);
OMX_ERRORTYPE (*ComponentDeInit)(
OMX_IN OMX_HANDLETYPE hComponent);
OMX_ERRORTYPE (*UseEGLImage)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN void* eglImage);
OMX_ERRORTYPE (*ComponentRoleEnum)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_U8 *cRole,
OMX_IN OMX_U32 nIndex);
} OMX_COMPONENTTYPE;
EmptyThisBuffer和FillThisBuffer是驅動元件運作的基本的機制,前者表示讓元件消耗緩沖區,表示對應元件輸入的内容;後者表示讓元件填充緩沖區,表示對應元件輸出的内容。
UseBuffer,AllocateBuffer,FreeBuffer為和端口相關的緩沖區管理函數,對于元件的端口有些可以自己配置設定緩沖區,有些可以使用外部的緩沖區,是以有不同的接口對其進行操作。
SendCommand表示向元件發送控制類的指令。
GetParameter,SetParameter,GetConfig,SetConfig幾個接口用于輔助的參數和配置的設定和擷取。
ComponentTunnelRequest用于元件之間的隧道化連接配接,其中需要制定兩個元件及其相連的端口。
ComponentDeInit用于元件的反初始化。
提示:OpenMax函數的參數中,經常包含OMX_IN和OMX_OUT等宏,它們的實際内容為空,隻是為了标記參數的方向是輸入還是輸出。
四、Openmax 常用函數介紹
1)OMXConfigParser ()The configuration parser API
函數原型
OMX_BOOL OMXConfigParser ( OMX_PTR aInputParameters,OMX_PTR aOutputParameters);
傳遞參數
aInputParameters 指向如下結構
typedef struct
{
OMX_U8* inPtr; //codec 配置頭部指針
OMX_U32 inBytes; //codec 配置頭部長度
OMX_STRING cComponentRole; //OMX codec類型 eg "video_decoder.mpeg4"
OMX_STRING cComponentName; //OMX 元件名稱
} OMXConfigParserInputs;
傳回值
OMX_FALSE : 處理codec配置頭部錯誤或不支援該格式
OMX_TURE : 正确處理codec配置頭部
函數作用
填充aOutputParameters,有兩種選擇:audio coded or vedio codec
typedef struct
{
OMX_U16 Channels; //通道:單聲道、立體聲、5.1
OMX_U16 BitsPerSample; //位寬(eg16)
OMX_U32 SamplesPerSec; //采樣率
} AudioOMXConfigParserOutputs;
typedef struct
{
OMX_U32 width; //檢測到的視訊剪輯寬度
OMX_U32 height; //檢測到的視訊剪輯高度
OMX_U32 profile; //參數
OMX_U32 level; //級别?
} VideoOMXConfigParserOutputs;
2)OMX_SetParameter
設定某個參數對應的值
#define OMX_SetParameter( /
hComponent, /
nParamIndex, /
pComponentParameterStructure) /
((OMX_COMPONENTTYPE*)hComponent)->SetParameter( /
hComponent, /
nParamIndex, /
pComponentParameterStructure)
3)OMX_SetConfig
設定某個config值
#define OMX_SetConfig( /
hComponent, /
nConfigIndex, /
pComponentConfigStructure) /
((OMX_COMPONENTTYPE*)hComponent)->SetConfig( /
hComponent, /
nConfigIndex, /
pComponentConfigStructure)
注意有時候,需要先停止某個元件(的某些端口),才能設定config 成功
4)OMX_SendCommand
一般的指令有:
OMX_CommandStateSet 、OMX_CommandFlush、OMX_CommandPortDisable" 、 "OMX_CommandPortEnable、CommandMarkBuffer
#define OMX_SendCommand( /
hComponent, /
Cmd, /
nParam, /
pCmdData) /
((OMX_COMPONENTTYPE*)hComponent)->SendCommand( /
hComponent, /
Cmd, /
nParam, /
pCmdData)
例子:OMXSAFE(OMX_SendCommand(vrenderer, OMX_CommandPortEnable, 1, 0)); // 停下1對應的端口
5)OMX_SetupTunnel
将兩個元件連接配接起來,實際會引起調用每個元件的ComponentTunnelRequest
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel(
OMX_IN OMX_HANDLETYPE hOutput,
OMX_IN OMX_U32 nPortOutput,
OMX_IN OMX_HANDLETYPE hInput,
OMX_IN OMX_U32 nPortInput);
例子:
OMXSAFE(OMX_SetupTunnel(reader, 0, vdecoder, 0)); // reader的0端口為出,vdecoder的0端口為入,連接配接成一個Tunnel
準備好後,就可以設定OMX_StateExecuting,來讓這個流程活動起來了。再以後,就可以通過OMX_StateIdle 來停下。
6)OMX_GetState
#define OMX_GetState( /
hComponent, /
pState) /
((OMX_COMPONENTTYPE*)hComponent)->GetState( /
hComponent, /
pState)
7)decOutputPortDef
OMX_PARAM_PORTDEFINITIONTYPE decOutputPortDef;
INIT_PARAM(decOutputPortDef);
decOutputPortDef.nPortIndex = 0;
err = OMX_GetParameter(pCtx->hReaderComp,
OMX_IndexParamPortDefinition,
&decOutputPortDef); // 利用IndexParamPortDefinition來得到元件的輸出端口的屬性
videoWidth = decOutputPortDef.format.video.nFrameWidth;
videoHeight = decOutputPortDef.format.video.nFrameHeight;
8) *pOmxBufferHeader
OMX_BUFFERHEADERTYPE *pOmxBufferHeader ;
// tell decoder output port that it will be using our buffer
err = OMX_UseBuffer(hDecodeComp,
&pOmxBufferHeader, //out
OMX_DECODE_OUTPUT_PORT,
NULL,
outSize,
(NvU8*)pOut);
将配置設定好的pOut指針和他的大小outSize,配成一個OMX_BUF, 并給pOmxBufferHeader,這樣就通過OMX_UseBuffer,來得到一個以後能給他用的Buffer,指針用我們配置設定的。
https://www.cnblogs.com/dyufei/p/8018520.html