Bootstrap

OpenMax概述及与Gstreamer的对接

  • OpenMAX为多媒体应用程序定义一套标准的、开放的应用程序编程接口 (API)。该开放标准的目标是降低将多媒体软件移植到新处理器和架构的成本和复杂性
  • C语言实现的跨平台开放多媒体的软件抽象层
  • 由NVIDIA公司和Khronos™在2006年推出
  • 包括三层,分别是应用层(AL)、集成层(IL)和开发层(DL)。其中IL层已经成为了事实上的多媒体框架标准
  • FFmpeg(常见的视频软解码开源库)、MediaCodec(安卓自带的视频编解码接口,代表播放器是ExoPlayer)、Gstreamer(常用开源流媒体应用框架)都已经适配了OpenMax,可以通过配置打开与openmax的对接

三层架构

  • Application Layer:应用和多媒体中间层的标准接口,使得应用在多媒体接口上具有了可移植性。(实际很少人会直接用这一层构建播放器,或者 使用api来播放文件)。
  • Integration Layer:用作嵌入式和/或移动设备中使用的音频、视频和图像编解码器的低级接口。使应用程序和媒体框架能够以统一的方式与多媒体编解码器和支持组件(即源和接收器)交互。编解码器本身可以是硬件或软件的任意组合,并且对用户完全透明。主要目标是使用专门的功能库为编解码器提供一定程度的系统抽象,以解决许多截然不同的媒体系统之间的可移植性问题。(IL层已经成为了事实上的多媒体框架标准,大多数设备厂商都会进行适配)
  • Development Layer:包含一组全面的音频、视频和图像功能API,这些功能可以由芯片供应商在新处理器上实现和优化,然后由编解码器供应商用于编码各种编解码器功能。它包括音频信号处理功能(例如 FFT 和滤波器)、图像处理基元(例如色彩空间转换)和视频处理基元,以支持编解码器(例如 MPEG-4、H.264、MP3、AAC 和 JPEG)的优化实施。。(目的是指导硬件设计,但是实际上也很少参考使用,各家都是私有方案)

Gstreamer对接OpenMax

  • gst-omx是基于openMAX开发的插件
  • 和OMX IL封装的codec进行互连
  • 其实就是一个OMX IL Client

编译gst-omx

gst-omx默认没有编译,如下所示:

#meson out

...
Subproject gst-omx : skipped: feature omx disabled
...

gst-examples              : YES
gst-integration-testsuites: YES
gst-libav                 : YES
gst-omx                   : NO Feature 'omx' disabled
gst-plugins-bad           : YES
gst-plugins-base          : YES 2 warnings
gst-plugins-good          : YES
...

修改根目录的meson_options.txt,如下:

fuqiang@ubuntu:~/workspace/gstreamer$ git diff
diff --git a/meson_options.txt b/meson_options.txt
index 7a43f34430..dc125180ba 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -9,7 +9,7 @@ option('bad', type : 'feature', value : 'auto')
 option('devtools', type : 'feature', value : 'auto')
 option('ges', type : 'feature', value : 'auto')
 option('rtsp_server', type : 'feature', value : 'auto')
-option('omx', type : 'feature', value : 'disabled')
+option('omx', type : 'feature', value : 'auto')
 option('vaapi', type : 'feature', value : 'disabled')
 option('sharp', type : 'feature', value : 'disabled')
 option('rs', type : 'feature', value : 'disabled')

删除out,重新配置:

#rm -rf out
#meson out

...
gst-omx| subprojects/gst-omx/meson.build:227:2: Exception: Problem encountered: Unsupported omx target specified. Use the -Dtarget option

Subproject subprojects/gst-omx is buildable: NO (disabling)

...
gst-libav                 : YES
gst-omx                   : NO Problem encountered: Unsupported omx target specified. Use the -Dtarget option
gst-plugins-bad           : YES
gst-plugins-base          : YES 2 warnings
...

没有配置平台,修改gst-omx的meson_options.txt来修改平台(这里为了测试随意选择了一个平台,后面需要添加自己平台的配置文件,这样更规范)

diff --git a/subprojects/gst-omx/meson_options.txt b/subprojects/gst-omx/meson_options.txt
index e18beb25d8..f417d5fd91 100644
--- a/subprojects/gst-omx/meson_options.txt
+++ b/subprojects/gst-omx/meson_options.txt
@@ -1,7 +1,7 @@
 option('header_path', type : 'string', value : '',
     description : 'An extra include directory to find the OpenMax headers')
 option('target', type : 'combo',
-    choices : ['none', 'generic', 'rpi', 'bellagio', 'tizonia', 'zynqultrascaleplus'], value : 'none',
+    choices : ['none', 'generic', 'rpi', 'bellagio', 'tizonia', 'zynqultrascaleplus'], value : 'bellagio',
     description : 'The OMX platform to target')
 option('struct_packing', type : 'combo',
     choices : ['0', '1', '2', '4', '8'], value : '0',

删除out,重新配置,终于可以:

#rm -rf out
#meson out

...
gst-libav                 : YES
gst-omx                   : YES 1 warnings
gst-plugins-bad           : YES
...

创建自己的配置

fuqiang@ubuntu:~/workspace/gstreamer/subprojects/gst-omx/config$ tree
.
├── bellagio
│   ├── gstomx.conf
│   └── meson.build
├── meson.build
├── rpi
│   ├── gstomx.conf
│   └── meson.build
├── spacemit
│   ├── gstomx.conf
│   └── meson.build
├── tizonia
│   ├── gstomx.conf.in
│   └── meson.build
└── zynqultrascaleplus
    ├── gstomx.conf
    └── meson.build

5 directories, 11 files


fuqiang@ubuntu:~/workspace/gstreamer/subprojects/gst-omx/config/spacemit$ cat gstomx.conf 
[omxh264dec]    ------>创建了一个h264的解码组件
type-name=GstOMXH264Dec
core-name=/usr/local/lib/libomxil-spacemit.so.0    ------>后面要实现这个库
component-name=OMX.spacemit.video_decoder.avc
rank=257
in-port-index=0
out-port-index=1
hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1

相关编译配置的添加:

diff --git a/subprojects/gst-omx/config/meson.build b/subprojects/gst-omx/config/meson.build
index 1068c6d9c6..8c1f435b35 100644
--- a/subprojects/gst-omx/config/meson.build
+++ b/subprojects/gst-omx/config/meson.build
@@ -2,6 +2,8 @@ if omx_target == 'rpi'
   sub = 'rpi'
 elif omx_target == 'bellagio'
   sub = 'bellagio'
+elif omx_target == 'spacemit'
+  sub = 'spacemit'
 elif omx_target == 'zynqultrascaleplus'
   sub = 'zynqultrascaleplus'
 elif omx_target == 'tizonia'
diff --git a/subprojects/gst-omx/meson.build b/subprojects/gst-omx/meson.build
index 5149616ffe..a1fc27de14 100644
--- a/subprojects/gst-omx/meson.build
+++ b/subprojects/gst-omx/meson.build
@@ -204,6 +204,8 @@ elif omx_target == 'rpi'
   endif
 elif omx_target == 'bellagio'
   cdata.set('USE_OMX_TARGET_BELLAGIO', 1)
+elif omx_target == 'spacemit'
+  cdata.set('USE_OMX_TARGET_SPACEMIT', 1)
 elif omx_target == 'zynqultrascaleplus'
   cdata.set('USE_OMX_TARGET_ZYNQ_USCALE_PLUS', 1)
   have_allegro_header = cc.has_header (
diff --git a/subprojects/gst-omx/meson_options.txt b/subprojects/gst-omx/meson_options.txt
index e18beb25d8..2d3fc41a25 100644
--- a/subprojects/gst-omx/meson_options.txt
+++ b/subprojects/gst-omx/meson_options.txt
@@ -1,7 +1,7 @@
 option('header_path', type : 'string', value : '',
     description : 'An extra include directory to find the OpenMax headers')
 option('target', type : 'combo',
-    choices : ['none', 'generic', 'rpi', 'bellagio', 'tizonia', 'zynqultrascaleplus'], value : 'none',
+    choices : ['none', 'generic', 'rpi', 'bellagio', 'tizonia', 'zynqultrascaleplus', 'spacemit'], value : 'spacemit',
     description : 'The OMX platform to target')
 option('struct_packing', type : 'combo',
     choices : ['0', '1', '2', '4', '8'], value : '0',

OpenMax IL的接口与实现

OpenMax IL层的接口定义由若干个头文件组成,这也是实现它需要实现的内容,它们的基本描述如下所示。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:内容的管道定义

OMX_COMPONENTTYPE

OMX_Component.h中定义的OMX_COMPONENTTYPE结构体是OpenMax IL层的核心内容,表示一个组件,其内容如下所示:

typedef struct OMX_COMPONENTTYPE
{
    OMX_U32 nSize;    /* 这个结构体的大小,单位字节 */  
    OMX_VERSIONTYPE nVersion;    /* 版本号 */ 
    OMX_PTR pComponentPrivate;    /* 这个
组件的私有数据指针. */
    OMX_PTR pApplicationPrivate;    /* 调用者(IL client)设置的指针,用于保存它的
私有数据,传回给所有的回调函数 */  

    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);

    /** @ingroup buf */
    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;

;