Android 13 - Media框架(14)- OpenMax(三)

上一节学习了 media.codec 服务中的部分内容,这一节我们将一起了解 OMX IL 层的 API 以及相关的结构体等内容。

1、相关路径

以下是 Media 相关的头文件路径:
frameworks/native/headers/media_plugin/media/
cas 和 drm 是用于加密流解密使用,editor 里面有个 OMX_COLOR_FormatYUV420Planar 颜色空间的转换工具,这三个目录这里不做了解(不会),我们要重点关注的是 openmaxhardware 目录 。

1.1 hardware

frameworks/native/headers/media_plugin/media/hardware/
hardware 目录下放的是和硬件相关的一些头文件,CryptoAPI.h 大概是用于解密的,HDCPAPI.h 是用于HDMI加密的,这两个暂时不做了解;

OMXPluginBase.h 在前一节中已经做过了解了,libstagefrighthw.so 中的方法会创建一个 OMXPluginBase 对象;

VideoAPI.h 中声明有 media image (frame),以及 Aspects of color 等相关的结构体,这里不做过多描述。

1.1.1 HardwareAPI.h

以下内容部分翻译自 HardwareAPI.h 中的注释:

  • struct EnableAndroidNativeBuffersParams:这个结构体用于把android native buffer 用作 graphic buffer 和 secure buffer
    • graphic buffer usage:当 OMX node 通过 OMX_SetParameter 设定 OMX.google.android.index.enableAndroidNativeBuffers 配置时,这个功能将被打开(默认关闭);
      当某个端口的 native buffer 使用被禁止时,该端口将正常被使用,端口使用的 buffer 会用 UseBuffer 来设定,这是当 CPU 需要访问 buffer 时将使用的模式;
      当 native buffer 被启用时,视频的 color format 信息将会使用 Android pixel格式,而不是使用 OMX 格式。启用 native buffer 可能会改变 component 接收 buffer 的方式,如果 store-metadata-mode 被使能,那么将会以 metadata的形式传输。此外,除非支持 OMX.google.android.index.useAndroidNativeBuffer2 扩展,否则应该使用 OMX_SetParameter(UseAndroidNativeBuffer )来设定使用的 buffer。
    • secure buffer usage:当 OMX node 通过 OMX_SetParameter 设定 OMX.google.android.index.allocateNativeHandle 配置时,这个功能将被打开(默认关闭);
      当 native handle 在某个端口被禁用了,这个端口将会正常工作,并且预期调用 AllocateBuffer 来分配 buffer 并且返回 buffer 指针,这个模式会用作于 non-secure buffer 并且要组件分配 buffer 的情况;
      当 native handle 被使用时,组件需要分配一个 native_buffer_t 对象(可以通过binder传递),这个模式被用于 secure buffer,与之配合使用的有 nFilledLength、nAllocLength、nOffset 等参数。
    • 注释:之所以这个结构体放在 Hardware API中,我的理解是无论 graphic buffer 还是 secure buffer 都是由硬件分配管理的,上面的翻译中描述了两种模式成功应用和失败应用的情况,至于上层如何使用,后续我们将在 ACodec 章节中来学习。
  • struct StoreMetaDataInBuffersParams:这个结构体用于在 video source(camera…)和 video encoder之间,video decoder 和 output buffer 之间传递数据,而避免数据的拷贝。 meta data 在 OMX client 和 OMX component之间传递时,buffer中存储的并不是真正的 video 数据,而是一些用于定位/解析真正数据的信息。启动 meta data 时,bStoreMetaData 需要设置为 OMX_TRUE,默认情况下将会启用 meta data mode。
    当 bStoreMetaData 为 false 时,真正的 YUV 帧将会存储在 input/output buffer当中。
    Metadata buffer 需要使用 UseBuffer 来注册到 OMX component 中。
    • 当组件支援 OMX.google.android.index.storeANWBufferInMetadata 时,数据将会以 VideoNativeMetadata 的形式传递,并且会用 fence 来协同管理;
    • 当组件支援 OMX.google.android.index.storeMetaDataInBuffers 时,数据将会以 VideoGrallocMetadata 的形式传递。
    • 注释:之所以叫 meta data,指的是buffer中传递的不是真的数据,而是handle或者是物理地址,以及解析出buffer所需要的size、offset等信息,这些信息称为元信息,这个结构体一般会与上面的 grapgic buffer usage 搭配使用。
  • struct ConfigureVideoTunnelModeParams:用于 Android Tunnel Mode,网上讲 Tunnel Mode 的资料还比较少,具体如何实现不同的厂商可能有不同的方案,这里只翻译部分注释。
    • describe:如果 Tunnel Mode 被打开,那么 video decoder 解出的帧将会直接送到 sink(池子) 当中,nAudioHwSync 作为 HW SYNC ID 用来同步 audio hal 与 video 的输出,pSidebandWindow 需要绑定 codec 分配的 sideband window handle。

以上就是 HardwareAPI.h 中的部分内容,ACodec 并不会直接使用这些结构体,ACodec 回调用 IOmxNode 的接口,再由 OMXNodeInstance 解析参数,封装参数,最后传给 OMX component。

1.1.2 MetadataBufferType.h

这个文件中只定义了一个 enum MetadataBufferType,这里只对注释做一下翻译:

MetadataBufferType 定义了可以传递给 encoder 的 metadata buffer 的类型,这些类型主要是用于 libstagefright record框架使用的,这里的 record 可能指的是录屏或者是摄像头。Stagefright 框架是不需要知道关于 meta buffer的细节的,只要创建 meta buffer 和 video encoder 能够保持一致就行。

1.2 openmax

1.2.1 OMX_Core.h

这个文件应该算是 OMX 的入口了,文件一开始的注释:OMX_Core 头文件定了application 和 component 公用的内容,我的理解就是 OpenMax AL 和 IL 层之间的接口。

我们直接拉到文件末尾,可以看到一组函数声明,有心的小伙伴会发现我们在上一节的 QComOMXPlugin 中见过它们,这些方法会被封装在 libOmxCore.so 中,具体的内容需要 vendor 来实现,这里对这几个方法描述进行翻译:

函数参数里的OMX_OUTOMX_IN 用于记录当前的参数是返回参数还是输入参数。

  • OMX_Init:用来初始化 OMX core,这个方法执行时间应该小于 20 ms;
  • OMX_Deinit:对应于 OMX_Init,用于销毁 OMX core 中的内容,执行时间应该小于 20 ms;
  • OMX_ComponentNameEnum:枚举遍历系统中所有可用的编解码组件,第二个参数为一个字符指针,返回组件名称,名称类似于 OMX.qcom.video.decoder.h263;既然有遍历,那就会有一个容器来存储系统支援的所有组件信息,这个容器有各家vendor独立实现,实现不同会影响当前方法的实现;
  • OMX_GetHandle:根据给定的组件名称,将组件加载到内容当中(dlopen),调用组件的方法创建一个组件实例;(这里的意思是每个组件都是一个lib,每次要创建编解码实例都会dlopen,从而实现实例间互不影响,打开lib后会调用其中的方法返回示例,之后会直接使用这个实例,并且实例会被记录在 OMX core 当中);
  • OMX_FreeHandle:释放 component Handle;
  • OMX_GetRolesOfComponent:获取组件的 role;这里的 role 指的是什么呢?个人理解是这样:多个不同的编解码组件可能会走同一套流程,并不是我们所想象的每个组件都会单独编译出一个 lib,虽然说流程相同,但是内部的一些配置可能会不一样,为了区分当前使用的是哪一个组件,就用 role 来区分。

再往上看,又有一组函数声明,这组函数的命名和 struct OMX_COMPONENTTYPE 中定义的函数指针是一一对应的,我们之前有提到创建 OMX 组件后,我们并不是直接使用组件,而是将其封装到 OMXNode 当中,最终由 OMXNode 调用组件的方法,以下这组函数声明就是对调用组件方法的封装,第一个参数 hComponent 指的就是 handle component(组件句柄/指针):

  • OMX_GetComponentVersion:获取组件的信息,这是一个阻塞调用,耗时需要小于 5ms;
  • OMX_SendCommand:向组件发送一条命令,该调用是非阻塞的,组件需要先检查参数是否正确,然后才能将命令加入到组件线程中去执行,组件可能会调用 EventHandler callback 来返回结果,该函数的调用耗时需要小于 5ms;
    • 从这里我们可以知道,omx 组件中应该会有一个 component 线程来专门执行上层下发的命令;
    • 当命令是 OMX_CommandStateSet ,组件将会从 nParam 中获取新的状态,并且加入到命令队列中,从 executing 状态到 loaded 状态需要小于 500ms;
    • 当命令是 OMX_CommandFlush,组件将会强制把指定 port 上所有不在处理状态的 buffer 全部按照接收的顺序 return,每个 port 执行 flush 应该小于 5ms;
    • 当命令是 OMX_CommandPortDisable 或者是 OMX_CommandPortEnable,port 将会被关闭或者重启,耗时应该也小于 5ms;
  • OMX_GetParameter:获取当前组件的参数设置,它是阻塞调用,在 OMX_StateInvalid 状态下不能被调用,其他状态都能调用;它的第二个参数 nParamIndex 表示需要获取的参数类型,该方法执行时间应该小于 20ms;
  • OMX_SetParameter:类似 GetParameter;
  • OMX_GetConfig:从组件获取一个配置的结构体,这是一个阻塞调用,需要在 5ms 内完成;
  • OMX_SetConfig:给组件传递配置,需要与 GetConfig 搭配使用;
  • OMX_GetExtensionIndex:用于将 特定的vendor 配置或者参数翻译成为 OMX 结构体索引;
  • OMX_GetState
  • OMX_UseBuffer:请求组件使用一个已经分配的buffer(来自于另一个组件 或者 来自于上层),并且分配出组件自己的 buffer header,该调用应该在 5ms 内完成;
  • OMX_AllocateBuffer:请求组件分配一个新的buffer 和一个 buffer header,并且将 buffer header 的指针返回,该调用应该在 5ms 内完成;
  • OMX_FreeBuffer:组件将会释放调用创建的 buffer header,如果 buffer 也是组件创建的,那么也会释放掉该 buffer;
  • OMX_EmptyThisBuffer:将一块填有数据的 buffer 送给组件的 input 端口,这块 buffer 被清空时将会调用 EmptyBufferDone 回调,将 buffer 返回给应用层,这是一个非阻塞调用
  • OMX_FillThisBuffer:将一块空的 buffer 送到组件的 output 端口,组件将会填充该 buffer 并且调用 FillBufferDone 回传给应用层,同样的该函数也是非阻塞的
  • OMX_UseEGLImage

解下来再看看其他结构体:

typedef enum OMX_COMMANDTYPE
{
    OMX_CommandStateSet,    /**< Change the component state */
    OMX_CommandFlush,       /**< Flush the data queue(s) of a component */
    OMX_CommandPortDisable, /**< Disable a port on a component. */
    OMX_CommandPortEnable,  /**< Enable a port on a component. */
    OMX_CommandMarkBuffer,  /**< Mark a component/buffer for observation */
    OMX_CommandKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */
    OMX_CommandVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
    OMX_CommandMax = 0X7FFFFFFF
} OMX_COMMANDTYPE;

command type 就是可以设置的命令类型,一般有状态设置,flush,端口的开启和关闭,vendor也可以自定义命令类型。

typedef enum OMX_STATETYPE

OMX_STATETYPE 指的是组件的状态,比较常见的有如下几种:

  • OMX_StateInvalid:组件发生异常;
  • OMX_StateLoaded:组件被创建但是还没有初始化完成;
  • OMX_StateIdle:组件初始化完成并且准备好开始;
  • OMX_StateExecuting:组件收到开始命令并且开始处理数据;
  • OMX_StatePause:组件收到暂停命令,这个用的很少

一些常见的flag:

  • OMX_BUFFERFLAG_EOS:EOS 表示输出端口将不会有数据再出来,所以在最后一块buffer上应该附上 EOS,上层给 decoder 写数据带上 eos 表示当前流结束;
  • OMX_BUFFERFLAG_STARTTIME:这个 flag 标记了起播时或者seek后第一帧应该从哪一帧开始显示渲染;比如说 seek 之后要从某个 pts 开始播放,但是刚好这一帧不是关键帧,那么需要从关键帧开始解码,从目标帧开始渲染。如果组件接收到 starttime,它应该调用 SetConfig 去设置 OMX_ConfigTimeClientStartTime
  • OMX_BUFFERFLAG_DECODEONLY:组件只解码不渲染;
  • OMX_BUFFERFLAG_DATACORRUPT:表示当前buffer中的数据存在错误;
  • OMX_BUFFERFLAG_ENDOFFRAME:表示当前buffer标志着一帧数据的结束,默认情况下Android每次写入都是一帧数据,所以每次数据写给 component 都会带这个flag;
  • OMX_BUFFERFLAG_EXTRADATA:额外的数据需要加在数据流之前,某些格式需要加一些数据头;
  • OMX_BUFFERFLAG_CODECCONFIG:codec specific data (csd),例如播放 h264需要的 SPS/PPS,播放AAC需要的AudioSpecificConfig,没有这些配置信息将无法解码播放。

你可能感兴趣的:(Android,Media,android,framework,音视频,视频编解码)