android的openmax

2019-01-09 16:17:49 sinat_29874521 阅读数 307

在 Android 结构中,OpenMax IL 通常被当做多媒体引擎插件来使用,Android 最早的多媒体引擎是 OpenCore,后续版本逐渐使用 StageFright 来代替,这两种引擎可以使用 OpenMax 作为插件,主要实现编码/解码(Codec)处理。

OpenMax 主要分为三个层次,从上到下分别是 OpenMax DL(OpenMax Development Layer 开发层)、OpenMax IL(OpenMax Integration Layer 集成层)和 OpenMax AL(OpenMax Application Layer 应用层)。其中使用最多的是 OpenMax IL。

(1)OpenMax DL

OpenMax DL既然是开发层,那么它里面肯定定义了集音频、视频好图像功能的 API,这样供应闪可以在一个新的处理器上实现并优化。然后编码/解码供应商使用该法来编写更广泛的编码/解码器功能。OpenMax DL 可以处理 FFT 和 Filter 等音频信号,也可以实现颜色空间转化和处理原始视频,并且可以实现对诸如 MPEG-4、H.264、MP3、AAC 和 JPEG 等编码/解码器的优化。

(2)OpenMax IL

OpenMax IL 为集成层,它里边包含多个组件,用来处理多媒体编码/解码的交互。

(3)OpenMax AL

OpenMax AL 为应用层,那么肯定就和我们的 APP 相关了,里边存在了我们需要使用的方法。

——————————————————我是分割线————————————————————————

我们这里主要介绍的是 OpenMax IL,由于操作系统到硬件的差异和多媒体应用的差异,OpenMax DL 和 OpenMax AL 层相对较少使用。

OpenMax IL 层的解构

如上图,就是 OpenMax 的结构图,虚线部分是 OpenMax IL 层的内容,功能是实现了 OpenMax IL 中的各个组件(Component),对于下层而言,OpenMax IL 既可以调用 OpenMax DL 层的接口,也可以直接调用各种 Codec 实现,对于上层而言,OpenMax IL 既可以给 OpenMax AL 层等框架层(Meddleware)调用,也可以给应用程序直接调用。

OpenMax IL 层的主要内容包括

(1)Client:客户端,OpenMax IL 的调用者;

(2)Component:组件,OpenMax IL 的单元,每一个组件实现一种功能;

(3)Port:端口,组建的输入/输出接口;

(4)Tunneled:隧道化,让两个组件直接连接的方式。

 

如上图,是 OpenMax IL 运作流程图,OpenMax IL 层的客户端通过调用如下四个 OpenMax IL 组件来实现同一个功能。

(1)Source 组件:只有一个输出端口;

(2)Host 组件:只有一个输入端口;

(3)Accelerator 组件:具有一个输入端口,调用了硬件的编码/解码器,加速主要体现在此环节。

(4)Sink 组件:Accelerator 组件和 Sink 组件通过私有通信方式在内部连接,没有经过明确的组件端口。

在使用 OpenMax IL 时,可以由客户端处理数据流,也可以不经由客户端处理数据流。如上图所示,Source 组件到 Host 组件的数据就是经过客户端的;而 Host 组件到 Accelerator 组件的数据就没有经过客户端,使用了隧道化的方式;Accelerator 组件到 Sink 组件甚至可以使用私有的通信方式。

组件的功能和定义端口的类型有着密切的联系,在大多数情况下的联系如下:

(1)只有一个输出端口的是 Source 组件;

(2)只有一个输入端口的是 Host 组件;

(3)有多个输入端口,一个输出端口的是Mux 组件;

(4)有一个输入端口,多个输出端口的是 DeMux 组件,输入和输出各一个端口的座位中间处理环节,这是最常见的组件。

在 Android 系统中,主要使用了 OpenMax 的编码/解码器功能,使用最多的仍然是编码/解码器组件,尽管 OpenMax 也可以生成 输入、输出文件解析/构建等组件。主要是因为以下两点:

(1)输入输出环节和系统有很大关系,如果一定要使用 OpenMax 标准则会比较麻烦;

(2)文件解析/构架环节一般不需要使用硬件加速,因为编码/解码器组件最能体现硬件加速环节,所以经常使用。

在 Android 系统中,当实现 OpenMax IL 层和标准的 OpenMax IL层时需要实现如下两个环节。

(1)编码/解码器驱动程序:位于 Linux 内核空间,通过 Linux 内核 调用驱动程序,调用的驱动程序通常是非标准的驱动程序。

(2)OpenMax IL 层:根据 OpenMax IL 层的标准头文件实现不同功能的组件。

2016-09-24 22:19:31 hejjunlin 阅读数 11672

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52629598

前言:上篇中介绍OMX事件回调,从今天开始,走入Codec部分之OpenMAX框架里。看下今天的Agenda如下:

  • 一张图回顾音视频同步
  • 一张图看清OpenMAX在Android系统中位置
  • OpenMAX是什么
  • OpenMax IL简介
  • OpenMax IL结构
  • Android中OpenMax的使用情况
  • OpenMax的接口与实现
  • Android中OpenMax的适配层
  • mp3,aac解码时序图

一张图回顾音视频同步


这里写图片描述

一张图看清OpenMAX在Android系统中位置:


这里写图片描述

OpenMAX是什么?

以下是官网翻译:

  • OpenMAX™ 是无授权费的,跨平台的应用程序接口API,通过使媒体加速组件能够在开发、集成和编程环节中实现跨多操作系统和处理器硬件平台,提供全面的流媒体编解码器和应用程序便携化。
  • OpenMAX API将会与处理器一同提供,以使库和编解码器开发者能够高速有效地利用新器件的完整加速潜能 - 无需担心其底层的硬件结构。
    OpenMAX分为3层:
    • 第一层:OpenMax DL(Development Layer,开发层)
      • OpenMax DL定义了一个API,它是音频、视频和图像功能的集合。供应商能够在一个新的处理器上实现并优化,然后编解码供应商使用它来编写更广泛的编解码器功能。它包括音频信号的处理功能,如FFT和filter,图像原始处理,如颜色空间转换、视频原始处理,以实现例如MPEG-4、H.264、MP3、AAC和JPEG等编解码器的优化。
    • 第二层:OpenMax IL(Integration Layer,集成层)
      • OpenMax IL作为音频、视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如,资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是透明的底层接口应用于嵌入式、移动设备。它提供了应用程序和媒体框架,透明的。S编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。
    • 第三层:OpenMax AL(Appliction Layer,应用层)
      • OpenMax AL API在应用程序和多媒体中间件之间提供了一个标准化接口,多媒体中间件提供服务以实现被期待的API功能。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52629598

OpenMax的三个层次如图所示(来自OpenMax官网):


这里写图片描述

提示:在实际的应用中,OpenMax的三个层次中使用较多的是OpenMax IL集成层,由于操作系统到硬件的差异和多媒体应用的差异,OpenMax的DL和AL层使用相对较少。

OpenMax IL简介

  • OpenMax IL 处在中间层的位置,OpenMAX IL 作为音频,视频和图像编解码器 能与多媒体编解码器交互,并以统一的行为支持组件(例如资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是的底层接口应用于嵌入式或 / 和移动设备。它提供了应用程序和媒体框架, 透明的。本质上不存在这种标准化的接口,编解码器供 应商必须写私有的或者封闭的接口,集成进移动设备。 IL 的主要目的 是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。

  • OpenMax IL 的目的就是为硬件平台的图形及音视频提供一个抽象层,可以为上层的应用提供一个可跨平台的支撑。这一点对于跨平台的媒体应用来说十分重要。不同厂商的芯片底层的音视频接口虽然功能上大致相同,但是接口设计及用法上各有不同,而且相差很多。你要想让自己开发的媒体 应用完美的运行在不同的硬件厂商平台上,就得适应不同芯片的底层解码接口。这个对于应用开发来说十分繁琐。所以就需要类似于OpenMax IL 这种接口规范。应用假如涉及到音视频相关功能时,只需调用这些标准的接口,而不需要关心接口下方硬件相关的实现。假如换了硬件平台时,只需要把接口层与硬件适配好了就行了。上层应用不需要频繁改动。你可以把OpenMax IL看作是中间件中的porting层接口,但是现在中间件大部分都是自家定义自己的。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52629598

OpenMax IL结构

OpenMax IL的层次结构如图:


这里写图片描述

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

OpenMax IL主要内容如下所示。

  • 客户端(Client):OpenMax IL的调用者
  • 组件(Component):OpenMax IL的单元,每一个组件实现一种功能
  • 端口(Port):组件的输入输出接口
  • 隧道化(Tunneled):让两个组件直接连接的方式

组件、端口、隧道化思想和GStreamer (一种多媒体框架)中的 pipeline 十分类似。
Component实现单一功能、或是Source、Host、Accelerator和Sink。
Port 是 Component对外的输入输出口。
通过Tunneled 将单一Component串联起来形成一个完整功能。
OpenMax Core是辅助各个组件运行的部分

OpenMax IL 的基本运作过程如图

这里写图片描述

如图所示,openMAX IL的客户端,通过调用四个OpenMAX IL组件,实现了一个功能。四个组件分别是Source组件、Host组件、Accelerator组件和Sink组件。Source组件只有一个输出端口;而Host组件有一个输入端口和一个输出端口;Accelerator组件具有一个输入端口,调用了硬件的编解码器,加速主要体现在这个环节上。Accelerator组件和Sink组件通过私有通讯方式在内部进行连接,没有经过明确的组件端口。
OpenMAL IL在使用的时候,其数据流也有不同的处理方式:

  • 既可以经由客户端,也可以不经由客户端。
  • 图中,Source组件到Host组件的数据流就是经过客户端的;
  • 而Host组件到Accelerator组件的数据流就没有经过客户端,使用了隧道化的方式;
  • Accelerator组件和Sink组件甚至可以使用私有的通讯方式。

OpenMax Core是辅助各个组件运行的部分,它通常需要完成各个组件的初始化等工作,在真正运行过程中,重点是各个OpenMax IL的组件,OpenMax Core不是重点,也不是标准。

  • OpenMAL IL的组件是OpenMax IL实现的核心内容,一个组件以输入、输出端口为接口,端口可以被连接到另一个组件上。外部对组件可以发送命令,还进行设置/获取参数、配置等内容。组件的端口可以包含缓冲区(Buffer)的队列。
  • 组件的处理的核心内容是:通过输入端口消耗Buffer,通过输出端口填充Buffer,由此多组件相联接可以构成流式的处理。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52629598

OpenMAL IL中一个组件的结构如图:


这里写图片描述

组件的功能和其定义的端口类型密切相关,通常情况下:

  • 只有一个输出端口的,为Source组件;
  • 只有一个输入端口的,为Sink组件;
  • 有多个输入端口,一个输出端口的为Mux组件;
  • 有一个输入端口,多个输出端口的为DeMux组件;
  • 输入输出端口各一个组件的为中间处理环节,这是最常见的组件。
  • 端口具体支持的数据也有不同的类型。例如,对于一个输入、输出端口各一个组件,其输入端口使用MP3格式的数据,输出端口使用PCM格式的数据,那么这个组件就是一个MP3解码组件。
  • 隧道化(Tunneled)是一个关于组件连接方式的概念。通过隧道化可以将不同的组件的一个输入端口和一个输出端口连接到一起,在这种情况下,两个组件的处理过程合并,共同处理。尤其对于单输入和单输出的组件,两个组件将作为类似一个使用。

Android中OpenMax的使用情况

  • Android系统的一些部分对OpenMax IL层进行使用,基本使用的是标准OpenMax IL层的接口,只是进行了简单的封装。标准的OpenMax IL实现很容易以插件的形式加入到Android系统中。
  • Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为多媒体编解码的插件,只是没有直接使用OpenMax IL层提供的纯C接口,而是对其进行了一定的封装(C++封装)。
  • 在Android2.x版本之后,Android的框架层也对OpenMax IL层的接口进行了封装定义,甚至使用Android中的Binder IPC机制。Stagefright使用了这个层次的接口,OpenCore没有使用。
  • OpenCore使用OpenMax IL层作为编解码插件在前,Android框架层封装OpenMax接口在后面的版本中才引入。

Android OpenMax实现的内容

  • Android中使用的主要是OpenMax的编解码功能。虽然OpenMax也可以生成输入、输出、文件解析-构建等组件,但是在各个系统(不仅是Android)中使用的最多的还是编解码组件。媒体的输入、输出环节和系统的关系很大,引入OpenMax标准比较麻烦;文件解析-构建环节一般不需要使用硬件加速。编解码组件也是最能体现硬件加速的环节,因此最常使用。
  • 在Android中实现OpenMax IL层和标准的OpenMax IL层的方式基本,一般需要实现以下两个环节。
    • 编解码驱动程序:位于Linux内核空间,需要通过Linux内核调用驱动程序,通常使用非标准的驱动程序。
    • OpenMax IL层:根据OpenMax IL层的标准头文件实现不同功能的组件。
      Android中还提供了OpenMax的适配层接口(对OpenMax IL的标准组件进行封装适配),它作为Android本地层的接口,可以被Android的多媒体引擎调用。

OpenMax的接口与实现

OpenMax IL层的接口(1)

OpenMax IL层的接口定义由若干个头文件组成,这也是实现它需要实现的内容,它们的基本描述如下所示。

这里写图片描述

提示:OpenMax标准只有头文件,没有标准的库,设置没有定义函数接口。对于实现者,需要实现的主要是包含函数指针的结构体。
其中,OMX_Component.h中定义的OMX_COMPONENTTYPE结构体是OpenMax IL层的核心内容,表示一个组件,其内容如下所示:

这里写图片描述
这里写图片描述
这里写图片描述

OpenMax IL层的接口(2)

  • OMX_COMPONENTTYPE结构体实现后,其中的各个函数指针就是调用者可以使用的内容。各个函数指针和OMX_core.h中定义的内容相对应。
    EmptyThisBuffer和FillThisBuffer是驱动组件运行的基本的机制,前者表示让组件消耗缓冲区,表示对应组件输入的内容;后者表示让组件填充缓冲区,表示对应组件输出的内容。
  • UseBuffer,AllocateBuffer,FreeBuffer为和端口相关的缓冲区管理函数,对于组件的端口有些可以自己分配缓冲区,有些可以使用外部的缓冲区,因此有不同的接口对其进行操作。
  • SendCommand表示向组件发送控制类的命令。GetParameter,SetParameter,GetConfig,SetConfig几个接口用于辅助的参数和配置的设置和获取。
    ComponentTunnelRequest用于组件之间的隧道化连接,其中需要制定两个组件及其相连的端口。
  • ComponentDeInit用于组件的反初始化。
    提示:OpenMax函数的参数中,经常包含OMX_IN和OMX_OUT等宏,它们的实际内容为空,只是为了标记参数的方向是输入还是输出。
    OMX_Component.h中端口类型的定义为OMX_PORTDOMAINTYPE枚举类型,内容如下所示:

这里写图片描述

音频类型,视频类型,图像类型,其他类型是OpenMax IL层此所定义的四种端口的类型。
端口具体内容的定义使用OMX_PARAM_PORTDEFINITIONTYPE类(也在OMX_Component.h中定义)来表示,其内容如下所示:

这里写图片描述

对于一个端口,其重点的内容如下:

  • 端口的方向(OMX_DIRTYPE):包含OMX_DirInput(输入)和- -OMX_DirOutput(输出)两种
  • 端口分配的缓冲区数目和最小缓冲区数目
  • 端口的类型(OMX_PORTDOMAINTYPE):可以是四种类型
  • 端口格式的数据结构:使用format联合体来表示,具体由四种不同类型来表示,与端口的类型相对应
    OMX_AUDIO_PORTDEFINITIONTYPE,OMX_VIDEO_PORTDEFINITIONTYPE,OMX_IMAGE_PORTDEFINITIONTYPE和OMX_OTHER_PORTDEFINITIONTYPE等几个具体的格式类型,分别在OMX_Audio.h,OMX_Video.h,OMX_Image.h和OMX_Other.h这四个头文件中定义。
    OMX_BUFFERHEADERTYPE是在OMX_Core.h中定义的,表示一个缓冲区的头部结构。

OMX_Core.h中定义的枚举类型OMX_STATETYPE命令表示OpenMax的状态机,内容如下所示:

这里写图片描述

OpenMax组件的状态机可以由外部的命令改变,也可以由内部发生的情况改变。OpenMax IL组件的状态机的迁移关系如图所示:

这里写图片描述

OMX_Core.h中定义的枚举类型OMX_COMMANDTYPE表示对组件的命令类型,内容如下所示:

这里写图片描述

OMX_COMMANDTYPE类型在SendCommand调用中作为参数被使用,其中OMX_CommandStateSet就是改变状态机的命令。

OpenMax IL实现的内容

对于OpenMax IL层的实现,一般的方式并不调用OpenMax DL层。具体实现的内容就是各个不同的组件。OpenMax IL组件的实现包含以下两个步骤。

  • 组件的初始化函数:硬件和OpenMax数据结构的初始化,一般分成函数指针初始化、私有数据结构的初始化、端口的初始化等几个步骤,使用其中的pComponentPrivate成员保留本组件的私有数据为上下文,最后获得填充完成OMX_COMPONENTTYPE类型的结构体。
  • OMX_COMPONENTTYPE类型结构体的各个指针实现:实现其中的各个函数指针,需要使用私有数据的时候,从其中的pComponentPrivate得到指针,转化成实际的数据结构使用。端口的定义是OpenMax IL组件对外部的接口。OpenMax IL常用的组件大都是输入和输出端口各一个。对于最常用的编解码(Codec)组件,通常需要在每个组件的实现过程中,调用硬件的编解码接口来实现。在组件的内部处理中,可以建立线程来处理。OpenMax的组件的端口有默认参数,但也可以在运行时设置,因此一个端口也可以支持不同的编码格式。音频编码组件的输出和音频编码组件的输入通常是原始数据格式(PCM格式),视频编码组件的输出和视频编码组件的输入通常是原始数据格式(YUV格式)。
    提示:在一种特定的硬件实现中,编解码部分具有相似性,因此通常可以构建一个OpenMax组件的”基类”或者公共函数,来完成公共性的操作。

Android中OpenMax的适配层

Android中的OpenMax适配层的接口在frameworks/base/include/media/目录中的IOMX.h文件定义,其内容如下所示:

这里写图片描述
这里写图片描述

  • IOMX表示的是OpenMax的一个组件,根据Android的Binder IPC机制,BnOMX继承IOMX,实现者需要继承实现BnOMX。IOMX类中,除了和标准的OpenMax的GetParameter,SetParameter,GetConfig,SetConfig,SendCommand,UseBuffer,AllocateBuffer,FreeBuffer,FillThisBuffer和EmptyThisBuffer等接口之外,还包含了创造渲染器的接口createRenderer(),创建的接口为IOMXRenderer类型。
  • IOMX中只有第一个createRenderer()函数是纯虚函数,第二个的createRenderer()函数和createRendererFromJavaSurface()通过调用第一个createRenderer()函数实现。

IOMXRenderer类表示一个OpenMax的渲染器,其定义如下所示:

这里写图片描述

  • IOMXRenderer只包含了一个render接口,其参数类型IOMX::buffer_id实际上是void*,根据不同渲染器使用不同的类型。
  • 在IOMX.h文件中,另有表示观察器类的IOMXObserver,这个类表示OpenMax的观察者,其中只包含一个onMessage()函数,其参数为omx_message接口体,其中包含Event事件类型、FillThisBuffer完成和EmptyThisBuffer完成几种类型。

提示:Android中OpenMax的适配层是OpenMAX IL层至上的封装层,在Android系统中被StageFright调用,也可以被其他部分调用。

mp3 aac格式解码时序图

这里写图片描述

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

这里写图片描述

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

2014-07-03 11:03:20 qjclinux 阅读数 1323

最近由于公司在做数字电视,播放器和模块由供应商打包一起卖,驱动调通了,可是播放器要硬件解码,和平台差异,原厂又没有相关文档,就自己试着看了一个系统的播放器流程,顺便整理了一下,也方便以后查询,希望对播放器硬解码的开发能起到点作用。由于对视频也不了解,所以难免有不少错误,欢迎高手指导。

1: 由文件或者设备来创建一个dataSource(DataSource) (其中可以是文件描述符fd, uri ,http)
2:  通过dataSource来创建一个MediaExtractor(即视频的分流器paser)实例
3:  由 MediaExtractor实例来创建两个MediaSource(视频要包含两个, 一个为视频mVideoTrack, 一个为音频 mAudioTrack), 其中就重要的是read函数用于解码器获得原始视频数 据  进行解码
4:  创建一个OMXClient实例并连接(具体调用其connect()方法,并检查是否成功返回)
5:  创建相应的解码器实例 例如:
 mOmxSource = OMXCodec::Create(                                                                                                                                
   mClient.interface(), mAudioTrack->getFormat(),
   false, // createEncoder
   mAudioTrack);

 mVideoSource = OMXCodec::Create(
  mClient.interface(), mVideoTrack->getFormat(),
  false, // createEncoder
  mVideoTrack,   
  NULL, flags,
  mNativeWindow);   
 mAudioTrack mVideoTrack 即为两个MediaSource实例 分别代表原始的数据源
 注意这两个都返回了一个MediaSource的实例(mOmxSource and mVideoSource),这两个是用于用户获得解码后的音视频数据并进行播放使用的

 mNativeWindow(ANativeWindow) 为上层Surface的下层体现,即上层要用于显示的Surface

 创建 ANativeWindow 的方法:
 1:  从JAVA层会通过JNI传递一个JSurface的对象到JNI, 而JNI可以通过这个对象来获取IGraphicBufferProducer, 方法为:
   sp<Surface> surface;   
   sp<IGraphicBufferProducer> gbp;
   if (jSurface) {        
    surface = android_view_Surface_getSurface(env, jSurface);
    if (surface != NULL) {
     gbp = surface->getIGraphicBufferProducer();
    }                  
   }                      
  最终得到 一个 IGraphicBufferProducer 的 实例 gbp
 2: 上层提供一个 sp<IGraphicBufferProducer> &bufferProducer 的一个引用并可以用来创建ANativeWindow mNativeWindow = new Surface(bufferProducer)  //些处参考AwesomePlayer.cpp

6: 调用5返回来的两个MediaSource实例的read方法(主要是用于获取一个MediaBuffer的实例 mVideoBuffer)来获取解码后的数据(即在MediaBuffer中)并进行相应处理(即进行播放)

7: 通过ANativeWindow来显示图像
    status_t err = mNativeWindow->queueBuffer(mNativeWindow.get(), mVideoBuffer->graphicBuffer().get(), -1);
    sp<MetaData> metaData = mVideoBuffer->meta_data();
    metaData->setInt32(kKeyRendered, 1);   //主要设置一个渲染标记,说明已经显示 (猜得)

 

2011-07-30 11:05:22 bluedreamstart 阅读数 2064

OpenMax是一个多媒体应用程序的框架标准。

在Android中,OpenMax IL层(集成层)通常可以用于多媒体引擎的插件,Android多媒体引擎OpenCore和StageFright都可以使用OpenMax做为插件,主要用于编解码处理。

在Android框架层中,定义了由Android封装的OpenMax接口,它使用了C++类型的接口和Android的Binder 的IPC机制。Android封装的OpenMax接口被StageFright使用,OpenCore并不使用这个接口,而是使用其他形式的接口对OpenMax IL层接口进行封装。

OpenMax实际上分为三个层次,自上而下分别是,OpenMax DL(开发层),OpenMax IL(集成层)和OpenMax AL(应用层)

第一层:OpenMax DL(开发层)

OpenMax DL定义了一个API,它是音频、视频和图像功能的集合。

第二层:OpenMax IL(集成层)

OpenMax IL作为音频、视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如,资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是透明的底层接口应用于嵌入式、移动设备。它提供了应用程序和媒体框架,透明的。S编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。

第三层:OpenMax AL(Appliction Layer,应用层)

  OpenMax AL API在应用程序和多媒体中间件之间提供了一个标准化接口,多媒体中间件提供服务以实现被期待的API功能。



2015-03-16 10:35:30 happyzhouxiaopei 阅读数 3705

一、OpenMax简介

    OpenMAX是一个多媒体应用程序的标准。由NVIDIA公司和Khronos™在2006年推出。
    它是无授权费的、跨平台的C语言程序接口序列,这些接口对音频、视频、静态图片的常用操作进行封装。
    它包括三层,分别是应用层(AI)、集成层(IL)和开发层(DL)。其中IL层已经成为了事实上的多媒体框架标准。嵌入式处理器或者多媒体编解码模块的硬件生产者,通常提供标准的OpenMax IL层的软件接口,这样软件的开发者就可以基于这个层次的标准化接口进行多媒体程序的开发。
 

二、OpenMax在Android中的位置

        在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解码(Codec)处理。在Android的框架层,也定义了由Android封装的OpenMax接口,和标准的接口概念基本相同,但是使用C++类型的接口,并且使用了Android的Binder IPC机制。Android封装OpenMax的接口被StageFright使用,OpenCore没有使用这个接口,而是使用其他形式对OpenMax IL层接口进行封装。
        
        

三、OpenMax的主要概念

  • 客户端(Client):访问IL core或IL component的软件层,可能是位于GUI应用程序的下层,如GStreamer。IL client是一个典型的功能块,如filter graph multimedia framework, OpenMAX AL, 或application都可以调用它。IL client与OpenMAX IL core进行交互,利用IL core加载和卸载组件、在组件间建立直接通信以及获得组件方法的入口。
  • core:相关平台的代码,具有将IL component载入主存储器的功能,当应用程序不再需要某组件时,IL core将负责把该组件从存储器卸去。一般来说,组件一旦载入存储器,IL core将不在参与应用程序与组件之间的通信。
  • 端口(Port):组件的输入输出接口
  • 组件(Component):OpenMax IL的单元,每一个组件实现一种功能。组件按照端口可分类为Source(只有一个输出端口)、Sink(只有一个输入端口)和Host组件(一个输入端口和一个输出端口),此外有一个Accelerator组件,它具有一个输入端口,调用了硬件的编解码器,加速主要体现在这个环节上。
  • 隧道化(Tunneled):让两个组件直接连接的方式。通过隧道化可以将不同的组件的一个输入端口和一个输出端口连接到一起,在这种情况下,两个组件的处理过程合并,共同处理。尤其对于单输入和单输出的组件,两个组件将作为类似一个使用。

四、OpenMax的工作方式

        首先,搭建好OpenMax的工作环境:设置组件端口、加载组件,建立连接方式等等。
     
        然后通过输入端口消耗Buffer,通过输出端口填充Buffer,由此多组件相联接可以构成流式的处理。
 

五、OpenMax的测试方式

参考《Build OpenCORE 2.05 on x86 Linux.PDF》和《omx_decoder_test_app_guide.pdf
 

六、OpenMax接口和集成过程(以android平台的为例)


 

七、android中支持的组件和Role

根据pv_omxregistry.cpp,得到如下表格(其中黄绿色部分表示没有相应编码器)
组件 角色 动态库名
OMX.PV.mpeg4dec video_decoder.mpeg4 libomx_m4vdec_sharedlibrary
OMX.PV.h263dec video_decoder.h263 libomx_m4vdec_sharedlibrary
OMX.PV.avcdec video_decoder.avc libomx_avcdec_sharedlibrary
OMX.PV.wmvdec video_decoder.wmv libomx_wmvdec_sharedlibrary
OMX.PV.rvdec video_decoder.rv libomx_rvdec_sharedlibrary
OMX.PV.aacdec audio_decoder.aac libomx_aacdec_sharedlibrary
OMX.PV.amrdec audio_decoder.amr
audio_decoder.amrnb
audio_decoder.amrwb
libomx_amrdec_sharedlibrary
OMX.PV.mp3dec audio_decoder.mp3 libomx_mp3dec_sharedlibrary
OMX.PV.wmadec audio_decoder.wma libomx_wmadec_sharedlibrary
OMX.PV.radec audio_decoder.ra libomx_radec_sharedlibrary
     
OMX.PV.amrencnb audio_encoder.amrnb libomx_amrenc_sharedlibrary
OMX.PV.mpeg4enc video_encoder.mpeg4 libomx_m4venc_sharedlibrary
OMX.PV.h263enc video_encoder.h263 libomx_m4venc_sharedlibrary
OMX.PV.avcenc video_encoder.avc libomx_avcenc_sharedlibrary
OMX.PV.aacenc audio_encoder.aac libomx_aacenc_sharedlibrary
 

八、Openmax 一些函数的简单介绍

1 OMX core methods

1)OMX_Init
2)OMX_Deinit
3)OMX_GetHandle
4)OMX_FreeHandle
5)OMX_ComponentNameEnum
6)OMX_GetComponentsOfRole
7)OMX_GetRolesOfComponent
8)OMX_SetupTunnel
9)OMX_GetContentPipe


2 The configuration parser API
除了以上methods,强烈推荐OMX核心插件库包含此API
2.1函数原型
OMX_BOOL OMXConfigParser ( OMX_PTR aInputParameters,OMX_PTR aOutputParameters);

2.2 传递参数
aInputParameters 指向如下结构
typedef struct
{
OMX_U8* inPtr;    //codec 配置头部指针
OMX_U32 inBytes;   //codec 配置头部长度
OMX_STRING cComponentRole; //OMX codec类型 eg "video_decoder.mpeg4"
OMX_STRING cComponentName; //OMX 组件名称
} OMXConfigParserInputs;

2.3 返回值
OMX_FALSE : 处理codec配置头部错误或不支持该格式
OMX_TURE : 正确处理codec配置头部

2.4 函数作用
填充aOutputParameters,有两种选择:audio coded or vedio codec

for audio
typedef struct
{
OMX_U16 Channels;   //通道:单声道、立体声、5.1
OMX_U16 BitsPerSample;   //位宽(eg16)
OMX_U32 SamplesPerSec;   //采样率
} AudioOMXConfigParserOutputs;

typedef struct
{
OMX_U32 width;    //检测到的视频剪辑宽度
OMX_U32 height;    //检测到的视频剪辑高度
OMX_U32 profile;   //参数
OMX_U32 level;    //级别?
} VideoOMXConfigParserOutputs;

3 动态加载OMX内核
解释了\system\system\etc\pvplayer.cfg文件中最后一行的含义
(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f),"libomx_sharedlibrary.so"

作用:将OMX内核动态加载进OpenCORE框架
位置:\system\system\etc\pvplayer.cfg
形式:(OMX Core API OsclUuid), “shared library name.so”
eg:(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f),"libomx_core_vendorXYZ.so"

注意:(0xa054369c,0x22c5,0x412e,0x19,0x17,0x87,0x4c,0x1a,0x19,0xd4,0x5f) 提供独立API ID,不可修改


数据格式及OMX输入缓冲细节

1.1 帧起始代码
一般不用,H.264可能使用。

1.2 OMX缓冲区
三个值得信赖的关键参数
nFilledLen 缓冲区长度
nTimestamp 缓冲区时间戳
OMX_BUFFERLAG_ENDOFFRAME 缓冲区结束标志位

1.3多帧合并输入缓冲
一些音频信息,单帧过小(eg ARM),将其合并作为一个缓冲区处理。
nFilledLen为所有帧总长度,nTimestamp指向缓冲区第一帧时间。

1.4部分帧
视频解码单帧过大情况下,可能将单帧拆分后传递给缓冲区。
部分帧情况下,只有最后一帧的缓冲区才拥有OMX_BUFFERLAG_ENDOFFRAME。
部分帧缓冲区不会包含两帧信息。
流媒体可能包含多帧。
部分帧的nTimestamp应当相同。

总结:OMX输出缓冲区可能包含
——完整多帧
——完整单帧
——部分帧

1.5 错误的数据封装
多帧的部分帧封装 eg wrong(Frame1+Frame2 part)

1.6 Codec配置数据
Codec配置缓冲区使用OMX_BUFFERLAG_ENDOFFRAME 和OMX_BUFFERFLAG_CODECCONFIG标志位。
H.264的SPS和PPS使用独立的OMX输入缓冲区。

2 H264/AVC 解码器格式
Codec配置头部:
SPS和PPS NAL单元位于起始的OMX输入缓冲区。
SPS和PPS NALs使用独立的OMX输入缓冲区,并使用OMX_BUFFERLAG_ENDOFFRAME 和OMX_BUFFERFLAG_CODECCONFIG标记。

2.1 AVC NAL模式与AVC Frame模式
通过设置iOMXComponentUsesFullAVCFrame标志位,可以决定AVC数据使用哪种模式解码。
默认使用NAL模式,此种模式下OpenCORE框架同时提供完整单帧和部分帧输入缓冲区。
在Frame模式下,OpenCORE框架积累NALs并提供完整单帧给输入缓冲区。
OMX_OTHER_EXTRADATA结构体用来区分NAL边界。
如果iOMXComponentUsesFullAVCFrame和iOMXComponentUsesNALStratCodes都被置为OMX_TRUE,
NAL边界可被start codes区分,此时OMX_OTHER_EXTRADATA无用。

