Android-video openMAX详解

2023-11-11 07:59
文章标签 android 详解 video openmax

本文主要是介绍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(集成层)

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

 

3. OpenMax在Android中的使用情况

 

  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<String8> mRoles;    };    virtual status_t listNodes(List<ComponentInfo> *list) = 0;  // 节点列表    virtual status_t allocateNode(    const char *name, const sp<IOMXObserver> &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<IMemory> ¶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<IMemory> ¶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<IOMXRenderer> createRenderer(// 创建渲染器(从ISurface)    const sp<ISurface> &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_EXTRADATAif (eRet == OMX_ErrorNone && !secure_mode)enable_extradata(DEFAULT_EXTRADATA, true, true);
#endifeRet=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/h265if (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;}}//如果解码器格式是h264if (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的三个静态方法,也即是这三个方法与组件进行着消息传递

  对于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来管理组件。

这篇关于Android-video openMAX详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/388645

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc