OpenMAX框架的学习有两大难点,一是组件的状态切换与buffer的流转过程,这部分内容我们已经在IL Spec中学习过了;二是OMX组件使用的buffer类型与buffer分配过程,这一节我们来重点剖析OMX组件使用的buffer类型,后续的文章将以本章内容为基础。
ps:OMXNodeInstance相关的几篇内容前前后后改了有好几次了,希望大家能轻松阅读。
在实际应用中,OMX组件可能需要能够处理多种类型的输入数据。对于编码组件而言,其输入可能包括未压缩的视频流,例如来自设备屏幕的录屏数据或相机录制的原始视频。而对于解码组件,它可能需要处理普通的压缩视频文件,或者是加密的视频流。为了存储不同类型的输入数据,Android定义了不同类型的buffer。待处理的数据以不同的形式存储在这些缓冲区中,因此OMX组件需要明确即将处理的输入缓冲区类型,从而能够正确地解析buffer中的数据。
同样的,OMX组件输出结果时,会把数据填充到不同类型的buffer中,以适应不同的用途。对于编码组件,编码后的数据通常会被填充到一块普通的buffer中,应用获取到buffer后,会读取其中的数据并进行封装;对于解码组件,解码后的数据通常会被发送到surface进行渲染,因此数据需要填充到native window buffer中。为了让组件能够正确地将数据填充到buffer中,我们需要提前配置好需要使用的输出缓冲区类型,从而能够让组件正确地向buffer填充数据。
接下来会有两部分内容需要讨论:
Android上有哪些可用的input buffer和output buffer类型?
这些buffer的格式是怎样的?
在了解Buffer Types之前,我们先简单说明一下OMXNodeInstance的作用。在之前的学习中,我们提到OMXNodeInstance在框架中扮演的角色是IL Client,它封装了OMX Core提供的低层级接口,并进行了新的抽象。然而,在阅读源码的过程中我们会发现,OMXNodeInstance的接口仍然暴露了过多的细节,使用起来相对困难。因此,Android在OMXNodeInstance之上又进行了封装,这一层就是我们后续要学习的ACodec。查看ACodec对外开放的接口,已经完全没有了OMX框架的身影,所以ACodec的接口算是比较高层级的接口。因为ACodec封装了OMXNodeInstance的调用,所以笔者认为它也算得上是IL Client的一部分。
接下来让我们正式进入主题。
Android并没有直接定义具体的buffer类型,而是通过OMXNodeInstance抽象出的PortMode(端口模式)来指明组件端口所使用的buffer类型及其分配方式。相应地,为组件配置使用的buffer类型的方法就被命名为setPortMode。
PortMode是一个枚举,定义在IOMX.h中:
enum PortMode {
kPortModePresetStart = 0,
kPortModePresetByteBuffer,
kPortModePresetANWBuffer,
kPortModePresetSecureBuffer,
kPortModePresetEnd,
kPortModeDynamicStart = 100,
kPortModeDynamicANWBuffer,
kPortModeDynamicNativeHandle,
kPortModeDynamicEnd,
};
很多读者在看到PortMode时可能会心生疑惑:PortMode是如何体现buffer类型与分配方式的呢?
我们将PortMode中的枚举名拆成三部分:kPortMode(前缀,忽略),Preset/Dynamic(分配方式),buffer类型(后缀)。
先来了解buffer类型(也就是后缀部分)中的关键词:
Byte:字节。用于表示一块最普通、最常见的buffer,我们可以以字节的形式来访问和读写buffer中的内容;
Secure:安全,受保护的。用于表示Buffer是受保护的,我们不能够以普通的方法来访问或读写buffer中的内容,一般来说平台会提供特定的API实现buffer的读写操作;