本文参考: http://zhoujinjian.cc/2018/09/06/Android%20Video%20System%EF%BC%884%EF%BC%89%EF%BC%9AAndroid%20Multimedia%20-%20OpenMax%E5%AE%9E%E7%8E%B0%E5%88%86%E6%9E%90/index.html
1. 相关代码
/hardware/qcom/media/libstagefrighthw/
/hardware/qcom/media/mm-video-v4l2/vidc/vdec/src/ (venc也是类似)
/hardware/qcom/media/mm-core/inc/
/frameworks/av/media/libstagefright/omx/
/frameworks/av/media/libstagefright/omx/1.0/
/frameworks/av/media/libmedia/omx/1.0/
2. OpenMax简介
OpenMax是一个多媒体应用程序的框架标准。NuPlayer就是用OpenMax来做(codec)编解码的。
OpenMax实际上分成三个层次,自上而下分别是,OpenMax DL(开发层),OpenMax IL(集成层)和OpenMax AL(应用层)。三个层次的内容分别如下所示:
第一层:OpenMax DL(Development Layer,开发层)
OpenMax DL定义了一个API,它是音频、视频和图像功能的集合。供应商能够在一个新的处理器上实现并优化,然后编解码供应商使用它来编写更广泛的编解码器功能。它包括音频信号的处理功能,如FFT和filter,图像原始处理,如颜色空间转换、视频原始处理,以实现例如MPEG-4、H.264、MP3、AAC和JPEG等编解码器的优化。第二层:OpenMax IL(Integration Layer,集成层)
OpenMax IL作为音频、视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如,资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是透明的底层接口应用于嵌入式、移动设备。它提供了应用程序和媒体框架,透明的。编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。第三层:OpenMax AL(Appliction Layer,应用层)
OpenMax AL API在应用程序和多媒体中间件之间提供了一个标准化接口,多媒体中间件提供服务以实现被期待的API功能。
实际上,openMAX的目的就是做一套标准接口,媒体应用、硬件厂商都遵循同样的接口。硬件厂商提供处理器时和openMAX一起提供,媒体应用只需遵循接口配置就可以应用于不同的硬件芯片,而不用针对各个芯片去适配。
OpenMax AL和OpenMax DL应用较少,我们一般用的都是OpenMax IL层。
2.1 OpenMax IL(集成层)
图中的虚线中的内容是OpenMax IL层的内容,其主要实现了OpenMax IL中的各个组件(Component)。对下层,OpenMax IL可以调用OpenMax DL层的接口,也可以直接调用各种Codec实现。对上层,OpenMax IL可以给OpenMax AL 层等框架层(Middleware)调用,也可以给应用程序直接调用。
3. OpenMax在Android中的使用情况
4. OpenMax的接口与实现
4.1 OpenMax IL层接口
OpenMax IL层的接口定义由若干个头文件组成,这也是实现它需要实现的内容,位于/frameworks/native/include/media/openmax/下,
它们的基本描述如下所示:
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标准只有头文件,没有标准的库,设置没有定义函数接口。对于实现者,需要实现的主要是包含函数指针的结构体。
其中,OMX_Component.h中定义的OMX_COMPONENTTYPE结构体是OpenMax IL层的核心内容,表示一个组件,其内容如下所示:
[->frameworks/native/include/media/openmax/OMX_Component.h]
typedef struct OMX_COMPONENTTYPE
{
OMX_U32 nSize; /* 这个结构体的大小 */
OMX_VERSIONTYPE nVersion; /* 版本号 */
OMX_PTR pComponentPrivate; /* 这个组件的私有数据指针. */
/* 调用者(IL client)设置的指针,用于保存它的私有数据,传回给所有的回调函数 */
OMX_PTR pApplicationPrivate;
/* 以下的函数指针返回OMX_core.h中的对应内容 */
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结构的索引 */
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)(/* 为某个端口使用Buffer */
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)(/* 在某个端口分配Buffer */
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)(/*将某个端口Buffer释放*/
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*EmptyThisBuffer)(/* 让组件消耗这个Buffer */
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*FillThisBuffer)(/* 让组件填充这个Buffer */
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;
4.2 Android中OpenMax的适配层
Android中的OpenMax适配层的接口在frameworks/av/include/media/IOMX.h文件定义,其内容如下所示:
class IOMX : public IInterface {
public:
DECLARE_META_INTERFACE(OMX);
typedef void *buffer_id;
typedef void *node_id;
virtual bool livesLocally(pid_t pid) = 0;
struct ComponentInfo {// 组件的信息
String8 mName;
List mRoles;
};
virtual status_t listNodes(List *list) = 0; // 节点列表
virtual status_t allocateNode(
const char *name, const sp &observer, // 分配节点
node_id *node) = 0;
virtual status_t freeNode(node_id node) = 0; // 找到节点
virtual status_t sendCommand(// 发送命令
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
virtual status_t getParameter(// 获得参数
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) = 0;
virtual status_t setParameter(// 设置参数
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
virtual status_t getConfig(// 获得配置
node_id node, OMX_INDEXTYPE index,
void *params, size_t size) = 0;
virtual status_t setConfig(// 设置配置
node_id node, OMX_INDEXTYPE index,
const void *params, size_t size) = 0;
virtual status_t useBuffer(// 使用缓冲区
node_id node, OMX_U32 port_index, const sp ¶ms,
buffer_id *buffer) = 0;
virtual status_t allocateBuffer(// 分配缓冲区
node_id node, OMX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data) = 0;
virtual status_t allocateBufferWithBackup(// 分配带后备缓冲区
node_id node, OMX_U32 port_index, const sp ¶ms,
buffer_id *buffer) = 0;
virtual status_t freeBuffer(// 释放缓冲区
node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
virtual status_t fillBuffer(node_id node, buffer_id buffer) = 0; // 填充缓冲区
virtual status_t emptyBuffer(// 消耗缓冲区
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
OMX_U32 flags, OMX_TICKS timestamp) = 0;
virtual status_t getExtensionIndex(
node_id node,
const char *parameter_name,
OMX_INDEXTYPE *index) = 0;
virtual sp createRenderer(// 创建渲染器(从ISurface)
const sp &surface,
const char *componentName,
OMX_COLOR_FORMATTYPE colorFormat,
size_t encodedWidth, size_t encodedHeight,
size_t displayWidth, size_t displayHeight) = 0;
......
};
IOMX表示的是OpenMax的一个组件,根据Android的Binder IPC机制,BnOMX继承IOMX,实现者需要继承实现BnOMX。IOMX类中,有标准的OpenMax的GetParameter,SetParameter,GetConfig,SetConfig,SendCommand,UseBuffer,AllocateBuffer,FreeBuffer,FillThisBuffer和EmptyThisBuffer等接口。
在IOMX.h文件中,另有表示观察器类的IOMXObserver,这个类表示OpenMax的观察者,其中只包含一个onMessage()函数,其参数为omx_message接口体,其中包含Event事件类型、FillThisBuffer完成和EmptyThisBuffer完成几种类型。
提示:Android中OpenMax的适配层是OpenMAX IL层至上的封装层,在Android系统中被StageFright调用,也可以被其他部分调用。
4.3 一个qcom OpenMax IL组件的实现
高通平台对于编解码组件的处理都比较集中,不像TI那么分散和细致。一个组件实现都要包含Qc_omx_component.h头文件,位于很多地方,如hardware/qcom/media/mm-core/inc,要实现里面相关纯虚函数。当一个组件被创建后要初始化,就要实现component_init(OMX_IN OMX_STRING componentName)方法。
OMX_ERRORTYPE omx_vdec::component_init(OMX_STRING role)
{
OMX_ERRORTYPE eRet = OMX_ErrorNone;
struct v4l2_fmtdesc fdesc;
struct v4l2_format fmt;
struct v4l2_requestbuffers bufreq;
struct v4l2_control control;
unsigned int alignment = 0,buffer_size = 0;
int fds[2];
int r,ret=0;
bool codec_ambiguous = false;
//打开设备文件"/dev/video/venus_dec"或"/dev/video/q6_dec"
OMX_STRING device_name = (OMX_STRING)DEVICE_NAME;
......
drv_ctx.video_driver_fd = open(device_name, O_RDWR);
......
//如果是一个打开成功后,为什么要再次打开??excuse me ?
if (drv_ctx.video_driver_fd == 0) {
drv_ctx.video_driver_fd = open(device_name, O_RDWR);
}
//打开设备文件失败
if (drv_ctx.video_driver_fd < 0) {
DEBUG_PRINT_ERROR("Omx_vdec::Comp Init Returning failure, errno %d", errno);
return OMX_ErrorInsufficientResources;
}
drv_ctx.frame_rate.fps_numerator = DEFAULT_FPS;//帧率分子
drv_ctx.frame_rate.fps_denominator = 1;//帧率分母
//创建一个异步线程,执行async_message_thread函数,对输入端进行设置
ret = pthread_create(&async_thread_id,0,async_message_thread,this);
//创建线程失败,则关闭设备文件
if (ret < 0) {
close(drv_ctx.video_driver_fd);
DEBUG_PRINT_ERROR("Failed to create async_message_thread");
return OMX_ErrorInsufficientResources;
}
......
// Copy the role information which provides the decoder kind
//将组建角色名字copy进设备驱动上下文结构体的kind属性
strlcpy(drv_ctx.kind,role,128);
//如果是mpeg4解码组件
if (!strncmp(drv_ctx.kind,"OMX.qcom.video.decoder.mpeg4",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.mpeg4",\
OMX_MAX_STRINGNAME_SIZE);
drv_ctx.timestamp_adjust = true;
drv_ctx.decoder_format = VDEC_CODECTYPE_MPEG4;
eCompressionFormat = OMX_VIDEO_CodingMPEG4;
output_capability=V4L2_PIX_FMT_MPEG4;
/*Initialize Start Code for MPEG4*/
codec_type_parse = CODEC_TYPE_MPEG4;
m_frame_parser.init_start_codes (codec_type_parse);
......
//如果是mpeg2解码组件
} else if (!strncmp(drv_ctx.kind,"OMX.qcom.video.decoder.mpeg2",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.mpeg2",\
OMX_MAX_STRINGNAME_SIZE);
drv_ctx.decoder_format = VDEC_CODECTYPE_MPEG2;
output_capability = V4L2_PIX_FMT_MPEG2;
eCompressionFormat = OMX_VIDEO_CodingMPEG2;
/*Initialize Start Code for MPEG2*/
codec_type_parse = CODEC_TYPE_MPEG2;
m_frame_parser.init_start_codes (codec_type_parse);
......
//如果是h263解码组件
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.h263",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.h263",OMX_MAX_STRINGNAME_SIZE);
DEBUG_PRINT_LOW("H263 Decoder selected");
drv_ctx.decoder_format = VDEC_CODECTYPE_H263;
eCompressionFormat = OMX_VIDEO_CodingH263;
output_capability = V4L2_PIX_FMT_H263;
codec_type_parse = CODEC_TYPE_H263;
m_frame_parser.init_start_codes (codec_type_parse);
......
//如果是divx311...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.divx311",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.divx",OMX_MAX_STRINGNAME_SIZE);
DEBUG_PRINT_LOW ("DIVX 311 Decoder selected");
drv_ctx.decoder_format = VDEC_CODECTYPE_DIVX_3;
output_capability = V4L2_PIX_FMT_DIVX_311;
eCompressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
codec_type_parse = CODEC_TYPE_DIVX;
m_frame_parser.init_start_codes (codec_type_parse);
eRet = createDivxDrmContext();
if (eRet != OMX_ErrorNone) {
DEBUG_PRINT_ERROR("createDivxDrmContext Failed");
return eRet;
}
//如果是divx4...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.divx4",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.divx",OMX_MAX_STRINGNAME_SIZE);
DEBUG_PRINT_ERROR ("DIVX 4 Decoder selected");
drv_ctx.decoder_format = VDEC_CODECTYPE_DIVX_4;
output_capability = V4L2_PIX_FMT_DIVX;
eCompressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
codec_type_parse = CODEC_TYPE_DIVX;
codec_ambiguous = true;
m_frame_parser.init_start_codes (codec_type_parse);
eRet = createDivxDrmContext();
if (eRet != OMX_ErrorNone) {
DEBUG_PRINT_ERROR("createDivxDrmContext Failed");
return eRet;
}
//如果是divx...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.divx",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.divx",OMX_MAX_STRINGNAME_SIZE);
DEBUG_PRINT_ERROR ("DIVX 5/6 Decoder selected");
drv_ctx.decoder_format = VDEC_CODECTYPE_DIVX_6;
output_capability = V4L2_PIX_FMT_DIVX;
eCompressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
codec_type_parse = CODEC_TYPE_DIVX;
codec_ambiguous = true;
m_frame_parser.init_start_codes (codec_type_parse);
eRet = createDivxDrmContext();
if (eRet != OMX_ErrorNone) {
DEBUG_PRINT_ERROR("createDivxDrmContext Failed");
return eRet;
}
//如果是avc/h264...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.avc",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.avc",OMX_MAX_STRINGNAME_SIZE);
drv_ctx.decoder_format = VDEC_CODECTYPE_H264;
output_capability=V4L2_PIX_FMT_H264;
eCompressionFormat = OMX_VIDEO_CodingAVC;
codec_type_parse = CODEC_TYPE_H264;
m_frame_parser.init_start_codes (codec_type_parse);
m_frame_parser.init_nal_length(nal_length);
......
//如果是hevc/h265...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.hevc",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.hevc",OMX_MAX_STRINGNAME_SIZE);
drv_ctx.decoder_format = VDEC_CODECTYPE_HEVC;
output_capability=V4L2_PIX_FMT_HEVC;
eCompressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingHevc;
codec_type_parse = CODEC_TYPE_HEVC;
m_frame_parser.init_start_codes (codec_type_parse);
m_frame_parser.init_nal_length(nal_length);
...
//如果是vc1...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.vc1",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.vc1",OMX_MAX_STRINGNAME_SIZE);
drv_ctx.decoder_format = VDEC_CODECTYPE_VC1;
eCompressionFormat = OMX_VIDEO_CodingWMV;
codec_type_parse = CODEC_TYPE_VC1;
output_capability = V4L2_PIX_FMT_VC1_ANNEX_G;
m_frame_parser.init_start_codes (codec_type_parse);
//如果是wmv...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.wmv",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.vc1",OMX_MAX_STRINGNAME_SIZE);
drv_ctx.decoder_format = VDEC_CODECTYPE_VC1_RCV;
eCompressionFormat = OMX_VIDEO_CodingWMV;
codec_type_parse = CODEC_TYPE_VC1;
output_capability = V4L2_PIX_FMT_VC1_ANNEX_L;
m_frame_parser.init_start_codes (codec_type_parse);
//如果是vp8...
} else if (!strncmp(drv_ctx.kind, "OMX.qcom.video.decoder.vp8",\
OMX_MAX_STRINGNAME_SIZE)) {
strlcpy((char *)m_cRole, "video_decoder.vp8",OMX_MAX_STRINGNAME_SIZE);
output_capability=V4L2_PIX_FMT_VP8;
eCompressionFormat = OMX_VIDEO_CodingVPX;
codec_type_parse = CODEC_TYPE_VP8;
arbitrary_bytes = false;
// 如果是不认识的解码组件,则报错
} else {
DEBUG_PRINT_ERROR("ERROR:Unknown Component");
eRet = OMX_ErrorInvalidComponentName;
}
//如果没有错误
if (eRet == OMX_ErrorNone) {
//设置视频输出编码格式为YUV的一种
drv_ctx.output_format = VDEC_YUV_FORMAT_NV12;
//设置颜色编码
OMX_COLOR_FORMATTYPE dest_color_format = (OMX_COLOR_FORMATTYPE)
QOMX_COLOR_FORMATYUV420PackedSemiPlanar32m;
if (!client_buffers.set_color_format(dest_color_format)) {
DEBUG_PRINT_ERROR("Setting color format failed");
eRet = OMX_ErrorInsufficientResources;
}
//订阅事件
capture_capability= V4L2_PIX_FMT_NV12;
ret = subscribe_to_events(drv_ctx.video_driver_fd);
if (ret) {
DEBUG_PRINT_ERROR("Subscribe Event Failed");
return OMX_ErrorInsufficientResources;
}
struct v4l2_capability cap;
//设置查询能力标志位
ret = ioctl(drv_ctx.video_driver_fd, VIDIOC_QUERYCAP, &cap);
if (ret) {
DEBUG_PRINT_ERROR("Failed to query capabilities");
/*TODO: How to handle this case */
} else {
DEBUG_PRINT_HIGH("Capabilities: driver_name = %s, card = %s, bus_info = %s,"
" version = %d, capabilities = %x", cap.driver, cap.card,
cap.bus_info, cap.version, cap.capabilities);
}
ret=0;
fdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fdesc.index=0;
while (ioctl(drv_ctx.video_driver_fd, VIDIOC_ENUM_FMT, &fdesc) == 0) {
DEBUG_PRINT_HIGH("fmt: description: %s, fmt: %x, flags = %x", fdesc.description,
fdesc.pixelformat, fdesc.flags);
fdesc.index++;
}
fdesc.type=V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fdesc.index=0;
while (ioctl(drv_ctx.video_driver_fd, VIDIOC_ENUM_FMT, &fdesc) == 0) {
DEBUG_PRINT_HIGH("fmt: description: %s, fmt: %x, flags = %x", fdesc.description,
fdesc.pixelformat, fdesc.flags);
fdesc.index++;
}
update_resolution(320, 240);
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix_mp.height = drv_ctx.video_resolution.frame_height;
fmt.fmt.pix_mp.width = drv_ctx.video_resolution.frame_width;
fmt.fmt.pix_mp.pixelformat = output_capability;
ret = ioctl(drv_ctx.video_driver_fd, VIDIOC_S_FMT, &fmt);
if (ret) {
/*TODO: How to handle this case */
DEBUG_PRINT_ERROR("Failed to set format on output port");
}
DEBUG_PRINT_HIGH("Set Format was successful");
//如果有歧义的解码组件
if (codec_ambiguous) {
if (output_capability == V4L2_PIX_FMT_DIVX) {
struct v4l2_control divx_ctrl;
if (drv_ctx.decoder_format == VDEC_CODECTYPE_DIVX_4) {
divx_ctrl.value = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_4;
} else if (drv_ctx.decoder_format == VDEC_CODECTYPE_DIVX_5) {
divx_ctrl.value = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_5;
} else {
divx_ctrl.value = V4L2_MPEG_VIDC_VIDEO_DIVX_FORMAT_6;
}
divx_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_DIVX_FORMAT;
ret = ioctl(drv_ctx.video_driver_fd, VIDIOC_S_CTRL, &divx_ctrl);
if (ret) {
DEBUG_PRINT_ERROR("Failed to set divx version");
}
} else {
DEBUG_PRINT_ERROR("Codec should not be ambiguous");
}
}
//解码相关参数设置
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix_mp.height = drv_ctx.video_resolution.frame_height;
fmt.fmt.pix_mp.width = drv_ctx.video_resolution.frame_width;
fmt.fmt.pix_mp.pixelformat = capture_capability;
ret = ioctl(drv_ctx.video_driver_fd, VIDIOC_S_FMT, &fmt);
if (ret) {
/*TODO: How to handle this case */
DEBUG_PRINT_ERROR("Failed to set format on capture port");
}
DEBUG_PRINT_HIGH("Set Format was successful");
if (secure_mode) {
control.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE;
control.value = 1;
DEBUG_PRINT_LOW("Omx_vdec:: calling to open secure device %d", ret);
ret=ioctl(drv_ctx.video_driver_fd, VIDIOC_S_CTRL,&control);
if (ret) {
DEBUG_PRINT_ERROR("Omx_vdec:: Unable to open secure device %d", ret);
close(drv_ctx.video_driver_fd);
return OMX_ErrorInsufficientResources;
}
}
/*Get the Buffer requirements for input and output ports*/
//获得输入和输出的缓冲条件
drv_ctx.ip_buf.buffer_type = VDEC_BUFFER_TYPE_INPUT;
drv_ctx.op_buf.buffer_type = VDEC_BUFFER_TYPE_OUTPUT;
if (secure_mode) {
drv_ctx.op_buf.alignment=SZ_1M;
drv_ctx.ip_buf.alignment=SZ_1M;
} else {
drv_ctx.op_buf.alignment=SZ_4K;
drv_ctx.ip_buf.alignment=SZ_4K;
}
drv_ctx.interlace = VDEC_InterlaceFrameProgressive;
drv_ctx.extradata = 0;
drv_ctx.picture_order = VDEC_ORDER_DISPLAY;
control.id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER;
control.value = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY;
ret = ioctl(drv_ctx.video_driver_fd, VIDIOC_S_CTRL, &control);
drv_ctx.idr_only_decoding = 0;
m_state = OMX_StateLoaded;
#ifdef DEFAULT_EXTRADATA
if (eRet == OMX_ErrorNone && !secure_mode)
enable_extradata(DEFAULT_EXTRADATA, true, true);
#endif
eRet=get_buffer_req(&drv_ctx.ip_buf);
DEBUG_PRINT_HIGH("Input Buffer Size =%d",drv_ctx.ip_buf.buffer_size);
get_buffer_req(&drv_ctx.op_buf);
//如果解码器格式是h264或者hevc/h265
if (drv_ctx.decoder_format == VDEC_CODECTYPE_H264 ||
drv_ctx.decoder_format == VDEC_CODECTYPE_HEVC) {
h264_scratch.nAllocLen = drv_ctx.ip_buf.buffer_size;
h264_scratch.pBuffer = (OMX_U8 *)malloc (drv_ctx.ip_buf.buffer_size);
h264_scratch.nFilledLen = 0;
h264_scratch.nOffset = 0;
if (h264_scratch.pBuffer == NULL) {
DEBUG_PRINT_ERROR("h264_scratch.pBuffer Allocation failed ");
return OMX_ErrorInsufficientResources;
}
}
//如果解码器格式是h264
if (drv_ctx.decoder_format == VDEC_CODECTYPE_H264) {
if (m_frame_parser.mutils == NULL) {
m_frame_parser.mutils = new H264_Utils();
if (m_frame_parser.mutils == NULL) {
DEBUG_PRINT_ERROR("parser utils Allocation failed ");
eRet = OMX_ErrorInsufficientResources;
} else {
m_frame_parser.mutils->initialize_frame_checking_environment();
m_frame_parser.mutils->allocate_rbsp_buffer (drv_ctx.ip_buf.buffer_size);
}
}
//创建一个h264流的解析器
h264_parser = new h264_stream_parser();
if (!h264_parser) {
DEBUG_PRINT_ERROR("ERROR: H264 parser allocation failed!");
eRet = OMX_ErrorInsufficientResources;
}
}
//打开一个管道,读写端保存进fds数组
if (pipe(fds)) {
DEBUG_PRINT_ERROR("pipe creation failed");
eRet = OMX_ErrorInsufficientResources;
} else {
int temp1[2];
if (fds[0] == 0 || fds[1] == 0) {
if (pipe (temp1)) {
DEBUG_PRINT_ERROR("pipe creation failed");
return OMX_ErrorInsufficientResources;
}
//close (fds[0]);
//close (fds[1]);
fds[0] = temp1 [0];
fds[1] = temp1 [1];
}
//输入/读
m_pipe_in = fds[0];
//输出/写
m_pipe_out = fds[1];
//创建一个工作线程,调用omx开始处理解码,并进行i/o操作
r = pthread_create(&msg_thread_id,0,message_thread,this);
if (r < 0) {
DEBUG_PRINT_ERROR("component_init(): message_thread creation failed");
eRet = OMX_ErrorInsufficientResources;
}
}
}
//没有错误,然后收尾
if (eRet != OMX_ErrorNone) {
DEBUG_PRINT_ERROR("Component Init Failed");
DEBUG_PRINT_HIGH("Calling VDEC_IOCTL_STOP_NEXT_MSG");
(void)ioctl(drv_ctx.video_driver_fd, VDEC_IOCTL_STOP_NEXT_MSG,
NULL);
DEBUG_PRINT_HIGH("Calling close() on Video Driver");
close (drv_ctx.video_driver_fd);
drv_ctx.video_driver_fd = -1;
} else {
DEBUG_PRINT_HIGH("omx_vdec::component_init() success");
}
//memset(&h264_mv_buff,0,sizeof(struct h264_mv_buffer));
return eRet;
}
这个是解码组件的初始化实现, 步骤大概如下(maybe wrong):
打开media相关设备文件
5. Android中OpenMax的实现
5.1 android MediaCodec ACodec
1、ACodec消息机制:
ACodec有一个BaseState和派生出来的其他State,如 UninitializedState, LoadedToIdleState, ExecutingState等。当有消息过来时,如果派生类有重写的方法,则会调到重写的方法,如果没有,则会调到BaseState的
ACodec继承自AHierarchicalStateMachine类,该类用于将收到的消息传递给哪个state。
ACodec收到的消息分两种,一种是MediaCodec传过来的,对应onMessageReceived方法;另一种是OMX Component传过来的,对应onOMXMessage方法。而onOMXMessage里面又分了4种情况来调用不同的方法。(EVENT、EMPTY_BUFFER_DONE、FILL_BUFFER_DONE和FRAME_RENDERED)
2、MediaCodec与ACodec的通知:
OMX的组件解码之后,ACodec::BaseState:: onOMXFillBufferDone (…)会被回调,去取得解码后的数据。然后会在onOMXFillBufferDone中调用notify通知MediaCodec,发给MediaCodec的消息形如notify->setInt32(“what”, CodecBase::kWhatDrainThisBuffer);
MediaCodec收到ACodec发的消息之后会updateBuffers(kPortIndexOutput, msg) 进行更新,同时调用onOutputBufferAvailable()中通知NuPlayer::Decoder有可用的output buffer。
3、ACodec有三种端口模式状态,其会根据当前处于哪个状态来决定buffer如何处理:
KEEP_BUFFERS:当ACodec处于BaseState或者收到OnInputBufferFilled消息但是buffer里面没有填充有效的数据时,ACodec握有的buffer不会送到OMX 组件;
RESUBMIT_BUFFERS:当ACodec处于ExecutingState或者处于OutputPortSettingChangedState但是当前是input口的buffer时,ACodec将握有的buffer送给OMX 组件;
FREE_BUFFERS:当ACodec处于OutputPortSettingChangedState并且当前是output口的buffer时,ACodec将握有的buffer free。
4、stagefright类的调用关系:
OMXNodeInstance负责创建并维护不同的实例,这些实例以node作为唯一标识。这样播放器中每个ACodec在OMX服务端都对应有了自己的OMXNodeInstance实例。
OMXMaster用来维护底层软硬件解码库,根据OMXNodeInstance中想要的解码器来创建解码实体组件。
OMXPluginBase负责加载组件库,创建组件实例,由OMXMaster管理。Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。(Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent)
OMXClient是客户端用来与OMX IL进行通信的。
内部结构CallbackDispatcher作用是用来接收回调函数的消息
OMXNodeInstance + CallbackDispatcher = 合作完成不同实例的消息处理任务
5、ACodec同OMXNodeInstance的消息传递:
ACodec将CodecObserver observer对象通过omx->allocateNode()传递到OMXNodeInstance。
OMXNodeInstance将kCallbacks(OnEvent,OnEmptyBufferDone,OnFillBufferDone)传递给OMX Component
当OMX Component有消息notify上来时,OMXNodeInstance最先收到,然后调用OMX.cpp。将消息在OMX.cpp里面将OMX Component thread转换到CallbackDispatcher线程中处理。CallbackDispatcher又将消息反调到OMXNodeInstance. 最后调用CodecObserver 的onMessage()回到ACodec中
6、 ACodec与OMX组件的关系
ACodec ,CodecObserver和OMXNodeInstance是一一对应的,简单的可以理解它们3个构成了OpenMAX IL的一个Component,每一个node就是一个codec在OMX服务端的标识。当然还有CallbackDispatcher,用于处理codec过来的消息,通过它的post/loop/dispatch来发起接收,从OMX.cpp发送消息,最终通过OMXNodeInstance::onMessage -> CodecObserver::onMessage -> ACodec::onMessage一路往上,当然消息的来源是因为我们有向codec注册OMXNodeInstance::kCallbacks。
而在OMXPluginBase创建组件实例的时候,需要传递一个callback给组件,这个callback用于接收组件的消息,它的实现是在OMXNodeInstance.cpp中。而kcallbacks是OMXNodeInstance的静态成员变量,它内部的三个函数指针分别指向了OMXNodeInstance的三个静态方法,也即是这三个方法与组件进行着消息传递
对于NuPlayer来说,它并不直接接触解码组件,而是通过创建ACodec来和组件交互。ACode内部有一个id,这个id对应于一个OMXNodeInstance。OMX对象中会对产生的每一个OMXNodeInstance分配一个唯一的node_id。每一个OMXNodeInstance内部又保存着组件实例的指针【OMX_HANDLETYPE mHandle;】,通过这个指针就可以和组件进行交互。交互的流程为:ACodec → OMX → OMXNodeInstance → COMPONENT。
8、组件的管理
对组件的管理可以总结为:通过OMXMaster加载libstagefrighthw.so库文件,创建OMXPluginBase【即创建继承此类的组件对象】,通过这个类来管理组件。
Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent。OMXPluginBase扮演者组件的管理者。它负责加载组件库,创建组件实例。而OMXMaster则管理着OMXPluginBase,Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。
对于厂商来说,如果要实现自己的组件管理模块,需要通过继承实现OMXPluginBase,并将之编译为libstagefrighthw.so。在OMXMaster中会加载这个库文件,然后调用其createOMXPlugin方法获得一个OMXPluginBase指针,然后将其加入OMXPluginBase列表以及与组件名相关的map 【mPluginByComponentName】中,后续都会通过OMXPluginBase来管理组件。