【IT168技术】本文主要个大家介绍OMAP平台OpenMax IL的硬件实现过程。其中包括TI OpenMax IL实现的结构和机制,TI OpenMax IL的核心和公共内容,一个TI OpenMax IL组件的实现三个部分。
Android的开源代码中,已经包含了TI的OpenMax IL层的实现代码,其路径如下所示:
hardware/ti/omap3/omx/
其中包含的主要目录如下所示。
system:OpenMax核心和公共部分
audio:音频处理部分的OpenMax IL组件
video:视频处理部分OpenMax IL组件
image:图像处理部分OpenMax IL组件
TI OpenMax IL实现的结构如图18-7所示。
在TI OpenMax IL实现中,最上面的内容是OpenMax的管理者用于管理和初始化,中间层是各个编解码单元的OpenMax IL标准组件,下层是LCML层,供各个OpenMax IL标准组件所调用。
TI OpenMax IL实现的公共部分在system/src/openmax_il/目录中,主要的内容如下所示。
omx_core/src:OpenMax IL的核心,生成动态库libOMX_Core.so
lcml/:LCML的工具库,生成动态库libLCML.so
TI OpenMax IL的视频(Video)相关的组件在video/src/openmax_il/目录中,主要的内容如下所示。
prepost_processor:Video数据的前处理和后处理,生成动态库libOMX.TI.VPP.so
video_decode:Video解码器,生成动态库libOMX.TI.Video.Decoder.so
video_encode:Video编码器,生成动态库libOMX.TI.Video.encoder.so
▲图18-7 TI OpenMax IL实现的结构
TI OpenMax IL的音频(Audio)相关的组件在audio/src/openmax_il/目录中,主要的内容如下所示。
g711_dec:G711解码器,生成动态库libOMX.TI.G711.decode.so
g711_enc:G711编码器,生成动态库libOMX.TI.G711.encode.so
g722_dec:G722解码器,生成动态库libOMX.TI.G722.decode.so
g722_enc:G722编码器,生成动态库libOMX.TI.G722.encode.so
g726_dec:G726解码器,生成动态库libOMX.TI.G726.decode.so
g726_enc:G726编码器,生成动态库libOMX.TI.G726.encode.so
g729_dec:G729解码器,生成动态库libOMX.TI.G729.decode.so
g729_enc:G720编码器,生成动态库libOMX.TI.G729.encode.so
nbamr_dec:AMR窄带解码器,生成动态库libOMX.TI.AMR.decode.so
nbamr_enc:AMR窄带编码器,生成动态库libOMX.TI.AMR.encode.so
wbamr_dec:AMR宽带解码器,生成动态库libOMX.TI.WBAMR.decode.so
wbamr_enc:AMR宽带编码器,生成动态库libOMX.TI.WBAMR.encode.so
mp3_dec:MP3解码器,生成动态库libOMX.TI.MP3.decode.so
aac_dec:AAC解码器,生成动态库libOMX.TI.AAC.decode.so
aac_enc:AAC编码器,生成动态库libOMX.TI.AAC.encode.so
wma_dec:WMA解码器,生成动态库libOMX.TI.WMA.decode.so
TI OpenMax IL的图像(Image)相关的组件在image/src/openmax_il/目录中,主要的内
容如下所示。
jpeg_enc:JPEG编码器,生成动态库libOMX.TI.JPEG.Encoder.so
jpeg_dec:JPEG解码器,生成动态库libOMX.TI.JPEG.decoder.so
TI OpenMax IL的核心和公共内容
LCML的全称是“Linux Common Multimedia Layer”,是TI的Linux公共多媒体层。在OpenMax IL的实现中,这个内容在system/src/openmax_il/lcml/目录中,主要文件是子目录src中的LCML_DspCodec.c文件。通过调用DSPBridge的内容, 让ARM和DSP进行通信,然DSP进行编解码方面的处理。DSP的运行还需要固件的支持。
TI OpenMax IL的核心实现在system/src/openmax_il/omx_core/目录中,生成TI OpenMax IL的核心库libOMX_Core.so。
其中子目录src中的OMX_Core.c为主要文件,其中定义了编解码器的名称等,其片断如下所示:
{"OMX.TI.JPEG.decoder", "image_decoder.jpeg"}, /* 图像和视频编解码器 */
{"OMX.TI.JPEG.Encoder", "image_encoder.jpeg"},
{"OMX.TI.Video.Decoder", "video_decoder.avc"},
{"OMX.TI.Video.Decoder", "video_decoder.mpeg4"},
{"OMX.TI.Video.Decoder", "video_decoder.wmv"},
{"OMX.TI.Video.encoder", "video_encoder.mpeg4"},
{"OMX.TI.Video.encoder", "video_encoder.h263"},
{"OMX.TI.Video.encoder", "video_encoder.avc"},
/* ......省略 ,语音相关组件*/
#ifdef BUILD_WITH_TI_AUDIO /* 音频编解码器 */
{"OMX.TI.MP3.decode", "audio_decoder.mp3"},
{"OMX.TI.AAC.encode", "audio_encoder.aac"},
{"OMX.TI.AAC.decode", "audio_decoder.aac"},
{"OMX.TI.WMA.decode", "audio_decoder.wma"},
{"OMX.TI.WBAMR.decode", "audio_decoder.amrwb"},
{"OMX.TI.AMR.decode", "audio_decoder.amrnb"},
{"OMX.TI.AMR.encode", "audio_encoder.amrnb"},
{"OMX.TI.WBAMR.encode", "audio_encoder.amrwb"},
#endif
{NULL, NULL},
};
tComponentName数组的各个项中,第一个表示编解码库内容,第二个表示库所实现的功能。
其中,TIOMX_GetHandle()函数用于获得各个组建的句柄,其实现的主要片断如下所示:
OMX_PTR pAppData, OMX_CALLBACKTYPE* pCallBacks)
{
static const char prefix[] = "lib";
static const char postfix[] = ".so";
OMX_ERRORTYPE (*pComponentInit)(OMX_HANDLETYPE*);
OMX_ERRORTYPE err = OMX_ErrorNone;
OMX_COMPONENTTYPE *componentType;
const char* pErr = dlerror();
// ...... 省略错误处理内容
int i = 0;
for(i=0; i< COUNTOF(pModules); i++) { // 循环查找
if(pModules[i] == NULL) break;
}
// ...... 省略错误处理内容
int refIndex = 0;
for (refIndex=0; refIndex < MAX_TABLE_SIZE; refIndex++) {
// 循环查找组件列表
if (strcmp(componentTable[refIndex].name, cComponentName) == 0) {
if (componentTable[refIndex].refCount >= MAX_CONCURRENT_INSTANCES) {
// ...... 省略错误处理内容
} else {
char buf[sizeof(prefix) + MAXNAMESIZE + sizeof(postfix)];
strcpy(buf, prefix);
strcat(buf, cComponentName);
strcat(buf, postfix);
pModules[i] = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
// ...... 省略错误处理内容
// 动态取出初始化的符号
pComponentInit = dlsym(pModules[i], "OMX_ComponentInit");
pErr = dlerror();
// ...... 省略错误处理内容
*pHandle = malloc(sizeof(OMX_COMPONENTTYPE));
// ...... 省略错误处理内容
pComponents[i] = *pHandle;
componentType = (OMX_COMPONENTTYPE*) *pHandle;
componentType->nSize = sizeof(OMX_COMPONENTTYPE);
err = (*pComponentInit)(*pHandle); // 执行初始化工作
// ...... 省略部分内容
}
}
}
err = OMX_ErrorComponentNotFound;
goto UNLOCK_MUTEX;
// ...... 省略部分内容
return (err);
}
在TIOMX_GetHandle()函数中,根据tComponentName数组中动态库的名称,动态打开各个编解码实现的动态库,取出其中的OMX_ComponentInit符号来执行各个组件的初始化。
一个TI OpenMax IL组件的实现
TI OpenMax IL中各个组件都是通过调用LCML来实现的,实现的方式基本类似。主要都是实现了名称为OMX_ComponentInit的初始化函数,实现OMX_COMPONENTTYPE类型的结构体中的各个成员。各个组件其目录结构和文件结构也类似。
以MP3解码器的实现为例,在audio/src/openmax_il/mp3_dec/src目录中,主要包含以下文件。
OMX_Mp3Decoder.c:MP3解码器组件实现
OMX_Mp3Dec_CompThread.c:MP3解码器组件的线程循环
OMX_Mp3Dec_Utils.c:MP3解码器的相关工具,调用LCML实现真正的MP3解码的功能
OMX_Mp3Decoder.c中的OMX_ComponentInit()函数负责组件的初始化,返回的内容再从参数中得到,这个函数的主要片断如下所示:
{
OMX_ERRORTYPE eError = OMX_ErrorNone;
OMX_COMPONENTTYPE *pHandle = (OMX_COMPONENTTYPE*) hComp;
OMX_PARAM_PORTDEFINITIONTYPE *pPortDef_ip = NULL, *pPortDef_op = NULL;
OMX_AUDIO_PARAM_PORTFORMATTYPE *pPortFormat = NULL;
OMX_AUDIO_PARAM_MP3TYPE *mp3_ip = NULL;
OMX_AUDIO_PARAM_PCMMODETYPE *mp3_op = NULL;
MP3DEC_COMPONENT_PRIVATE *pComponentPrivate = NULL;
MP3D_AUDIODEC_PORT_TYPE *pCompPort = NULL;
MP3D_BUFFERLIST *pTemp = NULL;
int i=0;
MP3D_OMX_CONF_CHECK_CMD(pHandle,1,1);
/* ......省略,初始化OMX_COMPONENTTYPE类型的指针pHandle */
OMX_MALLOC_GENERIC(pHandle->pComponentPrivate,MP3DEC_COMPONENT_PRIVATE);
pComponentPrivate = pHandle->pComponentPrivate; /* 私有指针互相指向 */
pComponentPrivate->pHandle = pHandle;
/* ......略,初始化似有数据指针pComponentPrivate */
/* 设置输入端口(OMX_PARAM_PORTDEFINITIONTYPE类型)的默认值 */
pPortDef_ip->nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
pPortDef_ip->nPortIndex = MP3D_INPUT_PORT;
pPortDef_ip->eDir = OMX_DirInput;
pPortDef_ip->nBufferCountActual = MP3D_NUM_INPUT_BUFFERS;
pPortDef_ip->nBufferCountMin = MP3D_NUM_INPUT_BUFFERS;
pPortDef_ip->nBufferSize = MP3D_INPUT_BUFFER_SIZE;
pPortDef_ip->nBufferAlignment = DSP_CACHE_ALIGNMENT;
pPortDef_ip->bEnabled = OMX_TRUE;
pPortDef_ip->bPopulated = OMX_FALSE;
pPortDef_ip->eDomain = OMX_PortDomainAudio;
pPortDef_ip->format.audio.eEncoding = OMX_AUDIO_CodingMP3;
pPortDef_ip->format.audio.cMIMEType = NULL;
pPortDef_ip->format.audio.pNativeRender = NULL;
pPortDef_ip->format.audio.bFlagErrorConcealment = OMX_FALSE;
/* 设置输出端口(OMX_PARAM_PORTDEFINITIONTYPE类型)的默认值 */
pPortDef_op->nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
pPortDef_op->nPortIndex = MP3D_OUTPUT_PORT;
pPortDef_op->eDir = OMX_DirOutput;
pPortDef_op->nBufferCountMin = MP3D_NUM_OUTPUT_BUFFERS;
pPortDef_op->nBufferCountActual = MP3D_NUM_OUTPUT_BUFFERS;
pPortDef_op->nBufferSize = MP3D_OUTPUT_BUFFER_SIZE;
pPortDef_op->nBufferAlignment = DSP_CACHE_ALIGNMENT;
pPortDef_op->bEnabled = OMX_TRUE;
pPortDef_op->bPopulated = OMX_FALSE;
pPortDef_op->eDomain = OMX_PortDomainAudio;
pPortDef_op->format.audio.eEncoding = OMX_AUDIO_CodingPCM;
pPortDef_op->format.audio.cMIMEType = NULL;
pPortDef_op->format.audio.pNativeRender = NULL;
pPortDef_op->format.audio.bFlagErrorConcealment = OMX_FALSE;
/* ......省略,分配端口 */
/* 设置输入端口的默认格式 */
pPortFormat = pComponentPrivate->pCompPort[MP3D_INPUT_PORT]->pPortFormat;
OMX_CONF_INIT_STRUCT(pPortFormat, OMX_AUDIO_PARAM_PORTFORMATTYPE);
pPortFormat->nPortIndex = MP3D_INPUT_PORT;
pPortFormat->nIndex = OMX_IndexParamAudioMp3;
pPortFormat->eEncoding = OMX_AUDIO_CodingMP3;
/* 设置输出端口的默认格式 */
pPortFormat = pComponentPrivate->pCompPort[MP3D_OUTPUT_PORT]->pPortFormat;
OMX_CONF_INIT_STRUCT(pPortFormat, OMX_AUDIO_PARAM_PORTFORMATTYPE);
pPortFormat->nPortIndex = MP3D_OUTPUT_PORT;
pPortFormat->nIndex = OMX_IndexParamAudioPcm;
pPortFormat->eEncoding = OMX_AUDIO_CodingPCM;
/* ......省略部分内容 */
eError = Mp3Dec_StartCompThread(pHandle); // 启动MP3解码线程
/* ......省略部分内容 */
return eError;
}
这个组件是OpenMax的标准实现方式,对外的接口的内容只有一个初始化函数。完成OMX_COMPONENTTYPE类型的初始化。输入端口的编号为MP3D_INPUT_PORT(==0),类型为OMX_PortDomainAudio,格式为OMX_AUDIO_CodingMP3。输出端口的编号是MP3D_OUTPUT_PORT(==1),类型为OMX_PortDomainAudio,格式为OMX_AUDIO_ CodingPCM。
OMX_Mp3Dec_CompThread.c中定义了MP3DEC_ComponentThread()函数,用于创建MP3解码的线程的执行函数。
OMX_Mp3Dec_Utils.c中的Mp3Dec_StartCompThread()函数,调用了POSIX的线程库建立MP3解码的线程,如下所示:
nRet = pthread_create (&(pComponentPrivate->ComponentThread), NULL,
MP3DEC_ComponentThread, pComponentPrivate);
Mp3Dec_StartCompThread()函数就是在组件初始化函数OMX_ComponentInit()最后调用的内容。MP3线程的开始并不表示解码过程开始,线程需要等待通过pipe机制获得命令和数据(cmdPipe和dataPipe),在适当的时候开始工作。这个pipe在MP3解码组件的SendCommand等实现写操作,在线程中读取其内容。