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 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 方法中的内容还是比较清晰简洁的,主要做了如下几件事情:
- 检查 OMXNode 实例个数是否达到上限;
- 创建 OMXNodeInstance 实例,传入参数为 IOmx 服务,IOmxObserver,以及组件名称;
- 将创建的 OMX_COMPONENTTYPE 对象与 OMXNodeInstance 进行绑定;
- 获取 quirks 并给 OMXNodeInstance 设置相关信息;
- 将 IOmxObersver 和 OMXNodeInstance 以键值的形式双向绑定存储;
- 返回 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;
}
- 获取 OMX 组件当前的状态,按照状态依次设定 OMX_StateIdle、OMX_StateLoaded,并且调用 freeActiveBuffers 释放所有的 buffer,这里的buffer指的是什么我们后面再看;
- 调用 IOmx 的 freeNode 方法,传入参数为自身;
- 关闭 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);
}
- 移除两个keyedVector 中的键值,减少OMXNode引用计数,减少IOmxObserver 的引用计数释放资源;
- 调用destroyComponentInstance销毁OMX组件,最终创建的组件还是由 OMXStore 来销毁;
Omx::freeNode 执行完成,OMX组件被销毁,这时候 OMXNodeInstance 有没有被销毁呢?答案是没有的,退出 Omx::freeNode 时,OMXNodeInstance 的引用计数为 1,通过 binder 被 ACodec 持有,当 ACodec 销毁时,OMXNodeInstance 自然就销毁了。
把之前的一幅图改改,来表示 OMXNodeInsatnce、OMX_HANDLE、IOmx、OMXStore 之间的关系:
Android 13 - Media框架(24)- OMXNodeInstance(一)的更多相关文章
- 简析Android 兼容性测试框架CTS使用
一.什么是兼容性测试? 1)为用户提供最好的用户体验,让更多高质量的APP可以顺利的运行在此平台上 2)让程序员能为此平台写更多的高质量的应用程序 3)可以更好的利用Android应用市场 二.CTS ...
- 15类Android通用流行框架
15类Android通用流行框架 Android流行框架 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 图片加载 Android Universal Image Loader 一个 ...
- 25类Android常用开源框架
1.图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 F ...
- 15 个 Android 通用流行框架大全(转)
1. 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...
- Android 通用流行框架
原文出处: http://android.jobbole.com/83028/ 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Andro ...
- 经受时间沉淀的15 个 Android 通用流行框架大全
1. 缓存 名称描述 DiskLruCache: Java实现基于LRU的磁盘缓存 2.图片加载 名称描述 Android Universal Image Loader 一个强大的加载,缓存,展 ...
- Android通用流行框架大全
1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Android Universal Image Loader 一个强大的加载,缓存,展示图 ...
- 60.Android通用流行框架大全
转载:https://segmentfault.com/a/1190000005073746 Android通用流行框架大全 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的 ...
- 15 个 Android 通用流行框架大全
1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Android Universal Image Loader 一个强大的加载,缓存,展 ...
- Android 通用流行框架大全
1. 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...
随机推荐
- 知识图谱在RAG中的应用探讨
在这篇文章中,我们来详细探讨知识图谱(KG)在RAG流程中的具体应用场景. 缘起 关于知识图谱在现在的RAG中能发挥出什么样的作用,之前看了360 刘焕勇的一个分享,简单的提了使用知识图谱增强大模型的 ...
- Docker之离线安装和在线安装
一.离线安装 1.软件包下载 https://download.docker.com/linux/static/stable/x86_64/ 2.安装docker tar xvf /opt/docke ...
- 面向切面编程AOP[一](java 代码详解)
前言 说句实话,在工作中,使用的aop不是特别多,但是特别重要,一般是辅助程序,在现代开发者辅助程序相当重要,比如说我们需要打印一些log,但是我们不可能去卸载我们的业务程序中,因为这太..... 正 ...
- c# Barrier 线程回调
前言 假如现在有一个这样的需求,我一堆小黄人生产小黄丹,而大黄人要一直吃小黄丹. 如果是这样的话,想到就是一堆小黄人作为一个多线程,然后一直制造,然后另外一个大黄人一直检索是否有小黄丹,有就吃掉. 但 ...
- Elasticsearch数据同步优化
Elasticsearch数据同步优化 背景 为了满足项目需求,需要将大量数据的数据写入到ES进行检索,预估数据量是40亿左右,目前需要同步进去的是2亿左右. ES集群配置 三台128G的国产服务器 ...
- springboot 整合webservice 相关说明
1.环境依赖 jdk8, springboot 2.3.12.release,cxf版本需要根据springboot版本修改,方法:查看springboot版本的发布日期,然后根据日期找相近的两个版本 ...
- KubeOperator技术方案
KubeOperator技术方案 总体介绍︎ KubeOperator 是一个开源的轻量级 Kubernetes 发行版,专注于帮助企业规划.部署和运营生产级别的 Kubernetes 集群. Kub ...
- 力扣23(java)-合并k个升序链表(困难)
题目: 给你一个链表数组,每个链表都已经按升序排列. 请你将所有链表合并到一个升序链表中,返回合并后的链表. 示例 1: 输入:lists = [[1,4,5],[1,3,4],[2,6]]输出:[1 ...
- 力扣686(java)-重复叠加字符串匹配(中等)
题目: 给定两个字符串 a 和 b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 的子串,如果不存在则返回 -1. 注意:字符串 "abc" 重复叠加 ...
- 云原生DevOps的5步升级路径
简介: 究竟什么是云原生DevOps呢?我们认为:云原生DevOps是充分利用云原生基础设施,基于微服务/无服务架构体系和开源标准,语言和框架无关,具备持续交付和智能自运维能力,从而做到比传统DevO ...