Android 13 - Media框架(20)- ACodec(二)

这一节开始我们就来学习 ACodec 的实现

1、创建 ACodec

ACodec 是在 MediaCodec 中创建的,这里先贴出创建部分的代码:

    mCodec = mGetCodecBase(name, owner);if (mCodec == NULL) {ALOGE("Getting codec base with name '%s' (owner='%s') failed", name.c_str(), owner);return NAME_NOT_FOUND;}if (mDomain == DOMAIN_VIDEO) {// video codec needs dedicated looperif (mCodecLooper == NULL) {status_t err = OK;mCodecLooper = new ALooper;mCodecLooper->setName("CodecLooper");err = mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);if (OK != err) {ALOGE("Codec Looper failed to start");return err;}}mCodecLooper->registerHandler(mCodec);} else {mLooper->registerHandler(mCodec);}

从前面的学习我们可以知道,MediaCodec 使用的是异步消息处理的机制,创建MediaCodec 时需要传入一个 ALooper 对象用于处理发送给 MediaCodec 的消息。同样的 ACodec 也是用的异步消息处理机制,它也需要一个 ALooper,这个 ALooper 应该由上一级 MediaCodec 传递,从上面的代码我们可以知道,如果创建的是音频解码器,那么 ACodec 将会复用 MediaCodec 的 ALooper,也就是它们的消息处理会在相同线程当中;如果是视频解码器,那么 MediaCodec 会创建一个专门的 ALooper 给 ACodec 使用,ACodec 和 MediaCodec 的消息处理在不同线程中。

ACodec::ACodec(): mSampleRate(0),mNodeGeneration(0),mUsingNativeWindow(false),mNativeWindowUsageBits(0),mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),mIsVideo(false),mIsImage(false),mIsEncoder(false),mFatalError(false),mShutdownInProgress(false),mExplicitShutdown(false),mIsLegacyVP9Decoder(false),mIsStreamCorruptFree(false),mIsLowLatency(false),mEncoderDelay(0),mEncoderPadding(0),mRotationDegrees(0),mChannelMaskPresent(false),mChannelMask(0),mDequeueCounter(0),mMetadataBuffersToSubmit(0),mNumUndequeuedBuffers(0),mRepeatFrameDelayUs(-1LL),mMaxPtsGapUs(0LL),mMaxFps(-1),mFps(-1.0),mCaptureFps(-1.0),mCreateInputBuffersSuspended(false),mTunneled(false),mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0),mStateGeneration(0),mVendorExtensionsStatus(kExtensionsUnchecked) {memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo));mUninitializedState = new UninitializedState(this);mLoadedState = new LoadedState(this);mLoadedToIdleState = new LoadedToIdleState(this);mIdleToExecutingState = new IdleToExecutingState(this);mExecutingState = new ExecutingState(this);mOutputPortSettingsChangedState =new OutputPortSettingsChangedState(this);mExecutingToIdleState = new ExecutingToIdleState(this);mIdleToLoadedState = new IdleToLoadedState(this);mFlushingState = new FlushingState(this);mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;mInputEOSResult = OK;mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));changeState(mUninitializedState);
}

ACodec 的构造函数主要是初始化了成员对象,实例化了各个状态对象,并且将状态切换到了 UninitializedState,这时候我们就要去查看它的 stateEntered 方法:

void ACodec::UninitializedState::stateEntered() {ALOGV("Now uninitialized");if (mDeathNotifier != NULL) {if (mCodec->mOMXNode != NULL) {auto tOmxNode = mCodec->mOMXNode->getHalInterface<IOmxNode>();if (tOmxNode) {tOmxNode->unlinkToDeath(mDeathNotifier);}}mDeathNotifier.clear();}mCodec->mUsingNativeWindow = false;mCodec->mNativeWindow.clear();mCodec->mNativeWindowUsageBits = 0;mCodec->mOMX.clear();mCodec->mOMXNode.clear();mCodec->mFlags = 0;mCodec->mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;mCodec->mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;mCodec->mConverter[0].clear();mCodec->mConverter[1].clear();mCodec->mComponentName.clear();
}

UninitializedState::stateEntered 主要是将与 OMX 组件相关的成员对象重置初始化

2、initiateAllocateComponent

