在这篇文章中,我们将一起学习编解码过程中的两个数据驱动函数:emptyBuffer和fillBuffer,Android Media Framework(十)OMXNodeInstance - Ⅲ 是本文的基础,所以要仔细阅读上一篇文章哦。
1、emptyBuffer
客户端将数据填充到input buffer后会调用OMXNodeInstance的emptyBuffer方法,OMXNodeInstance根据不同的BufferType,对传递下来的不同信息进行封装,再统一调用OMX_EmptyThisBuffer通知到OMX组件消耗/清空input buffer中的数据。
emptyBuffer函数如下,首先我们要注意进入函数后就加了锁,既然有锁就说明有多个线程在使用OMXNodeInstance对象,有哪些线程呢?一个是ACodec中的消息处理线程,这部分内容我们将在ACodec相关章节做讲解;另一个是OMXNodeInstance中的CallbackDispatcherThread,用于处理组件的回调,这部分内容将在下一篇文章做讲解。
status_t OMXNodeInstance::emptyBuffer(
buffer_id buffer, const OMXBuffer &omxBuffer,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Mutex::Autolock autoLock(mLock);
if (mHandle == NULL) {
return DEAD_OBJECT;
}
switch (omxBuffer.mBufferType) {
case OMXBuffer::kBufferTypePreset:
return emptyBuffer_l(
buffer, omxBuffer.mRangeOffset, omxBuffer.mRangeLength,
flags, timestamp, fenceFd);
case OMXBuffer::kBufferTypeANWBuffer:
return emptyGraphicBuffer_l(
buffer, omxBuffer.mGraphicBuffer, flags, timestamp, fenceFd);
case OMXBuffer::kBufferTypeNativeHandle:
return emptyNativeHandleBuffer_l(
buffer, omxBuffer.mNativeHandle, flags, timestamp, fenceFd);
default:
break;
}
return BAD_VALUE;
}
我们在switch语句中看到,Input BufferType可能有三种,分别为kBufferTypePreset、kBufferTypeANWBuffer和kBufferTypeNativeHandle。再回忆下,三种BufferType分别对应哪些具体的Buffer类型:
kBufferTypePreset:
- kPortModePresetByteBuffer
- kPortModePresetSecureBuffer
kBufferTypeANWBuffer:
- kPortModeDynamicANWBuffer
kBufferTypeNativeHandle
- kPortModeDynamicNativeHandle
要注意的是,在这里kPortModePresetANWBuffer不能算到kBufferTypePreset中,因为它只能用在输出端口。
PresetBuffer表示所有使用的buffer都是预置的,buffer地址在整个数据处理阶段不会发生改变,因此数据传输只需要传递起始偏移量、数据长度、时间戳、flag信息即可。为什么不要传递fenceFd呢?fence只有ANWBuffer要用,有兴趣的同学可以阅读Graphics相关文章。
对于kPortModeDynamicANWBuffer和kPortModeDynamicNativeHandle而言,我们之前已经讨论过,带有Dynamic前缀的buffer需要在数据处理阶段动态地确定其地址或handle。在之前的文章中我们看到,当对使用Metadata的端口调用useBuffer时,BUFFERHEADER的pBuffer成员会指向一个大小为sizeof(VideoNativeMetadata/VideoNativeHandleMetadata)、内容初始化为空的buffer。随后,在上层调用emptyBuffer时,会将相关的buffer信息更新到BufferMeta中,并将这些信息填充到pBuffer所指向的buffer中,这样组件就能获取到真实的buffer地址或handle了。
理解以上两点内容,接下来的函数解析就会很简单了。
2、emptyBuffer_l
status_t OMXNodeInstance::emptyBuffer_l(
IOMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
BufferMeta *buffer_meta =
static_cast<BufferMeta *>(header->pAppPrivate);
header->nFilledLen = rangeLength;
header->nOffset = rangeOffset;
buffer_meta->CopyToOMX(header);
return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd);
}
简化后的代码很简单,首先根据buffer id找到input port中对应的BufferHeader,自然而然拿到了BufferMeta。接下来需要调用CopyToOMX将上层的数据写到OMX中,这里真的要做数据拷贝吗?
答案是不一定。从CopyToOMX的实现中我们可以看出,当mCopyToOmx为false不需要做数据拷贝。问题变成mCopyToOmx什么时候为false呢?这在前面一篇文章中已经做了解释,只有在Quirk下才会将mCopyToOmx置为true。至于Quirk会在何时启用请再仔细阅读上一篇文章。
void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
if (!mCopyToOmx) {
return;
}
memcpy(header->pBuffer + header->nOffset,
getPointer() + header->nOffset,
header->nFilledLen);
}
OMX_U8 *getPointer() {
return mMem.get() ? static_cast<OMX_U8*>(mMem->unsecurePointer()) :
mHidlMemory.get() ? static_cast<OMX_U8*>(
static_cast<void*>(mHidlMemory->getPointer())) : nullptr;
}
mCopyToOmx为true时数据是怎么做拷贝的呢?虽然CopyToOMX函数只传入了BufferHeader,但是BufferMeta中存储了Hidlmemory的地址,用getPointer拿到地址就能够进行数据拷贝了。
3、emptyGraphicBuffer_l
status_t OMXNodeInstance::emptyGraphicBuffer_l(
IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
status_t err = updateGraphicBufferInMeta_l(
kPortIndexInput, graphicBuffer, buffer, header);
int64_t codecTimeUs = getCodecTimestamp(timestamp);
header->nOffset = 0;
if (graphicBuffer == NULL) {
header->nFilledLen = 0;
} else if (mMetadataType[kPortIndexInput] == kMetadataBufferTypeGrallocSource) {
header->nFilledLen = sizeof(VideoGrallocMetadata);
} else {
header->nFilledLen = sizeof(VideoNativeMetadata);
}
return emptyBuffer_l(header, flags, codecTimeUs, (intptr_t)header->pBuffer, fenceFd);
}
4、emptyNativeHandleBuffer_l
5、emptyBuffer_l
6、fillBuffer
关注公众号《青山渺渺》阅读全文