学习开源代码最快的方式是先阅读它的文档,再查看它的头文件,最后研读代码实现并进行编译调试。Android早期引入OpenMAX IL作为使用音视频编解码器的标准接口,了解Android Media框架的底层运行原理要从OMX IL开始。在这一节,我们将阅读整理OpenMAX IL Spec中的介绍和架构部分,以便对整个框架有一个初步的概念。
阅读材料 openmax_il_spec_1_0.pdf 可在khronos官网下载,也可在公众号后台回复ILSpec获取下载链接。
OpenMAX Integration Layer(OMX IL,集成层)是由Khronos Group开发的一套低层级标准接口,旨在为编解码器提供一定程度的抽象,使得嵌入式或移动设备能够统一调用音频、视频和图像编解码器,从而实现编解码器实现代码和调用代码的跨平台性。
OMX IL API由两大主要部分组成,分别是Core API和Component API。
将OMX IL API封装并向上层提供高层级接口的部分被称为IL Client(客户端),IL Client使用OMX Core来加载组件,卸载组件,调用组件的方法。
为什么Android要引入OMX IL?
OMX IL为组件定义了一些状态,组件使用过程中会经历一系列的状态转换。OMX IL定义的状态有Unloaded、Loaded、Idle、Executing、Paused、WaitForResources、Invalid,但Android中实际用到的只有Loaded、Idle、Executing、Invalid,后续的文章中我们只重点了解Android使用的状态。
spec中有一个OMX IL组件的架构图,看懂它大致就能了解一个OMX组件应该如何实现了,这里对架构图做简单描述:
这一节对port有更多的描述,port存储有组件定义的要用的buffer的最小数量,buffer可能是由OMX组件自己分配 ,也有可能是使用预先分配 的。port中的每一个buffer都会关联到一个Buffer Header,Buffer Header除了指向缓冲区外还存储有与缓冲区关联的metadata(元数据)。
这里简单了解下Tunnel Mode(隧道模式),Tunnel Mode指的是将两个组件的端口直接连接,减少中间层的使用,从而减少数据的中间传递过程,提高运行效率。
举个例子我们有一个OMX解码组件,普通的输出流程是解码器将解码后的数据写到output buffer中,应用层拿到output buffer之后做Avsync,最后送给surface渲染,这里有将数据传到上层,上层做Avsync、执行渲染三个动作。
在OMX IL中每个数据处理模块都可以看作为组件,因此surface作为数据消耗者可以看作为一个sink组件。将解码组件的输出端口与sink组件的输入端口相连接,就形成了一个隧道,解码器的输出可以直接送到渲染模块,再通过硬件同步,减少了应用层接收数据和Avsync两个动作,减少了CPU消耗,提高了运行效率。
Tunnel Mode中的缓冲区分配与共享指的是隧道两端的输出端口和输入端口共用相同的缓冲区,这样做可以避免数据拷贝。buffer需要由其中一个组件分配并提供给另一个组件使用,提供buffer的组件/模块被称为supplier,提供buffer的端口被称为supplier port,接受缓冲区的端口被称为non-supplier port。
spec中用大量的篇幅描述Tunnel Mode的使用,可见是比较推荐使用这种方式,具体Tunnel Mode如何实现我们在后续文章再做了解。
组件可以向IL Client发送六种类型的事件,Android中常用的有以下三种: