这一节,我们将重点剖析OMXNodeInstance的useBuffer和allocateSecureBuffer方法。这些内容将会与前面两篇文章紧密相关,不过如果暂时阅读起来有困难,也没有关系,等后续ACodec相关内容学完后,再回过头来看应该就能更容易理解了。
ACodec通过调用OMXNodeInstance的useBuffer方法,指定OMX组件端口使用客户端分配的buffer。哪些类型的buffer需要由客户端分配呢?
在解码流程中,PresetByteBuffer、PresetANWBuffer和DynamicANWBuffer这些类型的buffer都是由客户端分配的,只有PresetSecureBuffer需要由OMX组件来分配。在编码流程中,所有的输入和输出buffer都应由客户端来分配。总的来说,只有PresetSecureBuffer是由组件分配,其他所有的buffer都是在客户端分配,再共享给组件使用的。
先来看IOmxNode定义的useBuffer接口,调用useBuffer需要传入一个CodecBuffer对象,结束调用后会回传一个uint32_t类型的数据:
useBuffer(
uint32_t portIndex,
CodecBuffer omxBuffer
) generates (
Status status,
BufferId buffer
);
对比OMXNodeInstance的useBuffer方法,会发现传入参数是OMXBuffer类型的对象,CodecBuffer和OMXBuffer之间有什么关系呢?为什么要使用OMXBuffer呢?
status_t OMXNodeInstance::useBuffer(
OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer);
由于客户端可能会分配出多种类型的buffer,它们的格式不同,携带的信息也不同,为了能够只用一个函数传递这些buffer,需要将它们以统一的格式进行封装,这就是CodecBuffer的作用。
struct CodecBuffer {
enum Type : int32_t {
INVALID = 0,
PRESET,
SHARED_MEM,
ANW_BUFFER,
NATIVE_HANDLE,
};
struct PresetAttributes {
uint32_t rangeOffset;
uint32_t rangeLength;
};
union Attributes {
PresetAttributes preset;
AnwBufferAttributes anwBuffer;
};
Type type;
Attributes attr;
handle nativeHandle;
memory sharedMemory;
};
CodecBuffer的定义如上,type字段用于记录被传递buffer的类型,attr字段用于记录buffer相关的信息,nativeHandle用于传递句柄,sharedMemory用于传递共享内存。
接下来的问题是,如何将不同类型的buffer封装为CodecBuffer呢?这个问题肯定难不倒大家,最简单的方式就是实现多个重载函数,根据不同的buffer类型实现不同的封装方式。Android选择将不同buffer封装为OMXBuffer,再实现一个方法将OMXBuffer转换为CodecBuffer。
先来看OMXBuffer内部定义的枚举BufferType:
enum BufferType {
kBufferTypeInvalid = 0,
kBufferTypePreset,
/* kBufferTypeSharedMem, not use */
kBufferTypeANWBuffer,
kBufferTypeNativeHandle,
kBufferTypeHidlMemory
};
可能有人会疑惑,OMXBuffer::BufferType和之前学的PortMode指示的buffer类型有什么区别呢?
PortMode是在configure期间由ACodec确定的,是比较具体的buffer信息,它指示了编解码所需用的buffer类型和分配方式。
OMXBuffer::BufferType是比较宽泛的类型,只在useBuffer、fillBuffer以及emptyBuffer中使用,用于封装传递给OMXNodeInstance的buffer。
OMXBuffer的定义如下:
OMXBuffer OMXBuffer::sPreset(static_cast<sp<MediaCodecBuffer> >(NULL));
OMXBuffer::OMXBuffer(const sp<MediaCodecBuffer>& codecBuffer)
: mBufferType(kBufferTypePreset),
mRangeOffset(codecBuffer != NULL ? codecBuffer->offset() : 0),
mRangeLength(codecBuffer != NULL ? codecBuffer->size() : 0) {
}
/* not use
OMXBuffer::OMXBuffer(const sp<IMemory> &mem)
: mBufferType(kBufferTypeSharedMem),
mMem(mem) {
}
*/
OMXBuffer::OMXBuffer(const sp<GraphicBuffer> &gbuf)
: mBufferType(kBufferTypeANWBuffer),
mGraphicBuffer(gbuf) {
}
OMXBuffer::OMXBuffer(const sp<NativeHandle> &handle)
: mBufferType(kBufferTypeNativeHandle),
mNativeHandle(handle) {
}
OMXBuffer::OMXBuffer(const hidl_memory &hidlMemory)
: mBufferType(kBufferTypeHidlMemory),
mHidlMemory(hidlMemory) {
}
OMXBuffer重载了多个构造函数,传入参数有以下几种:
看到这又有一个疑问,OMXBuffer::BufferType要如何与PortMode相对应起来呢?是不是kBufferTypePreset对应于kPortModePresetByteBuffer、kPortModePresetANWBuffer、kPortModePresetSecureBuffer呢?
答案是否定的,OMXBuffer::BufferType和PortMode没有什么对应关系。
OMXBuffer和CodecBuffer之间的转换代码位于LWOmxNode::useBuffer和TWOmxNode::useBuffer函数中,有兴趣的同学可以自行阅读。
status_t OMXNodeInstance::useBuffer(
OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {
// ...
switch (omxBuffer.mBufferType) {
case OMXBuffer::kBufferTypePreset: {
if (mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
break;
}
return useBuffer_l(portIndex, NULL, NULL, buffer);
}
// ...
case OMXBuffer::kBufferTypeANWBuffer: {
if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
break;
}
return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
}
case OMXBuffer::kBufferTypeHidlMemory: {
if (mPortMode[portIndex] != IOMX::kPortModePresetByteBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer
&& mPortMode[portIndex] != IOMX::kPortModeDynamicNativeHandle) {
break;
}
sp<IHidlMemory> hidlMemory = mapMemory(omxBuffer.mHidlMemory);
if (hidlMemory == nullptr) {
return NO_MEMORY;
}
return useBuffer_l(portIndex, NULL, hidlMemory, buffer);
}
default:
return BAD_VALUE;
break;
}
return INVALID_OPERATION;
}
useBuffer函数根据不同的BufferType调用了不同的内部方法来与OMX组件构建buffer连接。
在useBuffer中,我们可以看到会检查BufferType与当前的PortMode设定是否匹配,具体什么情况会传什么BufferType需要阅读ACodec才能知道,这里先讲结论:
useBuffer_l的代码比较长且逻辑稍微复杂,是因为Android将对kBufferTypePreset和kBufferTypeHidlMemory的处理放在一起了。为了看得更清楚,将对不同BufferType的处理分开,先来看对kBufferTypeHidlMemory的处理: