当前位置: 首页 > news >正文

Android 13 - Media框架(26)- OMXNodeInstance(三)

上一节我们了解了OMXNodeInstance中的端口定义,这一节我们一起来学习ACodec、OMXNode、OMX 组件使用的 buffer 到底是怎么分配出来的,以及如何关联起来的。(我们只会去了解 graphic buffer的创建、input bytebuffer的创建、secure buffer的创建)

1、ACodec::allocateOutputMetadataBuffers

我们先一起来回忆一下,ACodec在使用surface的情况下给ouput port分配buffer使用的是allocateOutputMetadataBuffers方法,这时候真正的graphic buffer还未分配出来,BufferInfo的状态还是OWNED_BY_NATIVE_WINDOW(意为 buffer 还在 native 中)。

    for (OMX_U32 i = 0; i < bufferCount; i++) {BufferInfo info;info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;info.mFenceFd = -1;info.mRenderInfo = NULL;info.mGraphicBuffer = NULL;info.mNewGraphicBuffer = false;info.mDequeuedAt = mDequeueCounter;// 创建 meta datainfo.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));// Initialize fence fd to -1 to avoid warning in freeBuffer().((VideoNativeMetadata *)info.mData->base())->nFenceFd = -1;info.mCodecData = info.mData;err = mOMXNode->useBuffer(kPortIndexOutput, OMXBuffer::sPreset, &info.mBufferID);mBuffers[kPortIndexOutput].push(info);ALOGV("[%s] allocated meta buffer with ID %u",mComponentName.c_str(), info.mBufferID);}

这里BufferInfo是作为分配出来的buffer的索引,分配的 mCodecData (Meta data) 没有任何作用,仅仅是起着占位的作用。

ACodec 需要给这个 BufferInfo 打上索引,调用useBuffer时,传入的OMXBuffer类型是kBufferTypePreset,这个内容可以到 OMXBuffer.cpp 中查询。

1.1、useBuffer

进入到 OMXNodeInstance 中,首先就会检查 port mode,我们可以看到,如果port mode不是dynamic的类型,会直接报错。

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);}}

kBufferTypePreset 在这里应该表示的是占位的意思。

我们不要被 useBuffer 的参数 buffer 迷惑了,它其实是一个 int 类型。判断完成后就会进入到 useBuffer_l 中。

1.2、useBuffer_l

进入下面的内容之前我们首先要了解的是,之所以方法名为 use buffer,指的是 OMX 组件端口使用的 buffer 是在其他地方分配的,OMX 组件仅仅只是使用。

useBuffer_l 的设计思路其实和之前的一篇文章中的 enableNativeBuffers_l 类似,它是把几个不同的设定写到一个函数体当中,用入参来判断当前走的是什么设定。useBuffer_l 有两个参数,但是它其实有三种可能的设定,一种是IMemory,另一种是 IHidlMemory,最后一个是两个参数都为 NULL,表示是一个占位