数据结构——NAL模式:
输入缓冲区包含一个或多个NAL,但只包含同一帧的NAL,一帧最后一个NAL才含有OMX_BUFFERLAG_ENDOFFRAME标志位。

数据结构——Frame模式:
每个输入缓冲区包含完整帧。
如果使用NAL start codes,可通过读取NAL start codes区分NAL边界。
否则使用OMX_OTHER_EXTRADATA结构体区分NAL边界。
在Frame模式中,每个缓冲区都含有OMX_BUFFERLAG_ENDOFFRAME标志位。
在Frame模式中,每个缓冲区都含有位于OMX_BUFFERLAGHEADERTYPE结构体nFlags区域的OMX_BUFFERLAG_EXTRADATA标志位。

缓冲区最后包含AVC frame,追加以下数据:
OMX_OTHER_EXTRADATATYPE extra;
OMX_OTHER_EXTRADATATYPE terminator;

extra.eType = OMX_ExtraDataNALSizeArray;
extra.nSize = 20+4*(number of NALs in the frame); // 20 is the size of
OMX_OTHER_EXTRADATATYPE structure + 4 bytes per NAL size
extra.nDataSize = 4 * (number of NALs in the frame)
extra.data[4*i] = size of the i-th NAL (data is declared as byte array – so offset is 4*i, since 4 bytes
is assigned to signal the size of each NAL unit)
terminator.eType = OMX_ExtraDataNone;
terminator.nSize = 20;
terminator.nDataSize = 0;

#define OMX_ExtraDataNALSizeArray 0x7F123321

通过获取OMX_OTHER_EXTRADATA结构体信息,可以得知每一帧包含NAL单元的数目并确定NAL边界。

一个例子:AVC Frame模式,包含2个NAL,包含extra数据结构
总结:
1)每个缓冲区都含有位于OMX_BUFFERLAGHEADERTYPE结构体nFlags区域的OMX_BUFFERLAG_EXTRADATA标志位
2)每个NAL的长度应当使用独立的4byte无符号整型数表示(eg OMX_U32)
3)所有NAL的长度被编码成OMX_U32的数组存放在buffer最后。
4)包含完整帧的缓冲区必须含有位于OMX_BUFFERLAGHEADERTYPE结构体nFlags区域的OMX_BUFFERLAG_ENDOFFRAME标志位。
5)一个独立的缓冲区不包含多帧数据。



================================================================================
android中的 AwesomePlayer就是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码。通过他的组件可以组成一个完整的播放器,包括sourc、demux、decode、output。但是为什么android只用他来做code呢?我认为有以下几方面:


1.在整个播放器中,解码器不得不说是最重要的一部分,而且也是最耗资源的一块。如果全靠软解,直接通过cpu来运算,特别是高清视频。别的事你就可以啥都不干了。所以解码器是最需要硬件提供加速的部分。现在的高清解码芯片都是主芯片+DSP结构,解码的工作都是通过DSP来做,不会在过多的占用主芯片。所有将芯片中DSP硬件编解码的能力通过openmax标准接口呈现出来,提供上层播放器来用。我认为这块是openmax最重要的意义。

2.source 主要是和协议打交道,demux 分解容器部分,大多数的容器格式的分解是不需要通过硬件来支持。只是ts流这种格式最可能用到硬件的支持。因为ts格式比较特殊,单包的大小太小了,只有188字节。所以也是为什么现在常见的解码芯片都会提供硬件ts demux 的支持。

3.音视频输出部分video\audio output 这块和操作系统关系十分紧密。可以看看著名开源播放器vlc。vlc 在mac、linux、Windows都有,功能上差别也不大。所以说他是跨平台的,他跨平台跨在哪?主要的工作量还是在音视频解码完之后的输出模块。因为各个系统的图像渲染和音频输出实现方法不同,所以vlc需要针对每个平台实现不同的output。这部分内容放在openmax来显然不合适。

所以openmax 中硬件抽象的编解码是最为常用的,也是为什么android中只用它来抽象code。

2.android中openmax实现框架



1.上面已经说过了,android系统中只用openmax来做code,所以android向上抽象了一层OMXCodec,提供给上层播放器用。
播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。

2.OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中 实现。

3. OMX把软编解码和硬件编解码统一看作插件的形式管理起来。
 
代码分析:

AwesomePlayer 中有个变量 

  1. OMXClient mClient;  
  1. OMXClient mClient;  
让我们看看   OMXClient 
  1. class OMXClient {  
  2. public:  
  3.     OMXClient();  
  4.   
  5.     status_t connect();  
  6.     void disconnect();  
  7.   
  8.     sp<IOMX> interface() {  
  9.         return mOMX;  
  10.     }  
  11.   
  12. private:  
  13.     sp<IOMX> mOMX;  
  14.   
  15.     OMXClient(const OMXClient &);  
  16.     OMXClient &operator=(const OMXClient &);  
  17. };  
  1. class OMXClient {  
  2. public:  
  3.     OMXClient();  
  4.   
  5.     status_t connect();  
  6.     void disconnect();  
  7.   
  8.     sp<IOMX> interface() {  
  9.         return mOMX;  
  10.     }  
  11.   
  12. private:  
  13.     sp<IOMX> mOMX;  
  14.   
  15.     OMXClient(const OMXClient &);  
  16.     OMXClient &operator=(const OMXClient &);  
  17. };  
OMXClient 有个IOMX 的变量 mOMX ,这个就是和OMX服务进行binder通讯的。
在 AwesomePlayer 的构造函数中会调用 
  1. CHECK_EQ(mClient.connect(), (status_t)OK);  
  1. CHECK_EQ(mClient.connect(), (status_t)OK);  
  1. status_t OMXClient::connect() {  
  2.     sp<IServiceManager> sm = defaultServiceManager();  
  3.     sp<IBinder> binder = sm->getService(String16("media.player"));  
  4.     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);  
  5.   
  6.     CHECK(service.get() != NULL);  
  7.   
  8.     mOMX = service->getOMX();  
  9.     CHECK(mOMX.get() != NULL);  
  10.   
  11.     if (!mOMX->livesLocally(NULL /* node */, getpid())) {  
  12.         ALOGI("Using client-side OMX mux.");  
  13.         mOMX = new MuxOMX(mOMX);  
  14.     }  
  15.   
  16.     return OK;  
  17. }  
  1. status_t OMXClient::connect() {  
  2.     sp<IServiceManager> sm = defaultServiceManager();  
  3.     sp<IBinder> binder = sm->getService(String16("media.player"));  
  4.     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);  
  5.   
  6.     CHECK(service.get() != NULL);  
  7.   
  8.     mOMX = service->getOMX();  
  9.     CHECK(mOMX.get() != NULL);  
  10.   
  11.     if (!mOMX->livesLocally(NULL /* node */, getpid())) {  
  12.         ALOGI("Using client-side OMX mux.");  
  13.         mOMX = new MuxOMX(mOMX);  
  14.     }  
  15.   
  16.     return OK;  
  17. }  
  1. sp<IOMX> MediaPlayerService::getOMX() {  
  2.     Mutex::Autolock autoLock(mLock);  
  3.   
  4.     if (mOMX.get() == NULL) {  
  5.         mOMX = new OMX;  
  6.     }  
  7.   
  8.     return mOMX;  
  9. }  
  1. sp<IOMX> MediaPlayerService::getOMX() {  
  2.     Mutex::Autolock autoLock(mLock);  
  3.   
  4.     if (mOMX.get() == NULL) {  
  5.         mOMX = new OMX;  
  6.     }  
  7.   
  8.     return mOMX;  
  9. }  

OMXClient::connect函数是通过binder机制 获得到MediaPlayerService,然后通过MediaPlayerService来创建OMX的实例。这样OMXClient就获得到了OMX的入口,接下来就可以通过binder机制来获得OMX提供的服务。
也就是说OMXClient 是android中 openmax 的入口。

在创建音视频解码mVideoSource、mAudioSource的时候会把OMXClient中的sp<IOMX> mOMX的实例 传给mVideoSource、mAudioSource来共享使用这个OMX的入口。
也就是说一个AwesomePlayer对应着 一个IOMX 变量,AwesomePlayer中的音视频解码器共用这个IOMX变量来获得OMX服务。
  1. sp<IOMX> interface() {  
  2.       return mOMX;  
  3.   }  
  1. sp<IOMX> interface() {  
  2.       return mOMX;  
  3.   }  
  1. mAudioSource = OMXCodec::Create(  
  2.                 mClient.interface(), mAudioTrack->getFormat(),  
  3.                 false// createEncoder   
  4.                 mAudioTrack);  
  1. mAudioSource = OMXCodec::Create(  
  2.                 mClient.interface(), mAudioTrack->getFormat(),  
  3.                 false// createEncoder  
  4.                 mAudioTrack);  
  1. mVideoSource = OMXCodec::Create(  
  2.             mClient.interface(), mVideoTrack->getFormat(),  
  3.             false// createEncoder   
  4.             mVideoTrack,  
  5.             NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);  
  1. mVideoSource = OMXCodec::Create(  
  2.             mClient.interface(), mVideoTrack->getFormat(),  
  3.             false// createEncoder  
  4.             mVideoTrack,  
  5.             NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);  
  1.   
  1.