Android Opencore OpenMAX学习

1 OMX core methods

1)OMX_Init
2)OMX_Deinit
3)OMX_GetHandle
4)OMX_FreeHandle
5)OMX_ComponentNameEnum
6)OMX_GetComponentsOfRole
7)OMX_GetRolesOfComponent
8)OMX_SetupTunnel
9)OMX_GetContentPipe


2 The configuration parser API
除了以上methods ,强烈推荐OMX 核心插件库包含此API
2.1
函数原型
OMX_BOOL OMXConfigParser ( OMX_PTR aInputParameters,OMX_PTR aOutputParameters);

2.2 传递参数
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;

2.3 返回值
OMX_FALSE :
处理codec 配置头部错误或不支持该格式
OMX_TURE :
正确处理codec 配置头部

2.4 函数作用
填充aOutputParameters ,有两种选择:audio coded or vedio codec

for audio
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;


3 动态加载OMX 内核
解释了/system/system/etc/pvplayer.cfg 文件中最后一行的含义
(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f),"libomx_sharedlibrary.so"

作用:将OMX 内核动态加载进OpenCORE 框架
位置:/system/system/etc/pvplayer.cfg
形式:(OMX Core API OsclUuid), “shared library name.so”
eg
(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f),"libomx_core_vendorXYZ.so"

注意:(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f) 提供独立API ID ,不可修改

数据格式及OMX 输入缓冲细节

1.1 帧起始代码
一般不用,H.264 可能使用。

1.2 OMX 缓冲区
三个值得信赖的关键参数
nFilledLen
缓冲区长度
nTimestamp
缓冲区时间戳
OMX_BUFFERLAG_ENDOFFRAME
缓冲区结束标志位

1.3 多帧合并输入缓冲
一些音频信息,单帧过小(eg ARM ),将其合并作为一个缓冲区处理。
nFilledLen
为所有帧总长度,nTimestamp 指向缓冲区第一帧时间。

1.4 部分帧
视频解码单帧过大情况下,可能将单帧拆分后传递给缓冲区。
部分帧情况下,只有最后一帧的缓冲区才拥有OMX_BUFFERLAG_ENDOFFRAME
部分帧缓冲区不会包含两帧信息。
流媒体可能包含多帧。
部分帧的nTimestamp 应当相同。

总结:OMX 输出缓冲区可能包含
——
完整多帧
——
完整单帧
——
部分帧

1.5 错误的数据封装
多帧的部分帧封装 eg wrongFrame1+Frame2 part

1.6 Codec 配置数据
Codec
配置缓冲区使用OMX_BUFFERLAG_ENDOFFRAME OMX_BUFFERFLAG_CODECCONFIG 标志位。
H.264
SPSPPS 使用独立的OMX 输入缓冲区。

2 H264/AVC 解码器格式
Codec
配置头部:
SPS
PPS NAL 单元位于起始的OMX 输入缓冲区。
SPS
PPS NALs 使用独立的OMX 输入缓冲区,并使用OMX_BUFFERLAG_ENDOFFRAME OMX_BUFFERFLAG_CODECCONFIG 标记。

2.1 AVC NAL 模式与AVC Frame 模式
通过设置iOMXComponentUsesFullAVCFrame 标志位,可以决定AVC 数据使用哪种模式解码。
默认使用NAL 模式,此种模式下OpenCORE 框架同时提供完整单帧和部分帧输入缓冲区。
Frame 模式下,OpenCORE 框架积累NALs 并提供完整单帧给输入缓冲区。
OMX_OTHER_EXTRADATA
结构体用来区分NAL 边界。
如果iOMXComponentUsesFullAVCFrameiOMXComponentUsesNALStratCodes 都被置为OMX_TRUE
NAL
边界可被start codes 区分,此时OMX_OTHER_EXTRADATA 无用。

数据结构——NAL 模式:
输入缓冲区包含一个或多个NAL ,但只包含同一帧的NAL ,一帧最后一个NAL 才含有OMX_BUFFERLAG_ENDOFFRAME 标志位。

数据结构——Frame 模式:
每个输入缓冲区包含完整帧。
如果使用NAL start codes ,可通过读取NAL start codes 区分NAL 边界。
否则使用OMX_OTHER_EXTRADATA 结构体区分NAL 边界。
Frame 模式中,每个缓冲区都含有OMX_BUFFERLAG_ENDOFFRAME 标志位。
Frame 模式中,每个缓冲区都含有位于OMX_BUFFERLAGHEADERTYPE 结构体nFlags 区域的OMX_BUFFERLAG_EXTRADATA 标志位。

缓冲区最后包含AVC frame ,追加以下数据:
OMX_OTHER_EXTRADATATYPE extra;
OMX_OTHER_EXTRADATATYPE terminator;

extra.eType = OMX_ExtraDataNALSizeArray;
extra.nSize = 20+4*(number of NALs in the frame); // 20 is the size of
OMX_OTHER_EXTRADATATYPE structure + 4 bytes per NAL size
extra.nDataSize = 4 * (number of NALs in the frame)
extra.data[4*i] = size of the i-th NAL (data is declared as byte array – so offset is 4*i, since 4 bytes
is assigned to signal the size of each NAL unit)
terminator.eType = OMX_ExtraDataNone;
terminator.nSize = 20;
terminator.nDataSize = 0;

#define OMX_ExtraDataNALSizeArray 0x7F123321

通过获取OMX_OTHER_EXTRADATA 结构体信息,可以得知每一帧包含NAL 单元的数目并确定NAL 边界。

一个例子:AVC Frame 模式,包含2NAL ,包含extra 数据结构
总结:
1
)每个缓冲区都含有位于OMX_BUFFERLAGHEADERTYPE 结构体nFlags 区域的OMX_BUFFERLAG_EXTRADATA 标志位
2
)每个NAL 的长度应当使用独立的4byte 无符号整型数表示(eg OMX_U32
3
)所有NAL 的长度被编码成OMX_U32 的数组存放在buffer 最后。
4
)包含完整帧的缓冲区必须含有位于OMX_BUFFERLAGHEADERTYPE 结构体nFlags 区域的OMX_BUFFERLAG_ENDOFFRAME 标志位。
5
)一个独立的缓冲区不包含多帧数据。

3 YUVRGB 数据格式Android Opencore OpenMAX学习 - wangshh03 - 王世宏的博客
 
OMX
编码组件中,生肉提供YUV 或者RGB 格式,OpenCORE 框架将提供一帧完成的YURRGB 数据给OMX 组件。

 

 

OpenMax 调用顺序(OpenMax Call Sequences
1 OMX
核心初始化 _OMX_MasterInit

1)
调用OMX_Init 函数
->OsclInit::Init(error, &select); //init all Oscl layers except Oscl scheduler.
->_Try_OMX_Create(error, data); //create the OMX singleton
->OsclSingletonRegistry::registerInstanceAndUnlock(data, OSCL_SINGLETON_ID_OMX, error); //Release the singleton.
->_Try_OMX_Init(error, status); //If create succeeded, then init the OMX globals.

2PV 框架列举所有OMX

OMX_ComponentNameEnum // 列举所有组件的名称
->OsclSingletonRegistry::getInstance(OSCL_SINGLETON_ID_OMX, error);
->oscl_strncpy(cComponentName, (data->ipRegTemplateList[Index])->ComponentName, nNameLength);

OMX_GetRolesOfComponent // 通过组件名称找到组件,返回其角色(role
->OsclSingletonRegistry::getInstance(OSCL_SINGLETON_ID_OMX, error);
->data->ipRegTemplateList[ii])->GetRolesOfComponent(RoleString)
->oscl_strncpy((OMX_STRING) roles[ii], (OMX_STRING)RoleString[ii], oscl_strlen((OMX_STRING)RoleString[ii]) + 1);

2 OMX 组件实例、功能及端口
OMX
核心初始化后,下一步为列举每个组件的功能和端口。
1
)调用OMX_GetHandle 获取所需的OMX 组件信息。

2 )调用OMX_GetParameter“PV_OMX_CAPABILITY_TYPE_INDEX” 这个index 去获取组件的功能。
万一组件不是OpenMax 全兼容或者OpenMax 的特性不明确,以上获取的功能决定了OMX 是否支持输入/ 输出端口“UseBufeer”“AllocateBuffer” 调用,以及OMX 是否支持部分帧等等。

注意:如果OMX 组件返回“OMX_ErrorUnsupportedIndex”index (或其他比如“OMX_ErrorNone” ),PV 框架将为组件功能赋默认值。

3 )调用OMX_GetParameter ,针对视频组件再调用“OMX_IndexVideoInit” ,针对音频组件则调用“OMX_IndexAudioIni” 以获取可用的端口号。

4 )循环查找可用的端口号以找到输入端口。

5 )循环查找可用的端口号以找到输出端口。

注意:
Index “PV_OMX_CAPABILITY_TYPE_INDEX” is defined as:
#define PV_OMX_COMPONENT_CAPABILITY_TYPE_INDEX 0xFF7A347
The OMX_GetParameter call expects the following structure to be filled for this index:
typedef struct PV_OMXComponentCapabilityFlagsType
{
// OMX COMPONENT CAPABILITY RELATED MEMBERS
OMX_BOOL iIsOMXComponentMultiThreaded;
OMX_BOOL iOMXComponentSupportsExternalOutputBufferAlloc;
OMX_BOOL iOMXComponentSupportsExternalInputBufferAlloc;
OMX_BOOL iOMXComponentSupportsMovableInputBuffers;
OMX_BOOL iOMXComponentSupportsPartialFrames;
OMX_BOOL iOMXComponentUsesNALStartCode;
OMX_BOOL iOMXComponentCanHandleIncompleteFrames;
OMX_BOOL iOMXComponentUsesFullAVCFrames;
} PV_OMXComponentCapabilityFlagsType;

功能参数的默认值:
1
iIsOMXComponentMultiThreaded —— 默认值OMX_TRUE
OMX
组件一般运行与独立的线程(与PV 框架线程不同),有可能将OMX 组件集成进PV 框架线程(e.g., 通过同步调用)。

2iOMXComponentSupportsExternalOutputBufferAlloc —— 默认值OMX_TRUE
OMX
规范要求OMX 组件支持外部分配输出缓冲(就是输出缓冲的OMX_UseBuffer 调用)。
如果组件不支持,必须通知PV 框架,以便其调用“OMX_AllocateBuffer” 代替。

3iOMXComponentSupportsExternalInputBufferAlloc —— 默认值OMX_TRUE
OMX
规范要求OMX 组件支持外部分配输入缓冲(就是输入缓冲的OMX_UseBuffer 调用)。
如果组件不支持,必须通知PV 框架,以便其调用“OMX_AllocateBuffer” 代替。

4iOMXComponentSupportsMovableInputBuffers —— 默认值OMX_TRUE
如果OMX 缓冲是外部分配的,为了提高稳定性和优化性能,可以分离OMX 缓冲头部信息(OMX_BUFFERHEADERTYPE) 与数据区(“pBuffer”) 。换句话说,使OMX 缓冲更有 移动性 ,当传递一个输入缓冲到OMX 组件时,“pBuffer” 区域和头部信息不一定指向相同的缓冲区。因此,可以分配更多的数据缓冲在框架中循环工作,只有在需要把缓冲传递到OMX 组件时才附加到头部信息去。如果OMX 组件要求头部和数据一直指向相同的缓冲,则iOMXComponentSupportsMovableInputBuffers 应该被置为OMX_FALSE

5iOMXComponentSupportsPartialFrames —— 默认值OMX_TRUE
OMX
规范要求OMX 组件支持将任意数据打包进OMX 缓冲,包括独立单帧、NAL 和被拆分到多个缓冲区的解码单元。PV 框架支持“OMX_BUFFERFLAG_ENDOFFRAME” 标记去组装部分帧。然而,如果OMX 组件不支持组装部分帧,也就是OMX 组件要求PV 框架来组装部分帧并提供给OMX 组件完整单帧或NAL ,此时iOMXComponentSupportsPartialFrames 需要被设置为OMX_FALSE
注意:设置为OMX_FALSE 将影响性能。

6iOMXComponentUsesNALStartCode —— 默认值OMX_FALSE
这个标志位将影响H264 解码。PV 框架提供所有信息以重建立H264 NALs (通过OMX 缓冲的大小和OMX_BUFFERFLAG_ENDOFFRAME 的大小)。因此没有必要由OMX 组件解码输出流来获取0x0001NAL 初始代码。如果OMXH264 组件及解码单元需要NAL 起始代码,可将iOMXComponentUsesNALStartCode 置为OMX_TRUE