status_t OMXNodeInstance::useBuffer_l(OMX_U32 portIndex, const sp<IMemory> &params,const sp<IHidlMemory> &hParams, IOMX::buffer_id *buffer) {BufferMeta *buffer_meta;OMX_BUFFERHEADERTYPE *header;OMX_ERRORTYPE err = OMX_ErrorNone;// 判断是否使用 meta databool isMetadata = mMetadataType[portIndex] != kMetadataBufferTypeInvalid;// 如果使用graphic buffer但是不用meta data则直接报错if (!isMetadata && mGraphicBufferEnabled[portIndex]) {ALOGE("b/62948670");android_errorWriteLog(0x534e4554, "62948670");return INVALID_OPERATION;}// 检查参数,不能同时设定两个参数size_t paramsSize;void* paramsPointer;if (params != NULL && hParams != NULL) {return BAD_VALUE;}// 解析传递的buffer的指针以及buffer的大小,如果什么都没有传,那么指针为NULLif (params != NULL) {// TODO: Using unsecurePointer() has some associated security pitfalls//       (see declaration for details).//       Either document why it is safe in this case or address the//       issue (e.g. by copying).paramsPointer = params->unsecurePointer();paramsSize = params->size();} else if (hParams != NULL) {paramsPointer = hParams->getPointer();paramsSize = hParams->getSize();} else {paramsPointer = nullptr;}// 使用的buffer的大小OMX_U32 allottedSize;// metadata mode下,设定的是占位符,OMXNode 与 OMX 组件之间通过 metadata交流,metadata buffer由 OMXNode分配// 这里需要根据不同的类型分配不同类型的meta data,确定metadata sizeif (isMetadata) {if (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource) {allottedSize = sizeof(VideoGrallocMetadata);} else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer) {// allottedSize = sizeof(VideoNativeMetadata);} else if (mMetadataType[portIndex] == kMetadataBufferTypeNativeHandleSource) {allottedSize = sizeof(VideoNativeHandleMetadata);} else {return BAD_VALUE;}} else {// NULL 只允许出现在 meta mode 当中// 如果不使用meta data 并且 没有传 buffer下来,那么直接报错// NULL params is allowed only in metadata mode.if (paramsPointer == nullptr) {ALOGE("b/25884056");return BAD_VALUE;}allottedSize = paramsSize;}// 是否是与graphic搭配使用的metadatabool isOutputGraphicMetadata = (portIndex == kPortIndexOutput) &&(mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource ||mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer);// 是否需要分配bufferuint32_t requiresAllocateBufferBit =(portIndex == kPortIndexInput)? kRequiresAllocateBufferOnInputPorts: kRequiresAllocateBufferOnOutputPorts;// quirks 模式下,如果不是使用的 meta data mode,则需要让 OMX 组件分配buffer// we use useBuffer for output metadata regardless of quirksif (!isOutputGraphicMetadata && (mQuirks & requiresAllocateBufferBit)) {// metadata buffers are not connected cross process; only copy if not meta.// quirks 模式下,将 OMX 组件分配的buffer 和 上层 buffer相关绑定,进行数据拷贝// 这应该是很没有效率的一种行为buffer_meta = new BufferMeta(params, hParams, portIndex, !isMetadata /* copy */, NULL /* data */);err = OMX_AllocateBuffer(mHandle, &header, portIndex, buffer_meta, allottedSize);if (err != OMX_ErrorNone) {CLOG_ERROR(allocateBuffer, err,SIMPLE_BUFFER(portIndex, (size_t)allottedSize,paramsPointer));}} else {OMX_U8 *data = NULL;// metadata buffers are not connected cross process// use a backup buffer instead of the actual bufferif (isMetadata) {// 为 meta data buffer 分配空间data = new (std::nothrow) OMX_U8[allottedSize];if (data == NULL) {return NO_MEMORY;}memset(data, 0, allottedSize);// 将 meta data buffer 封装到 BufferMeta 当中buffer_meta = new BufferMeta(params, hParams, portIndex, false /* copy */, data);} else {// 如果不是 meta data mode,直接使用buffer指针data = static_cast<OMX_U8 *>(paramsPointer);// 将 buffer 封装到 BufferMeta 当中buffer_meta = new BufferMeta(params, hParams, portIndex, false /* copy */, NULL);}// 直接把创建的 BufferMeta 传递给 OMX 组件使用,不需要进行buffer拷贝err = OMX_UseBuffer(mHandle, &header, portIndex, buffer_meta,allottedSize, data);if (err != OMX_ErrorNone) {CLOG_ERROR(useBuffer, err, SIMPLE_BUFFER(portIndex, (size_t)allottedSize, data));}}if (err != OMX_ErrorNone) {delete buffer_meta;buffer_meta = NULL;*buffer = 0;return StatusFromOMXError(err);}// 检查创建的buffer header中的上层数据是否和 创建的 buffer meta 相同CHECK_EQ(header->pAppPrivate, buffer_meta);// 为 buffer header 创建 index*buffer = makeBufferID(header);addActiveBuffer(portIndex, *buffer);sp<IOMXBufferSource> bufferSource(getBufferSource());if (bufferSource != NULL && portIndex == kPortIndexInput) {bufferSource->onInputBufferAdded(*buffer);}CLOG_BUFFER(useBuffer, NEW_BUFFER_FMT(*buffer, portIndex, "%u(%zu)@%p", allottedSize, paramsSize, paramsPointer));return OK;
}