创建 MediaCodec 时,ACodec 也就被创建了,随后就会调用 initiateAllocateComponent 方法创建 OMX 组件,ACodec 创建之后处在 UninitializedState,所以消息最终在该状态中被处理:

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {ALOGV("onAllocateComponent");CHECK(mCodec->mOMXNode == NULL);mCodec->mFatalError = false;// 创建 Callback 消息,并且设置好 notifysp<AMessage> notify = new AMessage(kWhatOMXMessageList, mCodec);// notify 的 generation 为 nodegeneration + 1,这是因为进入 loaded 状态后,mNodeGeneration 会 + 1notify->setInt32("generation", mCodec->mNodeGeneration + 1);// 需要检查 codecInfo 才能创建 OMXNodesp<RefBase> obj;CHECK(msg->findObject("codecInfo", &obj));sp<MediaCodecInfo> info = (MediaCodecInfo *)obj.get();if (info == nullptr) {ALOGE("Unexpected nullptr for codec information");mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);return false;}AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName();AString componentName;CHECK(msg->findString("componentName", &componentName));// 创建 callback 对象sp<CodecObserver> observer = new CodecObserver(notify);sp<IOMX> omx;sp<IOMXNode> omxNode;status_t err = NAME_NOT_FOUND;// 创建 OMXClientOMXClient client;// 获取 IOmx 服务if (client.connect(owner.c_str()) != OK) {mCodec->signalError(OMX_ErrorUndefined, NO_INIT);return false;}// 将获取到的 IOmx 服务代理封装为 Legacy 模式omx = client.interface();pid_t tid = gettid();int prevPriority = androidGetThreadPriority(tid);androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);// 创建 IOmxNode 服务代理,并且封装为 IOMXNodeerr = omx->allocateNode(componentName.c_str(), observer, &omxNode);androidSetThreadPriority(tid, prevPriority);mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec));auto tOmxNode = omxNode->getHalInterface<IOmxNode>();if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) {mDeathNotifier.clear();}// 记录新的状态下的 ACodec 状态++mCodec->mNodeGeneration;mCodec->mComponentName = componentName;mCodec->mRenderTracker.setComponentName(componentName);mCodec->mFlags = 0;// 记录是否创建的是 secure 组件if (componentName.endsWith(".secure")) {mCodec->mFlags |= kFlagIsSecure;mCodec->mFlags |= kFlagIsGrallocUsageProtected;mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;}mCodec->mOMX = omx;mCodec->mOMXNode = omxNode;// 调用 callback 通知 MediaCodec 完成阻塞调用mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());// 切换状态到 LoadedStatemCodec->changeState(mCodec->mLoadedState);return true;
}

这里涉及的内容比较多:

  1. 创建 notify 对象,并且传入到 CodecObserver 对象中,CodecObserver 会把 OMX 发送回来的消息重新封装,再通过 notify message 转发给 ACodec,最后在不同状态中处理。这里有个是 mNodeGeneration 用于检查 OMX 消息及时性的,但是实际并未启用。这里有一点需要注意,这些 State 状态类都是 ACodec 的内部类,C++11之后内部类可以访问外部类的私有成员以及私有方法,所以虽然这些类并不是 ACodec 的友元,但是同样是可以调用 ACodec 所有方法的。
  2. 创建 OMXNode 之前,会先检查从 MediaCodec 层获取到的 MediaCodecInfo,如果没有这个信息将会报错,这里算是一个双重检查,防止强行越过 MediaCodecList 的检查;
  3. 调用 OMXClient 的方法获取 IOmx 的代理,并用该代理创建 IOmxNode 代理,传入参数为组件名称;
  4. 如果组件名称以 secure 结尾,那么说明需要创建安全组件,并且记录到 ACodec mFlags 成员中;
  5. 调用 onComponentAllocated 通知 MediaCodec 完成阻塞调用;
  6. 切换状态到 LoadedState

在看 LoadedState 的 stateEntered 方法之前,我们要先看下 BaseState 给出的 stateExited 方法,这里用到了 mStateGeneration,用来记录 ACodec 当前的状态变化,在处理消息时,如果传来的消息 generation 不等于当前的generation,说明状态机发生错误,这和之前看到的部分是不一样的,具体什么情况会出现不一样我们后续再做了解。

void ACodec::BaseState::stateExited() {++mCodec->mStateGeneration;
}

从上面的代码我们可以知道,每次状态切换,mStateGeneration数都会加 1 。

接下来看 LoadedState 的 stateEntered

void ACodec::LoadedState::stateEntered() {ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());mCodec->mPortEOS[kPortIndexInput] =mCodec->mPortEOS[kPortIndexOutput] = false;mCodec->mInputEOSResult = OK;mCodec->mDequeueCounter = 0;mCodec->mMetadataBuffersToSubmit = 0;mCodec->mRepeatFrameDelayUs = -1LL;mCodec->mInputFormat.clear();mCodec->mOutputFormat.clear();mCodec->mBaseOutputFormat.clear();mCodec->mGraphicBufferSource.clear();if (mCodec->mShutdownInProgress) {bool keepComponentAllocated = mCodec->mKeepComponentAllocated;mCodec->mShutdownInProgress = false;mCodec->mKeepComponentAllocated = false;onShutdown(keepComponentAllocated);}mCodec->mExplicitShutdown = false;mCodec->processDeferredMessages();
}