7iOMXComponentCanHandleIncompleteFrames —— 默认值OMX_TRUE
如果丢失部分数据流(比如数据包丢失),假定OMX 组件及解码单元可以解决这个问题。如果不可以,则将iOMXComponentCanHandleIncompleteFrames 设置为OMX_FALSE 。当“iOMXComponentSupportsPartialFrames” 也被设置为OMX_FALSE 时,这点相当重要,因为OMX 组件不提供组装部分帧,也就是说PV 框架必须提供组装好的frame/NALs ,因此需要通知OMX 组件不完整的帧数据是否可以传递进来。

8iOMXComponentUsesFullAVCFrames —— 默认值OMX_FALSE
这个标志位决定AVC 解码中使用NAL 模式还是frame 模式。AVC 数据可由以上两种模式提供给OMX 组件。默认为NAL 模式(设置为OMX_FALSE ),此模式下OpenCore 框架可以为OMX 输入缓冲同时提供完整或者部分AVC NALFrame 模式下(OMX_TRUE ),OpenCore 框架积累NAL ,并提供给OMX 输入缓冲一帧完整的数据。
NAL
边界的问起前面讨论过了,此处省略。

3 OMX 组件输入输出缓冲协商
在任何数据交换前,输入输出缓冲需要进行协商。PV 框架需要做以下工作:、
1
)调用OMX_GetParameter 获取输入端口缓冲参数(最小/ 实际缓冲数,缓冲区大小等等)
2
)检查有效的输入缓冲参数(修改一些参数,比如帧的长度和宽度,以及缓冲区的数目等等)
3
)调用OMX_SetParameter 函数设置输入缓冲参数
4
)调用OMX_GetParameter 获取输出端口缓冲参数(最小/ 实际缓冲数,缓冲区大小等等)
5
)检查有效的输出缓冲参数(修改一些参数,比如帧的长度和宽度,以及缓冲区的数目等等)
6
)调用OMX_SetParameter 函数设置输出缓冲参数

过程如图三(省略)

一些设想:
1
)缓冲区尺寸的设想:
对于编码器组件,最终分配的输出缓冲区尺寸可能小于profile/level/target 比特率所要求的最大帧尺寸。出于内存消耗考虑,OMX 编码器组件应当能够输出frameNAL 的比特流划分到多个输出缓冲区去。老调重弹,最后一个部分缓冲区使用“OMX_BUFFERFLAG_ENDOFFRAME” 标记结尾。

对于解码器组件,最终分配的输入缓冲区尺寸可能小于profile/level/target 比特率所要求的最大帧尺寸。出于内存消耗考虑,PV 框架在适当的时候提供采用“OMX_BUFFERFLAG_ENDOFFRAME” 标记结尾的输入缓冲区。如果输入缓冲包含完整帧(H264 也可能是NAL 单元 )或多帧,“OMX_BUFFERFLAG_ENDOFFRAME” 将会用来标记一帧的结尾。如果完整的frame/NAL 不能放入一个输入缓冲区,则会被拆分放入多个缓冲区。包含部分信息的缓冲区,“OMX_BUFFERFLAG_ENDOFFRAME” 标记位将会被置为0 。一帧数据的最后一个缓冲区中“OMX_BUFFERFLAG_ENDOFFRAME” 才会被置为1 。通过这种做法,组件可以方便的重构一帧数据。

如果OMX 解码器组件不兼容组长部分帧,PV 框架将负责做这件事情。

2 )输入/ 输出缓冲区数量。
框架或许希望分配比OMX 要求更多的的输入/ 输出缓冲区以提供更优的性能(比如解码器输出缓冲或编码器输入缓冲—— 缓冲更多的数据可以提高性能)。尽管性能提升了,但无法送到OMX 组件去(不太理解,应该还是协商不好的意思)。

4 OMX 状态变换 加载-> 空闲
如果部分已分配的缓冲区进入空闲状态,缓冲区分配将挂起。在变化过程中,PV 框架将
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StateLoaded 改变为OMX_StateIdle
2
)调用一系列“OMX_UseBuffer” 或者“OMX_AllocateBuffer” 通知OMX 组件。这些调用使用NumInputBuffer 记录输入输入端口的数目,使用NumOutputBuffer 记录输入输入端口的数目。
3
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

