这一节,我们将重点剖析OMXNodeInstance的useBuffer和allocateSecureBuffer方法。
1、OMXBuffer
ACodec通过调用OMXNodeInstance的useBuffer方法,指定OMX组件端口使用客户端分配的buffer。哪些类型的buffer属于客户端分配的buffer呢?
在解码流程中,PresetByteBuffer、PresetANWBuffer和DynamicANWBuffer这些类型的buffer都是由客户端分配的,而PresetSecureBuffer则需要由OMX组件来分配。而在编码流程中,所有的输入和输出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的类型,用Attributes记录buffer相关的信息,如果传递的是nativeHandle则用handle记录,如果传递的是hidlmemory则用memory来记录。接下来的问题是,如何将这些buffer封装为CodecBuffer呢?这个问题肯定难不倒大家,最简单的方式就是实现多个重载函数,根据传入的不同类型的buffer实现不同的封装方式。Android的实现方式多走了一点弯路,选择将不同buffer封装为OMXBuffer,再实现一个方法将OMXBuffer转换为CodecBuffer。选择将不同buffer封装为OMXBuffer的方法也是使用重载的方法,只不过是重载的构造函数。接下来就来看看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内部定义了一个枚举BufferType,用于表示buffer类型。
enum BufferType {
kBufferTypeInvalid = 0,
kBufferTypePreset,
/* kBufferTypeSharedMem, not use */
kBufferTypeANWBuffer,
kBufferTypeNativeHandle,
kBufferTypeHidlMemory
};
可能有读者会疑惑,这里的BufferType和我们之前学的PortMode所指示的buffer类型有什么区别呢?
PortMode是在configure期间由ACodec确定的,比较具体的buffer信息,它指示了编解码所需用的buffer类型和分配方式。
OMXBuffer中的BufferType是一个比较宽泛的类型,比如说kPortModePresetANWBuffer和kPortModeDynamicANWBuffer都是属于OMXBuffer::kBufferTypeANWBuffer。调用useBuffer时会根据此BufferType来决定如何与组件建立buffer连接。其实笔者认为PortMode和BufferType有点重复了,只用PortMode即可。
不过既然写了,我们就来看看吧。
值得注意的是OMXBuffer::kBufferTypePreset是一个特殊的枚举值,它并不直接指明buffer的具体类型。在之前的讨论中,我们知道preset buffer的分配是OMX组件在OMX_StateLoadToIdle状态下完成的,而dynamic buffer则是在组件在OMX_StateExecuting状态时动态分配的。这意味着在OMX_StateLoadToIdle状态下,dynamic buffer实际上是不存在的。
为了保持客户端与组件之间调用的一致性,对使用dynamic buffer的端口调用useBuffer时传入一个空的buffer,先建立起客户端与组件之间buffer的连接,当真正的dynamic buffer分配完成后,再更新BufferHeader指向的内容即可。
为了让数据能够跨进程传输,需要使用HidlMemory,对应的BufferType是kBufferTypeHidlMemory。
OMXBuffer和CodecBuffer之间是如何互相转换的,这里就不再做具体解释,有兴趣的同学可以阅读LWOmxNode::useBuffer和TWOmxNode::useBuffer。
2、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;
}
根据不同的BufferType调用了不同方法来与OMX组件构建buffer连接,分别是useBuffer_l和useGraphicBuffer_l,接下来我们一一解析。
useBuffer_l
useBuffer_l的代码比较长且逻辑稍微复杂,是因为Android将对kBufferTypePreset和kBufferTypeHidlMemory的处理放在一起了。
为了看得更清楚,我们将这部分代码分成两半,先来看对kBufferTypeHidlMemory的处理:
status_t OMXNodeInstance::useBuffer_l(
OMX_U32 portIndex, const sp<IMemory> ¶ms,
const sp<IHidlMemory> &hParams, IOMX::buffer_id *buffer) {
BufferMeta *buffer_meta;
OMX_BUFFERHEADERTYPE *header;
OMX_ERRORTYPE err = OMX_ErrorNone;
bool isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;
// isMetadata = false,mMetadataType[in/out] = kMetadataBufferTypeInvalid
if (!isMetadata && mGraphicBufferEnabled[portIndex]) {
return INVALID_OPERATION;
}
size_t paramsSize;
void* paramsPointer;
if (hParams != NULL) {
paramsPointer = hParams->getPointer();
paramsSize = hParams->getSize();
}
bool isOutputGraphicMetadata = (portIndex == kPortIndexOutput) &&
(mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource ||
mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer);
// isOutputGraphicMetadata = false
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
? kRequiresAllocateBufferOnInputPorts
: kRequiresAllocateBufferOnOutputPorts;
// 检查Quirks
if (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit)) {
buffer_meta = new BufferMeta(
params, hParams, portIndex, !isMetadata /* copy */, NULL /* data */);
err = OMX_AllocateBuffer(
mHandle, &header, portIndex, buffer_meta, allottedSize);
} else {
OMX_U8 *data = NULL;
data = static_cast<OMX_U8 *>(paramsPointer);
buffer_meta = new BufferMeta(
params, hParams, portIndex, false /* copy */, NULL);
err = OMX_UseBuffer(
mHandle, &header, portIndex, buffer_meta,
allottedSize, data);
}
CHECK_EQ(header->pAppPrivate, buffer_meta);
*buffer = makeBufferID(header);
addActiveBuffer(portIndex, *buffer);
}
3、allocateSecureBuffer
关注公众号《青山渺渺》阅读全文