转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼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干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。
如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易