一些设想与推荐参数:
根据OMX 规范,兼容的OMX 组件必须同时支持OMX_UseBuffer OMX_AllocateBuffer 调用。然而,出于一些内在原因,完全实现是不可能的,组件应当通知PV 框架其所具有的功能。

推荐将INPUT 缓冲分配在OMX 组件外部,这样可以减少额外的内存拷贝(也就是说输入缓冲使用OMX_UseBuffer )。

5 变换到 执行 状态与数据交换
状态变化到执行时才开始真正处理数据。本步骤中,PV 框架将:
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StateIdle 改变为OMX_StateExecuting
2
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)
3
)通过OMX_EmptyThisBuffer 调用将输入缓冲发送给OMX 组件,通过OMX_FillThisBuffer 调用将输出缓冲发送给OMX 组件,组件使用合适的回调函数返回缓冲区。

过程如图5

Android Opencore OpenMAX学习 - wangshh03 - 王世宏的博客
 

注意:

1 )在任意时刻OMX 组件如果拥有所有输入缓冲区(也就是所有的输入缓冲都通过EmptyThisBuffer 调用传给了OMX 组件),此时将不能再传递任何输入缓冲区给OMX 组件,知道OMX 通过EmptyBufferDone 释放一个缓冲。对于输出缓冲区同样。

2 )如果PV 框架没有及时将缓冲区发给OMX 框架,OMX 组件不会多次返回同一个缓冲区(也就是一旦OMX 框架返回一个缓冲区,只有PV 框架再次调用这个缓冲,OMX 组件才可使用)。这是OMX 缓冲交换APSs 所规定的基本规则。

6 暂停
PV
框架常见的功能包括暂停和恢复,PV 框架将:
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StateExecuting 改变为OMX_StatePause
2
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

PV 框架向OMX 组件发送暂停命令后,立刻停止发送输入输出缓冲。

有暂停就有恢复,PV 框架将:
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StatePause 改变为OMX_StateExecuting
2
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

PV 框架的状态恢复到执行后,将恢复发送OMX 输入输出缓冲。

过程如图6

7 端口刷新(如果可用)
端口刷新调用一般用于重新配置解码器组件,这样会清空所有数据。在此情况下,PV 框架将:
1
)通过“OMX_SendCommand” 调用发送刷新全部端口指令。
2
)等待输入缓冲的输出缓冲的两个EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

刷新命令来了之后,PV 框架立刻停止向OMX 组件发送输入/ 输出缓冲。当组件接收到2 个回调函数后,PV 框架开始恢复发送输入/ 输出缓冲。

端口刷新的顺序和通告并不相关。

过程如图7

为了防止动态端口被重复配置,也可以先于OMX IL 用户关闭OMX 组件端口调用端口刷新指令。

8 停止/ 变化到“IDLE” 状态
为了停止处理过程,PV 框架将:
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StatePause 改变为OMX_StateIdle
2
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

过程见图8

一些设想:
当命令完成“IDEL” 态的回调后,PV 框架假设所有输入输出缓冲按照OMX 规范要求那样返回。

9 OMX 组件状态转换 IDLE->Loaded State De-initialization
PV 框架结束与一个OMX 组件的所有交互后,将:
1
)通过“OMX_SendCommand” 调用发送命令给OMX 组件,将状态由OMX_StateIdle 改变为OMX_StateLoaded
2
)向OMX 组件发送一系列“OMX_FreeBuffer” 调用。
3
)等待OMX 组件的EventHandler 事件回调,通知框架状态变换完成(OMX_EventCmdComplete) 。使用NumInputBuffer 记录输入输入端口的数目,使用NumOutputBuffer 记录输入输入端口的数目。
4
)对OMX 核心执行OMX_FreeHandle 调用,释放组件句柄。

流程图见图9

注意:在向OMX 组件发送卸载命令之前,PV 框架一直在等待OMX 组件返回的输入输出缓冲。由于回调的过程的不同步性,一些EmptyBufferDone/FillBufferDone 的回调有可能在OMX 组件状态由“executing” “idle” 之后才到达。

10 OMX Core 卸载
PV
框架调用函数OMX_Deinit()

翻译 from openmax_call_sequences.pdf

来自/external/opencore/doc/omx_core_integration_guide.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值