Android-video openMAX详解

本文参考: 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/

  • QComOMXPlugin.cpp
  • QComOMXMetadata.h
  • QComOMXPlugin.h

/hardware/qcom/media/mm-video-v4l2/vidc/vdec/src/   (venc也是类似)

  • omx_swvdec.cpp
  • omx_swvdec_utils.cpp
  • omx_vdec_extensions.hpp   
  • omx_vdec_v4l2.cpp

/hardware/qcom/media/mm-core/inc/

  • 此处很多omx相关头文件

/frameworks/av/media/libstagefright/omx/

  • SoftOMXPlugin.cpp
  • OmxGraphicBufferSource.cpp
  • OMXMaster.cpp
  • OMXNodeInstance.cpp
  • SoftOMXComponent.cpp

/frameworks/av/media/libstagefright/omx/1.0/

  • Omx.cpp
  • WOmxObserver.cpp

/frameworks/av/media/libmedia/omx/1.0/

  • WOmx.cpp
  • WGraphicBufferSource.cpp
  • WOmxObserver.cpp

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(集成层)

Android-video openMAX详解_第1张图片

  图中的虚线中的内容是OpenMax IL层的内容,其主要实现了OpenMax IL中的各个组件(Component)。对下层,OpenMax IL可以调用OpenMax DL层的接口,也可以直接调用各种Codec实现。对上层,OpenMax IL可以给OpenMax AL 层等框架层(Middleware)调用,也可以给应用程序直接调用。

 

3. OpenMax在Android中的使用情况

Android-video openMAX详解_第2张图片

 

  1. android系统中只用openmax来做Codec,所以android向上抽象了一层OMXCodec,提供给上层播放器用。播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。
  2. OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中的实现。
  3. OMX把软编解码和硬件编解码统一看作插件的形式管理起来。

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;
  •  1)EmptyThisBuffer和FillThisBuffer是驱动组件运行的基本的机制,前者表示让组件消耗缓冲区,表示对应组件输入的内容;后者表示让组件填充缓冲区,表示对应组件输出的内容。
  •  2)UseBuffer,AllocateBuffer,FreeBuffer为和端口相关的缓冲区管理函数,对于组件的端口有些可以自己分配缓冲区,有些可以使用外部的缓冲区,因此有不同的接口对其进行操作。
  •  3)SendCommand表示向组件发送控制类的命令。GetParameter,SetParameter,GetConfig,SetConfig几个接口用于辅助的参数和配置的设置和获取。
  •  4)ComponentTunnelRequest用于组件之间的隧道化连接,其中需要制定两个组件及其相连的端口。
  •  5)ComponentDeInit用于组件的反初始化。

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相关设备文件

  1. 创建一个异步线程,执行async_message_thread函数,对输入端进行设置
  2. 根据解码器role名配置相关属性
  3. 对视频解码相关基本配置进行设置
  4. 创建一个管道,然后再开一个个工作线程,调用omx开始处理解码,并进行i/o操作

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的三个静态方法,也即是这三个方法与组件进行着消息传递

Android-video openMAX详解_第3张图片

  对于NuPlayer来说,它并不直接接触解码组件,而是通过创建ACodec来和组件交互。ACode内部有一个id,这个id对应于一个OMXNodeInstance。OMX对象中会对产生的每一个OMXNodeInstance分配一个唯一的node_id。每一个OMXNodeInstance内部又保存着组件实例的指针【OMX_HANDLETYPE mHandle;】,通过这个指针就可以和组件进行交互。交互的流程为:ACodec → OMX → OMXNodeInstance → COMPONENT。

Android-video openMAX详解_第4张图片

8、组件的管理
  对组件的管理可以总结为:通过OMXMaster加载libstagefrighthw.so库文件,创建OMXPluginBase【即创建继承此类的组件对象】,通过这个类来管理组件。
  Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent。OMXPluginBase扮演者组件的管理者。它负责加载组件库,创建组件实例。而OMXMaster则管理着OMXPluginBase,Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。
  对于厂商来说,如果要实现自己的组件管理模块,需要通过继承实现OMXPluginBase,并将之编译为libstagefrighthw.so。在OMXMaster中会加载这个库文件,然后调用其createOMXPlugin方法获得一个OMXPluginBase指针,然后将其加入OMXPluginBase列表以及与组件名相关的map 【mPluginByComponentName】中,后续都会通过OMXPluginBase来管理组件。

Android-video openMAX详解_第5张图片

你可能感兴趣的:(安卓基础)