LoadedState::stateEntered 会对编解码过程中记录信息的成员变量进行重置,后面是关于 shutdown 的处理流程,这里暂时不看。

到这里我们先做一个小结:MediaCodec 创建完成后,ACodec 最终会进入到 LoadedState,这个状态代表了内部的 OMX 组件已经创建完成,UninitializedState 则表示OMX组件还未创建或者是已经销毁的状态。

3、initiateConfigureComponent

组件创建完成后,就要开始配置组件了,这时候状态在 LoadedState,所以我们去这个状态下找对应的处理:

bool ACodec::LoadedState::onConfigureComponent(const sp<AMessage> &msg) {ALOGV("onConfigureComponent");CHECK(mCodec->mOMXNode != NULL);status_t err = OK;// 检查 mime type,调用 configureCodec 方法AString mime;if (!msg->findString("mime", &mime)) {err = BAD_VALUE;} else {err = mCodec->configureCodec(mime.c_str(), msg);}if (err != OK) {ALOGE("[%s] configureCodec returning error %d",mCodec->mComponentName.c_str(), err);mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));return false;}// 调用 CallbackmCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat);return true;
}

onConfigureComponent 会先去检查 mime type,如果没有将会返回error,如果有则会再调用 ACodec 的 configureCodec 方法做组件的配置,这个方法会比较复杂,我们留到下一节来讲,最后会调用 callback 完成 MediaCodec 阻塞调用,同时把 input format 和 output format 回传给 MediaCodec,需要注意的是这里的 output format 并不一定是准确的,可能是 omx 设定的默认值,在decoder解出相关序列信息之后会把真正的 output format 再回传回来。

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

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

相关文章

ES 如何将国际标准时间格式进行格式化与调整时区

需求&#xff0c;日志收集的时候&#xff0c;时间格式是国际标准时间格式。形如yyyy-MM-ddTHH:mm:ss.SSS。 &#xff08;2023-12-05T02:45:50.282Z&#xff09;这个时区也不对&#xff0c;那如何将此类型的时间&#xff0c;进行格式化呢&#xff1f; 本篇文章体统一个案例&…

Other -- ChatGPT 原理

本文为个人理解&#xff0c;帮助小白&#xff08;本人就是&#xff09;了解正在创建新时代的 AI 产品&#xff0c;如文中理解有误欢迎留言。 [参考链接--](https://baijiahao.baidu.com/s?id1765556782543603120&wfrspider&forpc) 1. 了解一些基本概念 大语言模型&a…

修改 Ganglia 监控 Grid Report timezone 时区 为 东八区 +8 PRC

Ganglia 监控 Grid Report timezone 默认时区 为 零时区 0 现在要修改为 东八区 8 具体操作如下 modify ganglia-web report timezone 0 --> 8 vim /apps/svr/httpd-2.4.48/htdocs/ganglia/header.php // add timezone GMT8 ini_set(date.timezone, PRC);详细记录&#x…

【面试】测试/测开(ING)

63. APP端特有的测试 参考&#xff1a;APP专项测试、APP应用测试 crash和anr的区别 1&#xff09;网络测试 2&#xff09;中断测试 3&#xff09;安装、卸载测试 4&#xff09;兼容测试 5&#xff09;性能测试&#xff08;耗电量、流量、内存、服务器端&#xff09; 6&#xf…

画对比折线图【Python】

出这一期想必是我做某个课程作业遇到了。 由于去各个官网下载对比图要钱&#xff0c;我还是不想花钱的&#xff01;真讨厌&#xff01;浅浅水一期。 以下是要做的对比图的数据&#xff1a; 代码&#xff1a; from matplotlib import pyplot as plt#设置中文显示plt.rcParams[…

CSS新手入门笔记整理:CSS浮动布局

文档流概述 正常文档流 “文档流”指元素在页面中出现的先后顺序。正常文档流&#xff0c;又称为“普通文档流”或“普通流”&#xff0c;也就是W3C标准所说的“normal flow”。正常文档流&#xff0c;将一个页面从上到下分为一行一行&#xff0c;其中块元素独占一行&#xf…

ChatGPT OpenAI API请求限制 尝试解决

1. OpenAI API请求限制 Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for gpt-3.5-turbo-16k in organization org-U7I2eKpAo6xA7RUa2Nq307ae on reques…

让内存无处可逃:智能指针[C++11]

