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

news2024/11/26 8:21:07

为了了解 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 mParser
        const 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状态置为 Idle
            ALOGV("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 组件状态设置为 Loaded
            ALOGV("forcing Idle->Loaded");
            sendCommand(OMX_CommandStateSet, OMX_StateLoaded);
			// 销毁所有 buffer
            freeActiveBuffers();

            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:
        {
        	// 销毁所有 buffer
            freeActiveBuffers();
            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.coloradmin.cn/o/1325310.html

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

相关文章

X 进制减法问题

思路如下&#xff1a; 代码如下&#xff1a; #include<iostream> using namespace std; const int N 1e5 5; const long long MOD 1000000007; //定义了常量 N 和 MOD。N 是数组的最大长度&#xff0c;MOD 是取模运算的模数。int numsA[N], numsB[N]; //声明了两个数…

IDEA创建springboot工程

选择spring boot的版本和依赖 finish创建完成 删除无用的文件

Android 自动化测试——Monkey测试

Android自带了很多方便的测试工具和方法&#xff0c;包括我们常用的单元测试、Robotium测试、Monkey测试、MonkeyRunner测试、senevent模拟等。这些方法对于我们编写高质量的APP十分有用。也可以提前暴露我们程序的隐藏问题。今天给大家讲一下Monkey测试&#xff0c;Monkey测试…

页面菜单,通过get请求一个url后,跳转另外一个页面,+丢失问题

业务场景描述&#xff1a; 在A系统&#xff0c;菜单点击跳B系统这个操作。 A系统菜单是get请求到B系统的一个缓冲页面&#xff0c;然后这个缓冲页面获取到url中的accessToken后&#xff0c;在这个页面中通过post请求后端接口。 问题描述&#xff1a; 当accessToken中包含了…

Stable Diffusion Windows 部署简单认知

写在前面 偶然看到&#xff0c;简单了解博文为 SD 部署&#xff0c;以及简单使用&#xff0c;部署过程遇到问题解决理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。…

adam优化器和动量

原始的SGD 加上动量&#xff08;惯性&#xff0c;每一次更新根据前面所有结果&#xff0c;使结果更快收敛&#xff09; AdaGrad 与SGD的核心区别在于计算更新步长时&#xff0c;增加了分母&#xff1a;梯度平方累积和的平方根。此项能够累积各个参数 的历史梯度平方&#xf…

Vue 2.5 入门学习记录

Vue 2.5 入门学习记录 1. 基础知识Vue 是什么Vue引入方式Vue特点Vue实例中的数据事件方法Vue中的属性绑定和双向绑定Vue中的v-if、v-show、v-fortoDoList制作局部组件&全局组件 2. vue-cli工程3. 工程案例实践使用vue-cli实现todoList及删除某个元素全局样式与局部样式 4. …

2024年camtasia怎么导出mp4

Camtasia 2024是一款屏幕录制和视频剪辑软件&#xff0c;教授课程&#xff0c;培训他人&#xff0c;以更快的速度和更吸引人的方式进行沟通和屏幕分享。使您在Windows和Mac上进行录屏和剪辑创作专业外观的视频变得更为简单。 Camtasia Studio 2023 win-安装包&#xff1a;https…

智能优化算法应用:基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜜獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜜獾算法4.实验参数设定5.算法结果6.参考文献7.MA…

机器视觉工程师,我存多少钱可以躺平

钱钱钱&#xff01;还是钱&#xff01;除了钱还能聊点别的吗&#xff1f;钱可以让你生活的更好&#xff0c;也可以让你​在生活中的选择很无奈。 如果想要知道拥有多少存款才能躺平不上班&#xff0c;那么首先要明确躺平的定义。所谓的躺平&#xff0c;其实并不代表不能工作&a…

车载V2X方案的选型分享

ACX200T面向 5G车联网C-V2X 应用的安全芯片&#xff0c;满足V2X场景下消息认证的专用安全芯片&#xff0c;该款芯片采用公司自主的 高速硬件加密引擎 &#xff0c;支 持国家标准SM1、SM2、SM3、SM4密码算法&#xff0c;同时支持国际ECDSA、AES、SHA-1密码算法。可实现网联汽车云…

SWUST-跳绳比赛

由于这里只算前60s的&#xff0c;又有每秒跳一个&#xff0c;假设60秒抽象为数组&#xff0c;每个值为1。根据输入来更新数组&#xff0c;若停下则更新为0。最后对数组求和即可。 代码如下 #include<stdio.h> #include<stdlib.h> int main() {int n;scanf("…

Qt前端技术:2.QSS

border-style&#xff1a;后边是两个参数的话第一个参数改变上下的style 第二个参数改变左右的style 如果后边是三个参数的话第一个参数改变上边的style第二个参数改变左右的style&#xff0c;第三个参数改变的下边的style 如果后边是四个参数的话对应的顺序为上&#xff0c;右…

【蓝桥杯】专题练习

前缀和 3956. 截断数组 - AcWing题库 一看到题目很容易想到的思路是对数组求前缀和&#xff0c;然后枚举两个分段点就好&#xff0c;时间复杂度是On^2&#xff0c;n是1e5会t&#xff0c;需要优化。 朴素的代码&#xff0c;会超时&#xff1a; #include <bits/stdc.h> u…

Floating point exception

参考:https://blog.csdn.net/yyangzhenjie/article/details/87859506?spm1001.2101.3001.6661.1&utm_mediumdistribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-87859506-blog-126091159.235%5Ev39%5Epc_relevant_3m_sort_dl_base2&depth_1-ut…

使用包、Crate 和模块管理项目(下)

1、使用 use 关键字将路径引入作用域 在之前的示例中我们引用模块中的函数或者结构体之类的&#xff0c;都是需要用到相对路径或者绝对路径去引用&#xff0c;然尔在这里&#xff0c;有一种方法可以简化这个过程。我们可以使用 use 关键字创建一个短路径&#xff0c;然后就可以…

Spring Boot3通过GraalVM生成exe执行文件

一、安装GraalVM 1、官网&#xff1a;https://www.graalvm.org/downloads/ 2、配置环境变量 2.1、环境变量必须使用JAVA_HOME&#xff0c;否则会出现问题 2.2、在系统变量配置Path,%JAVA_HOME%\bin&#xff0c;注意必须放在顶部第一位 2.3、配置jdk的环境变量&#xff0c;在P…

IDEA中如何创建各种类型的java工程

如果你的工程下面的module没有互相依赖&#xff0c;就相当于是一个小的项目&#xff0c;idea版本不同&#xff0c;细节可能不同 1、普通的Java 工程 在工程上&#xff0c;右键- New - Module&#xff0c;如下&#xff1a; 指明Java工程的名称及使用的JDK版本&#xff1a; 创建…

Redis设计与实现之数据库

目录 一、数据库 1、数据库的结构 2、 数据库的切换 3、 数据库键空间 4、键空间的操作 添加新键 删除键 更新键 取值 其他操作 5、 键的过期时间 6、过期时间的保存 7、设置生存时间 8、过期键的判定 9、 过期键的清除 定时删除 惰性删除 定期删除 10、过期…

基于SSM+Vue的新闻管理系统

基于SSMVue的新闻管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 新闻列表 管理员界面 用户界面 摘要 新闻管理系统基于SSM&#xff08;Spr…