Android 13 - Media框架(24)- OMXNodeInstance(一)

为了了解 ACodec 是如何与 OpenMAX 组件进行 buffer 流转的,我们有必要先来学习 OMXNodeInstance,在前面的章节中,我们已经了解了 media.codec 进程包含的内容,以及 OpenMAX 框架中的一些内容。这一节我们将来学习 OMXNode 与 media.codec 进程之间的关系,了解OMXNode是如何创建、使用、销毁的。

1、创建 OMXNodeInstance

我们回到 Omx.cpp 来看 allocateNode 方法:

Return<void> Omx::allocateNode(const hidl_string& name,const sp<IOmxObserver>& observer,allocateNode_cb _hidl_cb) {using ::android::IOMXNode;using ::android::IOMXObserver;sp<OMXNodeInstance> instance;{// 检查是否到达了 OMXNode 实例最大个数Mutex::Autolock autoLock(mLock);if (mLiveNodes.size() == kMaxNodeInstances) {_hidl_cb(toStatus(NO_MEMORY), nullptr);return Void();}// 创建 OMXNodeInstance 实例,传入参数为 IOmx 服务,IOmxObserver,以及组件名称instance = new OMXNodeInstance(this, new LWOmxObserver(observer), name.c_str());// 调用 OMXStore 的方法创建 OMX_COMPONENTTYPE 对象OMX_COMPONENTTYPE *handle;OMX_ERRORTYPE err = mStore->makeComponentInstance(name.c_str(), &OMXNodeInstance::kCallbacks,instance.get(), &handle);if (err != OMX_ErrorNone) {LOG(ERROR) << "Failed to allocate omx component ""'" << name.c_str() << "' "" err=" << asString(err) <<"(0x" << std::hex << unsigned(err) << ")";_hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);return Void();}// 将创建的 OMX_COMPONENTTYPE 对象与 OMXNodeInstance 进行绑定instance->setHandle(handle);// 获取 quirks 信息// Find quirks from mParserconst auto& codec = mParser.getCodecMap().find(name.c_str());if (codec == mParser.getCodecMap().cend()) {LOG(WARNING) << "Failed to obtain quirks for omx component ""'" << name.c_str() << "' ""from XML files";} else {uint32_t quirks = 0;for (const auto& quirk : codec->second.quirkSet) {if (quirk == "quirk::requires-allocate-on-input-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnInputPorts;}if (quirk == "quirk::requires-allocate-on-output-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnOutputPorts;}}// 如果有 quirks 信息则设置instance->setQuirks(quirks);}// 将 IOmxObersver 和 OMXNodeInstance 以键值的形式存储mLiveNodes.add(observer.get(), instance);// 将 OMXNodeInstance 和 IOmxObersver 以键值的形式存储mNode2Observer.add(instance.get(), observer.get());}observer->linkToDeath(this, 0);// 返回 OMXNodeInstance 给 ACodec_hidl_cb(toStatus(OK), new TWOmxNode(instance));return Void();
}

allocateNode 方法中的内容还是比较清晰简洁的,主要做了如下几件事情:

  1. 检查 OMXNode 实例个数是否达到上限;
  2. 创建 OMXNodeInstance 实例,传入参数为 IOmx 服务,IOmxObserver,以及组件名称
  3. 将创建的 OMX_COMPONENTTYPE 对象与 OMXNodeInstance 进行绑定
  4. 获取 quirks 并给 OMXNodeInstance 设置相关信息;
  5. 将 IOmxObersver 和 OMXNodeInstance 以键值的形式双向绑定存储;
  6. 返回 OMXNodeInstance 返回给 ACodec。

以上是代码的解释,接下来是我们对这段代码的理解:

  • OMX_COMPONENTTYPE 是由 OMXStore 创建,这个指针由 OMXStore 来管理,所以最终也由 OMXStore 来释放;
  • OMX_COMPONENTTYPE 和 OMXNodeInstance 进行了绑定,OMXNodeInstance帮我们进行OMX组件方法的封装,之后上层调用 OMXNodeInstance 的方法,OMXNodeInstance 最终调用到组件的接口;
  • OMXNodeInstance 的引用计数为2,mLiveNodes 中存有一个计数,上层ACodec存有一个计数,mNode2Observer 中存储的是指针,所以不会有计数增加。

2、OMXNodeInstance 构造函数

OMXNodeInstance::OMXNodeInstance(Omx *owner, const sp<IOMXObserver> &observer, const char *name): mOwner(owner),mHandle(NULL),mObserver(observer),mDying(false),mSailed(false),mQueriedProhibitedExtensions(false),mQuirks(0),mBufferIDCount(0),mRestorePtsFailed(false),mMaxTimestampGapUs(0LL),mPrevOriginalTimeUs(-1LL),mPrevModifiedTimeUs(-1LL)
{mName = ADebug::GetDebugName(name);DEBUG = ADebug::GetDebugLevelFromProperty(name, "debug.stagefright.omx-debug");ALOGV("debug level for %s is %d", name, DEBUG);DEBUG_BUMP = DEBUG;mNumPortBuffers[0] = 0;mNumPortBuffers[1] = 0;mDebugLevelBumpPendingBuffers[0] = 0;mDebugLevelBumpPendingBuffers[1] = 0;mMetadataType[0] = kMetadataBufferTypeInvalid;mMetadataType[1] = kMetadataBufferTypeInvalid;mPortMode[0] = IOMX::kPortModePresetByteBuffer;mPortMode[1] = IOMX::kPortModePresetByteBuffer;mSecureBufferType[0] = kSecureBufferTypeUnknown;mSecureBufferType[1] = kSecureBufferTypeUnknown;mGraphicBufferEnabled[0] = false;mGraphicBufferEnabled[1] = false;mIsSecure = AString(name).endsWith(".secure");mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled("legacy-adaptive");
}

OMXNodeInstance 的构造函数主要就初始化了几个数组,这个数组中都是由两个元素,索引0表示input port,索引 1 表示 output port,以下是一些数组的意义:

  • mNumPortBuffers:port 中buffer的数量;
  • mMetadataType:meta data 的类型,这个用于确定有surface的情况下ouput buffer的类型,以及input为camera或者是graphic(录屏)的情况下 input buffer的类型;
  • mPortMode:端口模式;
  • mSecureBufferType:secure buffer 的类型;
  • mGraphicBufferEnabled:是否使用graphic buffer;

看过前面章节的小伙伴应该大致可以揣摩出使用的枚举类型的意义。

void OMXNodeInstance::setHandle(OMX_HANDLETYPE handle) {CLOG_LIFE(allocateNode, "handle=%p", handle);CHECK(mHandle == NULL);mHandle = handle;if (handle != NULL) {mDispatcher = new CallbackDispatcher(this);}
}

setHandle方法可以把创建的OMX组件和OMXNodeInstance实例进行绑定,同时会创建一个CallbackDispatcher对象。

3、CallbackDispatcher

CallbackDispatcher 从名字上来看是回调的调度者,它的作用是开启一个线程,所有由OMX组件发上来的消息或者事件都会进入到该线程的队列当中,按照顺序一个一个通过 IOmxObserver 发回到 ACodec 层。

OMX callback 会把事件或者消息封装成为 omx_message,再通过IOmxObserver发送,具体如何封装的,以及ACodec如何解封装,参考OnEvent、OnEmptyBufferDone、OnFillBufferDone这三个方法的实现。

具体 CallbackDispatcher 和 CallbackDispatcherThread 是如何实现的我们这里不做过多的了解。

4、销毁 OMXNodeInstance

我们常常只关注对象是如何创建的,其实销毁的过程也很重要,这里我们就一起来了解OMXNode是如何被销毁的。

目光回到 ACodec 中来,当我们调用了 initiateShutdown 去释放组件时,ACodec 会调用 IOMXNode 的 freeNode 方法:

        case ACodec::kWhatReleaseCodecInstance:{ALOGI("[%s] forcing the release of codec",mCodec->mComponentName.c_str());status_t err = mCodec->mOMXNode->freeNode();ALOGE_IF("[%s] failed to release codec instance: err=%d",mCodec->mComponentName.c_str(), err);mCodec->mCallback->onReleaseCompleted();mCodec->changeState(mCodec->mUninitializedState);break;}

最终调用到 OMXNodeInstance 的 freeNode 方法中:

status_t OMXNodeInstance::freeNode() {CLOG_LIFE(freeNode, "handle=%p", mHandle);static int32_t kMaxNumIterations = 10;// Transition the node from its current state all the way down// to "Loaded".// This ensures that all active buffers are properly freed even// for components that don't do this themselves on a call to// "FreeHandle".// The code below may trigger some more events to be dispatched// by the OMX component - we want to ignore them as our client// does not expect them.bool expected = false;if (!mDying.compare_exchange_strong(expected, true)) {// exit if we have already freed the node or doing so right now.// NOTE: this ensures that the block below executes at most once.ALOGV("Already dying");return OK;}// 获取 OMX 组件当前的状态OMX_STATETYPE state;CHECK_EQ(OMX_GetState(mHandle, &state), OMX_ErrorNone);switch (state) {case OMX_StateExecuting:{// 将OMX状态置为 IdleALOGV("forcing Executing->Idle");sendCommand(OMX_CommandStateSet, OMX_StateIdle);OMX_ERRORTYPE err;int32_t iteration = 0;// 阻塞等待while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone&& state != OMX_StateIdle&& state != OMX_StateInvalid) {if (++iteration > kMaxNumIterations) {CLOGW("failed to enter Idle state (now %s(%d), aborting.",asString(state), state);state = OMX_StateInvalid;break;}usleep(100000);}CHECK_EQ(err, OMX_ErrorNone);if (state == OMX_StateInvalid) {break;}FALLTHROUGH_INTENDED;}case OMX_StateIdle:{// 将 OMX 组件状态设置为 LoadedALOGV("forcing Idle->Loaded");sendCommand(OMX_CommandStateSet, OMX_StateLoaded);// 销毁所有 bufferfreeActiveBuffers();OMX_ERRORTYPE err;int32_t iteration = 0;// 阻塞等待while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone&& state != OMX_StateLoaded&& state != OMX_StateInvalid) {if (++iteration > kMaxNumIterations) {CLOGW("failed to enter Loaded state (now %s(%d), aborting.",asString(state), state);state = OMX_StateInvalid;break;}ALOGV("waiting for Loaded state...");usleep(100000);}CHECK_EQ(err, OMX_ErrorNone);FALLTHROUGH_INTENDED;}case OMX_StateLoaded:{// 销毁所有 bufferfreeActiveBuffers();FALLTHROUGH_INTENDED;}case OMX_StateInvalid:break;default:LOG_ALWAYS_FATAL("unknown state %s(%#x).", asString(state), state);break;}Mutex::Autolock _l(mLock);// 调用 OMXNodeInstance 的另一个 freeNode 方法,传入参数为自身status_t err = mOwner->freeNode(this);// 关闭 Dispatcher 线程,销毁相关内容 mDispatcher.clear();mOMXBufferSource.clear();mHandle = NULL;CLOG_IF_ERROR(freeNode, err, "");free(mName);mName = NULL;ALOGV("OMXNodeInstance going away.");return err;
}
  1. 获取 OMX 组件当前的状态,按照状态依次设定 OMX_StateIdle、OMX_StateLoaded,并且调用 freeActiveBuffers 释放所有的 buffer,这里的buffer指的是什么我们后面再看;
  2. 调用 IOmx 的 freeNode 方法,传入参数为自身
  3. 关闭 Dispatcher 线程;

这里比较令人疑惑的可能就是第二点了,我们刚刚调用的 OMXNode freeNode 干了什么?为什么又要调用一个freeNode呢?

其实从上面的代码我们可以看出来,OMXNodeInstance 的 freeNode 方法是用于关闭或者销毁 OMX 组件所使用的一些资源,但是这时候 OMX 组件还是存在没有被销毁的。之所以把销毁资源的方法放在 OMXNodeInstance 中是为了保证 API 调用逻辑的统一,所有的关于组件的操作方法都放在 OMXNode 当中。

当OMX组件的资源全部释放完成,下一步就是要销毁OMX组件了,调用的方法就是 IOmx 的 freeNode 方法:

status_t Omx::freeNode(sp<OMXNodeInstance> const& instance) {if (instance == NULL) {return OK;}{Mutex::Autolock autoLock(mLock);// 获取OMXNode指针ssize_t observerIndex = mNode2Observer.indexOfKey(instance.get());if (observerIndex >= 0) {wp<IBase> observer = mNode2Observer.valueAt(observerIndex);ssize_t nodeIndex = mLiveNodes.indexOfKey(observer);// 移除引用计数,移除指针if (nodeIndex >= 0) {mNode2Observer.removeItemsAt(observerIndex);mLiveNodes.removeItemsAt(nodeIndex);sp<IBase> sObserver = observer.promote();if (sObserver != nullptr) {sObserver->unlinkToDeath(this);}} else {LOG(WARNING) << "Inconsistent observer record";}}}OMX_ERRORTYPE err = OMX_ErrorNone;if (instance->handle() != NULL) {// 调用destroyComponentInstance销毁OMX组件err = mStore->destroyComponentInstance(static_cast<OMX_COMPONENTTYPE*>(instance->handle()));}return StatusFromOMXError(err);
}
  1. 移除两个keyedVector 中的键值,减少OMXNode引用计数,减少IOmxObserver 的引用计数释放资源;
  2. 调用destroyComponentInstance销毁OMX组件,最终创建的组件还是由 OMXStore 来销毁;

Omx::freeNode 执行完成,OMX组件被销毁,这时候 OMXNodeInstance 有没有被销毁呢?答案是没有的,退出 Omx::freeNode 时,OMXNodeInstance 的引用计数为 1,通过 binder 被 ACodec 持有,当 ACodec 销毁时,OMXNodeInstance 自然就销毁了。

把之前的一幅图改改,来表示 OMXNodeInsatnce、OMX_HANDLE、IOmx、OMXStore 之间的关系:
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/235417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快猫视频模板源码定制开发 苹果CMS 可打包成双端APP

苹果CMS快猫视频网站模板源码&#xff0c;可用于开发双端APP&#xff0c;后台支持自定义参数&#xff0c;包括会员升级页面、视频、演员、专题、收藏和会员系统等完整模块。还可以直接指定某个分类下的视频为免费专区&#xff0c;具备完善的卡密支付体系&#xff0c;无需人工管…

听GPT 讲Rust源代码--src/tools(17)

File: rust/src/tools/rust-analyzer/crates/profile/src/hprof.rs 在Rust源代码中&#xff0c;rust/src/tools/rust-analyzer/crates/profile/src/hprof.rs文件是rust-analyzer中的性能分析模块&#xff0c;用于代码运行时的性能统计和分析。下面将详细介绍每个结构体的作用&a…

【操作系统】什么是进程?

文章目录 进程进程的属性进程的状态挂起 进程 进程是一个可并发执行的具有独立功能的程序关于某个数据集合的执行过程&#xff0c;也是操作系统进行资源分配和保护的基本单位。 进程的属性 结构性&#xff1a; 共享性&#xff1a;同一程序运行于不同数据集合上构成不同的进程…

Flink Table API 与 SQL 编程整理

Flink API总共分为4层这里主要整理Table API的使用 Table API是流处理和批处理通用的关系型API&#xff0c;Table API可以基于流输入或者批输入来运行而不需要进行任何修改。Table API是SQL语言的超集并专门为Apache Flink设计的&#xff0c;Table API是Scala和Java语言集成式…

【漏洞复现】奥威亚 教学视频应用服务平台任意文件上传漏洞

漏洞描述 AVA 教学视频应用服务平台是由广州市奥威亚电子科技有限公司基于当前教育视频资源建设的背景及用户需求的调研,开发出来能够适应时代发展和满足学校需求,具有实效性、多功能、特点鲜明的平台。 该平台存在任意文件上传漏洞,通过此漏洞攻击者可上传webshell木马,…

OpenCV-9颜色空间的转换

颜色转换API&#xff1a;cvtColor&#xff08;img&#xff0c;colorsapce&#xff09; cvt含义为转换 convesion(转换) 下面为示例代码&#xff1a; import cv2# callback中至少有一个参数 def callback(value):passcv2.namedWindow("color", cv2.WINDOW_NORMAL) …

C#上位机与欧姆龙PLC的通信03----创建项目工程

1、创建仿真PLC 这是一款CP1H-X40DR-A的PLC&#xff0c;呆会后面创建工程的时候需要与这个类型的PLC类型一致&#xff0c;否则程序下载不到PLC上。 2、创建虚拟串口 首先安装&#xff0c;这个用来创建虚拟串口来模拟真实的串口&#xff0c;也就是上位机上有那种COM口&#xf…

Java版直播商城免 费 搭 建:电商、小程序、三级分销及免 费 搭 建,平台规划与营销策略全掌握

随着互联网的快速发展&#xff0c;越来越多的企业开始注重数字化转型&#xff0c;以提升自身的竞争力和运营效率。在这个背景下&#xff0c;鸿鹄云商SAAS云产品应运而生&#xff0c;为企业提供了一种简单、高效、安全的数字化解决方案。 鸿鹄云商SAAS云产品是一种基于云计算的软…

神经科学与计算神经科学的蓬勃发展与未来趋势

导言 神经科学和计算神经科学是当前科学研究领域中备受关注的方向。本文将深入研究这两个领域的发展历程、遇到的问题、解决过程&#xff0c;以及未来的可用范围。我们还将关注在各国的应用现状以及未来的研究趋势&#xff0c;探讨如何在竞争中取胜&#xff0c;以及在哪些方面发…

阿里云大模型数据存储解决方案,为 AI 创新提供推动力

云布道师 随着国内首批大模型产品获批名单问世&#xff0c;百“模”大战悄然开启。在这场百“模”大战中&#xff0c;每一款大模型产品的诞生&#xff0c;都离不开数据的支撑。如何有效存储、管理和处理海量多模态数据集&#xff0c;并提升模型训练、推理的效率&#xff0c;保…

LVS+keepalived小白都看得懂也不来看?

1 高可用集群 1.1 一个合格的集群应该具备的特性 1.负载均衡 LVS Nginx HAProxy F5 2.健康检查&#xff08;使得调度器检查节点状态是否可以正常运行&#xff0c;调度器&#xff08;负载均衡器&#xff09;也要做健康检查&#xff09;for调度器/节点服务器 keeplived hearb…

机器学习中的一些经典理论定理

PAC学习理论 当使用机器学习方法来解决某个特定问题时&#xff0c;通常靠经验或者多次试验来选择合适的模型、训练样本数量以及学习算法收敛的速度等。但是经验判断或多次试验往往成本比较高&#xff0c;也不太可靠&#xff0c;因此希望有一套理论能够分析问题难度、计算模型能…

PyQt6 QTableWidget表格控件

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计50条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

JavaWeb笔记之前端开发HTML

一、引言 1.1HTML概念 网页&#xff0c;是网站中的一个页面&#xff0c;通常是网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台。通俗的说&#xff0c;网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件&#xff0c;俗称 HTML文件。 …

Opencv入门6(读取彩色视频并转换为对数极坐标视频)

源码如下&#xff1a; #include <opencv2/opencv.hpp> #include <iostream> int main(int argc, char* argv[]) { cv::namedWindow("Example2_11", cv::WINDOW_AUTOSIZE); cv::namedWindow("Log_Polar", cv::WINDOW_AUTOSIZE); c…

互联网的演进与未来展望:一代、二代、三代互联网的发展之路

导言 从诞生至今&#xff0c;互联网已经经历了一代、二代、三代的演变。本文将深入研究这三个互联网时代的发展过程、遇到的问题、解决的过程&#xff0c;以及未来的可用范围&#xff0c;同时考察各国在互联网应用上的状况和未来的研究趋势。还将探讨在哪个方向能够取胜&#x…

LeetCode 取经之路——第三题-无重复长度的最长子串

&#x1f389;&#x1f389;&#x1f389;今天给大家分享的是一道滑动窗口的OJ题。 3.无重复长度的最长子串 &#x1f61b;&#x1f61b;&#x1f61b;希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff01; 动动…

【开源软件】最好的开源软件-2023-第四名 vaadin

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

[电子榨菜] js中的闭包closure

0.写在前面: 下学期就打算去实习了,这段时间要密集接收考试和面试的捶打,计网和软工就没有办法为大家继续贡献开源内容了,明年九月份之前的更新内容将会以前端,人工智能,和工程设计为基础, 很抱歉啦,不过我还是希望我这一年来的努力可以帮到一些人.虽然自己这一年过的浑浑噩噩…

2023/12/20 work

1. 使用select完成TCP客户端程序 2. 使用poll完成TCP并发服务器 3. 思维导图