useBuffer 的内容很长,里面有很多判断,很多同学可能读不太明白里面到底想干什么,这里我们就来解析一下:

  1. 首先,它会判断configure阶段配置的一些内容, 比如说metadata mode需要和graphic buffer共同使用;IMemory 参数和 IHidlMemory 参数不能同时设定;

  2. 接下来会分为两种情况,

    • metadata mode,也就是我们上面讨论的传下来的buffer类型是kBufferTypePreset的情况(占位),这种情况上层没有真正传buffer下来,OMX组件和上层通过metadata进行数据传递,所以usebuffer中会根据configure的配置获取需要传递的metadata类型,计算metadata所需的size;
    • preset byte buffer,这种就是普通的non secure buffer,可以通过指针读写buffer中的数据,use buffer需要获取buffer的地址以及大小;
  3. 判断是否用quirks,这个东西我们在创建OMXNodeInstance时有看见过,它是在xml中配置的,一旦有了这个配置,那么只要端口不是metadata mode,则需要让OMX组件分配buffer,上层传下来的buffer会与OMX分配的buffer做绑定,创建一个BufferMeta,上层传的数据会在这里做拷贝,写入到OMX组件分配的buffer当中。(这里是调用OMX_AllocateBuffer的地方之一)
    请添加图片描述

  4. 如果不使用quirks,那么数据就不需要在OMXNodeInstance层做中转了。

    • metadata mode,分配meta data大小的buffer,创建出BufferMeta;
    • preset byte buffer,直接用上层传的buffer创建BufferMeta;
    • 调用OMX_UseBuffer,将 BufferMeta 和 metadata/buffer 注册到 OMX 组件当中,并且创建出 OMX_BUFFERHEADERTYPE
  5. 为创建出的 OMX_BUFFERHEADERTYPE 分配 id;

到这里,useBuffer_l 的分析就完成了,之所以要在 OMXNodeInstance 这一层创建 BufferMeta,可能就是为了记录上层传下来的所有buffer,或者是做数据中转用的。

quirks模式会在 OMXNodeInstance 层多做一次数据拷贝,现在已经被弃用了。

再放上两张示意图:

一张是 preset byte buffer:
请添加图片描述

另一张是meta data,可以看到 meta data 模式下,output buffer 不会有任何东西回传给上层。
请添加图片描述
OMXNodeInstance 收到返回的 OMX_BUFFERHEADERTYPE 后会为其分配 id:

IOMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {if (bufferHeader == NULL) {return 0;}Mutex::Autolock autoLock(mBufferIDLock);IOMX::buffer_id buffer;do { // handle the very unlikely case of ID overflowif (++mBufferIDCount == 0) {++mBufferIDCount;}buffer = (IOMX::buffer_id)mBufferIDCount;} while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0);mBufferIDToBufferHeader.add(buffer, bufferHeader);mBufferHeaderToBufferID.add(bufferHeader, buffer);return buffer;
}

可以看到一个 OMXNodeInstance 的buffer id用一个mBufferIDCount 成员来管理,所以input buffer和ouput buffer id 是连续的。

makeBufferID 还为 id 和 bufferheader 建立起一个映射,便于快速完成查找。

1.2、allocateSecureBuffer

allocate buffer 是让 OMX 组件分配需要使用的 buffer,在ACodec中,只有在使用 secure buffer时,会让omx组件分配buffer handle。secure buffer是一块受保护的buffer,我们无法读取到内部的资料,内存拷贝需要通过操作 handle 来完成,这里我们要看 handle 是如何创建的:

                BufferInfo info;info.mStatus = BufferInfo::OWNED_BY_US;info.mFenceFd = -1;info.mRenderInfo = NULL;info.mGraphicBuffer = NULL;info.mNewGraphicBuffer = false;if (mode == IOMX::kPortModePresetSecureBuffer) {void *ptr = NULL;sp<NativeHandle> native_handle;err = mOMXNode->allocateSecureBuffer(portIndex, bufSize, &info.mBufferID,&ptr, &native_handle);info.mData = (native_handle == NULL)? new SecureBuffer(format, ptr, bufSize): new SecureBuffer(format, native_handle, bufSize);info.mCodecData = info.mData;}

可以看到,直接就调用 OMXNode 的 allocateSecureBuffer 方法了,这块buffer将由native层创建,所以 allocateSecureBuffer 将会调用组件的OMX_AllocateBuffer方法:

status_t OMXNodeInstance::allocateSecureBuffer(OMX_U32 portIndex, size_t size, IOMX::buffer_id *buffer,void **buffer_data, sp<NativeHandle> *native_handle) {if (buffer == NULL || buffer_data == NULL || native_handle == NULL) {ALOGE("b/25884056");return BAD_VALUE;}if (portIndex >= NELEM(mSecureBufferType)) {ALOGE("b/31385713, portIndex(%u)", portIndex);android_errorWriteLog(0x534e4554, "31385713");return BAD_VALUE;}Mutex::Autolock autoLock(mLock);if (mHandle == NULL) {return DEAD_OBJECT;}if (!mSailed) {ALOGE("b/35467458");android_errorWriteLog(0x534e4554, "35467458");return BAD_VALUE;}// 检查 port modeif (mPortMode[portIndex] != IOMX::kPortModePresetSecureBuffer) {ALOGE("b/77486542");android_errorWriteLog(0x534e4554, "77486542");return INVALID_OPERATION;}// 创建 BufferMeta,这里的 bufferMeta没有起实质性作用BufferMeta *buffer_meta = new BufferMeta(portIndex);OMX_BUFFERHEADERTYPE *header;// 调用 OMX 组件分配 secure bufferOMX_ERRORTYPE err = OMX_AllocateBuffer(mHandle, &header, portIndex, buffer_meta, size);if (err != OMX_ErrorNone) {CLOG_ERROR(allocateBuffer, err, BUFFER_FMT(portIndex, "%zu@", size));delete buffer_meta;buffer_meta = NULL;*buffer = 0;return StatusFromOMXError(err);}CHECK_EQ(header->pAppPrivate, buffer_meta);// 为 bufferheader 分配id*buffer = makeBufferID(header);// OMX 组件创建的 secure buffer handle 将存储在 buffer header 的 pBuffer 中if (mSecureBufferType[portIndex] == kSecureBufferTypeNativeHandle) {*buffer_data = NULL;// 将handle封装为 native handle*native_handle = NativeHandle::create((native_handle_t *)header->pBuffer, false /* ownsHandle */);} else {*buffer_data = header->pBuffer;*native_handle = NULL;}addActiveBuffer(portIndex, *buffer);sp<IOMXBufferSource> bufferSource(getBufferSource());if (bufferSource != NULL && portIndex == kPortIndexInput) {bufferSource->onInputBufferAdded(*buffer);}CLOG_BUFFER(allocateSecureBuffer, NEW_BUFFER_FMT(*buffer, portIndex, "%zu@%p:%p", size, *buffer_data,*native_handle == NULL ? NULL : (*native_handle)->handle()));return OK;
}

allocateSecureBuffer 和 useBuffer 的内容大同小异,只是调用 OMX_AllocateBuffer 回传的 buffer header 中存储有 OMX 组件分配的 secure buffer header,这个buffer handle将会被封装成为 NativeHandle 回传给上层。OMXNodeInstance 中的 BufferMeta 将不会存储任何相关的资讯。
请添加图片描述

http://www.lryc.cn/news/267247.html

相关文章:

  • 力扣题目学习笔记(OC + Swift)21. 合并两个有序链表
  • C# WPF上位机开发(windows pad上的应用)
  • Word使用技巧【开题报告】
  • 电子学会C/C++编程等级考试2022年06月(七级)真题解析
  • git中的smart checkout和force checkout
  • vue3整合Element-Plus,极速上手。
  • 学习Vue2.x
  • 新手如何快速熟悉代码,写出东西(持续更新)
  • 11-网络安全框架及模型-软件安全能力成熟度模型(SSCMM)
  • Linux操作系统基础知识点
  • python 通过opencv及face_recognition识别人脸
  • Android开发中常见的Hook技术有哪些?
  • 【linux c多线程】线程的创建,线程信息的获取,获取线程返回值
  • MFC或QT中,自绘控件的目的和实现步骤
  • ceph集群搭建详细教程(ceph-deploy)
  • 机器视觉系统选型-避免畸变
  • 机器学习笔记 - 线性判别分析(LDA)的原理和应用
  • 基于5G智能网关的智慧塔吊监测方案
  • CountDownLatch详解以及用法示例
  • 【http】缓存协议
  • vscode中使用GitHub Copilot Chat
  • lvgl 双物理显示器的驱动实现
  • 论文阅读——X-Decoder
  • 【Kubernetes】控制器Statefulset
  • 智能优化算法应用:基于鱼鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码
  • 探索 Vue3 (五) 骨架屏
  • java取出list中的某几个属性组成一个新的集合的几种方式
  • 开源自托管导航页配置服务Dashy本地搭建结合内网穿透远程访问
  • Cloudstack多个管理服务器节点
  • 31. Ajax