智能指针 文章目录 智能指针前言RAII什么是智能指针智能指针的应用示例 C98的auto_ptr共享型智能指针&#xff1a;shared_ptrshared_ptr的使用初始化获取原生指针指定删除器默认删除器default_delete指定删除器指定删除器管理动态数组 shared_ptr的伪实现shared_ptr的注意事项避…

【Docker】进阶之路:(五)Docker引擎

【Docker】进阶之路&#xff1a;&#xff08;五&#xff09;Docker引擎 Docker引擎简介Docker引擎的组件构成runccontainerd Docker引擎简介 Docker引擎是用来运行和管理容器的核心部分。Docker首次发布时&#xff0c;Docker 引擎由LXC 和 Docker daemon 两个核心组件构成。 …

linux驱动开发——内核调试技术

目录 一、前言 二、内核调试方法 2.1 内核调试概述 2.2 学会分析内核源程序 2.3调试方法介绍 三、内核打印函数 3.1内核镜像解压前的串口输出函数 3.2 内核镜像解压后的串口输出函数 3.3 内核打印函数 四、获取内核信息 4.1系统请求键 4.2 通过/proc 接口 4.3 通过…

算法:有效的括号(入栈出栈)

时间复杂度 O(n) 空间复杂度 O(n∣Σ∣)&#xff0c;其中 Σ 表示字符集&#xff0c;本题中字符串只包含 6 种括号 /*** param {string} s* return {boolean}*/ var isValid function(s) {const map {"(":")","{":"}","["…

List截取指定长度(java截取拼接URL)

场景&#xff1a; N多个参数&#xff0c;截取指定个数&#xff0c;拼接URL public static void main(final String[] args) {int count 0;//每页数量final int pageSize 5;final List<Integer> memberNos ListUtil.toList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13…

python格式化内容

1.字符串格式化: 定义列表 [{"姓名": "张三", "年龄": 18, "性别": "男"}, {"姓名": "里斯李四李斯", "年龄": 18, "性别": "男"}, {"姓名": "斯托夫斯基…

C++知识 抽象基类

抽象基类通常包含至少一个纯虚函数&#xff0c;即一个没有具体实现的虚函数&#xff0c;通过在基类中声明纯虚函数&#xff0c;它强制派生类提供这个函数的具体实现。 通过在类的声明中使用 virtual 关键字和 0 初始化来创建纯虚函数&#xff0c;这样的类就成为抽象基类。以下…

上位机与PLC:ModbusTCP通讯之数据类型转换

前请提要: 从PLC读取的数值,不管是读正负整数还是正负浮点数,读取过来后都会变成UInt16,也就是Ushort类型 一、ushort(UInt16)转成 Int32 源代码方法: //ushort类型转Int32类型的方法private int ushortToInt32(ushort[] date, int start){//先进行判断,长度是否正确…

MySQL_6.MySQL常用创建语句

1.数据库创建,查询,删除 (1)创建一个test数据库 CREATE DATABASE test ; CREATE DATABASE IF NOT EXISTS test; # default character set :默认字符集 CREATE DATABASE IF NOT EXISTS test default character set UTF8; # default collate&#xff1a;默认排序规格 # utf8_g…

前端知识(七)———HTTPS:保护网络通信安全的关键

当谈到网络通信和数据传输时&#xff0c;安全性是一个至关重要的问题。在互联网上&#xff0c;有许多敏感信息需要通过网络进行传输&#xff0c;例如个人身份信息、银行账户信息和商业机密等。为了保护这些信息不被未经授权的人访问和篡改&#xff0c;HTTPS&#xff08;超文本传…

AI:大语言模型LLM

LLM 大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是一种利用大量文本数据进行训练的自然语言处理模型&#xff0c;其评价可以从多个方面进行。 以下是一些主要的评价方面&#xff1a; 语言理解和生成能力&#xff1a;评价大语言模型在自然语言理…

模型评价指标

用训练好的模型结果进行预测&#xff0c;需要采用一些评价指标来进行评价&#xff0c;才可以得到最优的模型 常用的指标&#xff1a; 1.分类任务 ConfusionMatrix 混淆矩阵Accuracy 准确率Precision 精确率Recall 召回率F1 score H-mean值ROC Curve ROC曲线PR …

PostgreSQL pgvector:如何利用向量数据库提升搜索效率和精度

LLMs模型实战教程 文章来源&#xff1a;https://zhuanlan.zhihu.com/p/641516393 Kevin 一、介绍 随着基础模型的兴起&#xff0c;向量数据库的受欢迎程度也飙升。事实上&#xff0c;在大型语言模型环境中&#xff0c;向量数据库也很有用。 在机器学习领域&#xff0c;我们经…