Android 13 - Media框架(27)- ACodec(五)

前面几节我们了解了OMXNodeInstance是如何处理setPortMode、allocateBuffer、useBuffer的,这一节我们再回到ACodec,来看看 ACodec start 的其他部分。

我们首先来回顾一下,ACodec start 的状态切换以及处理的事务,我们用一张不太准确的图来表示:请添加图片描述
可以看到将 OMX 组件设置为 OMX_StateIdle 之后,OMX 组件会等待所有的 buffer 都分配完成,然后将状态设置完成的消息返回给ACodec层。我们之前说OMX再处理这个状态时会处在阻塞的情况,现在看来,每次接收到buffer都去判断一下有没有收到所有buffer应该就好了,可以不需要阻塞等待 buffer 到来。

ACodec 收到 OMX 组件 OMX_StateIdle 状态设置完成的事件后,会继续将 OMX 组件的状态设置为 OMX_StateExecuting,同时将 ACodec 自身的状态切换到 IdleToExecutingState,到这里组件就开始正式运转起来了,同时会将状态设置完成的事件通知到上层。

ACodec 接收到事件后会正式切换到 ExecutingState,开始运行。

这里会有一个问题,OMX 组件和 ACodec 都正式开始运行了,它们是如何运行起来的,所有的buffer是如何被驱动的?

答案就是在 IdleToExecutingState 的 onOMXEvent 中,在切换到 ExecutingState 之前,会先调用 resume 方法,把 input buffer 交给上层,把output buffer都送给 OMX 组件,这样buffer就开始流转了。

正式看 resume 方法之前,我原先有一个疑惑,LoadedState 状态里,将 OMX 组件状态设置为 OMX_StateExecuting,万一 这个状态设定特别快,ACodec 还没有进入到 IdleToExecutingState ,ACodec 就收到 OMX_StateExecuting 设定完成的事件怎么办呢?是不是 ACodec 就无法执行到 resume 方法了呢?答案是杞人忧天了,ACodec 所有的消息是在一个线程中处理的,sendCommand 和 changeState 方法在同一个函数中执行,属于同一条消息的处理过程,在收到OMX_StateExecuting设定完成时,ACodec 状态时一定进入到 IdleToExecutingState 的。

1、resume

void ACodec::ExecutingState::resume() {// 首先判断是否要驱动提交 buffer,只有在 未启动,flush之后才会真正提交if (mActive) {ALOGV("[%s] We're already active, no need to resume.", mCodec->mComponentName.c_str());return;}submitOutputBuffers();// Post all available input buffersif (mCodec->mBuffers[kPortIndexInput].size() == 0u) {ALOGW("[%s] we don't have any input buffers to resume", mCodec->mComponentName.c_str());}for (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); i++) {BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);if (info->mStatus == BufferInfo::OWNED_BY_US) {postFillThisBuffer(info);}}mActive = true;
}

我们在上文中已经讲过了,ACodec 通过调用 resume 来驱动 buffer 流转,调用postFillThisBuffer 将 input buffer 传递给 MediaCodec,调用 submitOutputBuffers 将 output buffer 传递给 OMX 组件。

1.1、 submitOutputBuffers

void ACodec::ExecutingState::submitOutputBuffers() {submitRegularOutputBuffers();if (mCodec->storingMetadataInDecodedBuffers()) {submitOutputMetaBuffers();}
}

submitOutputBuffers 分为两个步骤,首先会调用 submitRegularOutputBuffers,翻译过来就是 提交常规的 output buffer,这里有个问题,什么是常规 output buffer呢?来看代码:

void ACodec::ExecutingState::submitRegularOutputBuffers() {bool failed = false;for (size_t i = 0; i < mCodec->mBuffers[kPortIndexOutput].size(); ++i) {BufferInfo *info = &mCodec->mBuffers[kPortIndexOutput].editItemAt(i);// 有 surface(native window)if (mCodec->mNativeWindow != NULL) {// output buffer必须归属于 ACodec 或者是 native windowif (info->mStatus != BufferInfo::OWNED_BY_US&& info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {ALOGE("buffers should be owned by us or the surface");failed = true;break;}// 如果归属于 native window 则直接退出if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {continue;}} else {// 没有 surface 的情况下,output buffer应该归属于 ACodecif (info->mStatus != BufferInfo::OWNED_BY_US) {ALOGE("buffers should be owned by us");failed = true;break;}}ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID);// 打印 log 检查 fenceinfo->checkWriteFence("submitRegularOutputBuffers");// 提交 output buffer 给 OMX 组件status_t err = mCodec->fillBuffer(info);if (err != OK) {failed = true;break;}}// 如果以上过程出现异常则直接报错if (failed) {mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);}
}

首先 output buffer 分为有 Surface 和 无Surface 两种情况,这两种情况下buffer 分别来自于:

  • Surface:在 native window 中分配,归属于 OWNED_BY_NATIVE_WINDOW;
  • no Surface:在 ACodec 中分配,共享内存, 归属于 OWNED_BY_US;

有 surface 的情况下,会检查BufferInfo的归属,如果有buffer归属于组件直接报错,如果有 buffer 归属于 nativewindow,说明graphic buffer还未分配,不需要进行传递。执行到最后我们会发现,BufferInfo 归属于 ACodec 时,该buffer会被传递给 OMX 组件。

没有 surface 的情况下,会检查 BufferInfo 是否归属于 ACodec,如果不是则直接报错。执行到最后会把属于 ACodec 的output buffer 全部传递给 OMX 组件。

综上我们可以得知,BufferInfo 中的 graphic buffer已经被分配,和普通的 output buffer 被认为是 RegularOutput,这两种buffer在一开始就会直接被传递给 OMX 组件。

还有两个问题要注意,初始状态下为什么有surface时,output buffer可能有两个归属呢?这是因为使用有两种情况,我们这里只要求了解 dynamic native window buffer,也就是 BufferInfo 一开始归属于 native window的情况。

这里出现的 fence 我们后面再了解。

1.2、 submitOutputBuffers

上面讲了 regular buffer 是如何提交的,接下来要讲其他buffer是如何提交的。这里的其他指的就是 BufferInfo 中的 graphic buffer 还未分配的情况,之所以要单独拎出来,是因为在提交之前还需要获取 graphic buffer。

调用 submitOutputMetaBuffers 之前要先判断是不是 metadata mode(kPortModeDynamicANWBuffer),我们之前也讲过了,这种模式下,output buffer是动态分配的,一开始是归属于native window的,如果不是这种情况就可以跳过了。

void ACodec::ExecutingState::submitOutputMetaBuffers() {// submit as many buffers as there are input buffers with the codec// in case we are in port reconfiguringfor (size_t i = 0; i < mCodec->mBuffers[kPortIndexInput].size(); ++i) {BufferInfo *info = &mCodec->mBuffers[kPortIndexInput].editItemAt(i);if (info->mStatus == BufferInfo::OWNED_BY_COMPONENT) {if (mCodec->submitOutputMetadataBuffer() != OK)break;}}if (mCodec->mIsLowLatency) {maybePostExtraOutputMetadataBufferRequest();}// *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED ***mCodec->signalSubmitOutputMetadataBufferIfEOS_workaround();
}

看完这段我们要直呼好家伙了,在调用 submitOutputMetadataBuffer 之前会判断 BufferInfo 是否归属于 OWNED_BY_COMPONENT,我们之前刚说过初始状态下,BufferInfo 是归属于 OWNED_BY_NATIVE_WINDOW,也就是说启动一开始,OMX组件是不会获得真正的output buffer的,是不是和我们预期的不一样了呢…

output buffer如何被送给 OMX 组件我们后面会了解到的。

1.3、 postFillThisBuffer

初始状态下,把input buffer送给MediaCodec之前会检查BufferInfo 是否归属于 ACodec,如果是则调用postFillThisBuffer:

void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {// 检查端口是否已经收到eosif (mCodec->mPortEOS[kPortIndexInput]) {return;}CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);// 给 input buffer 设置初始formatinfo->mData->setFormat(mCodec->mInputFormat);// 提交给 ACodecmCodec->mBufferChannel->fillThisBuffer(info->mBufferID);// 解除 BufferInfo 对 MediaCodecBuffer 的引用info->mData.clear();// 设置bufferinfo 状态info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}
  1. 检查端口是否已经收到eos,如果是则不会把input buffer回传给上层;
  2. 将默认的input format 设置给 mediaCodecBuffer;
  3. 将 MediaCodecBuffer 传递给 MediaCodec;
  4. 解除 BufferInfo 对 MediaCodecBuffer 的引用,填充数据期间,ACodec无法使用该buffer
  5. 将 BufferInfo 状态设置为 OWNED_BY_UPSTREAM,表示input buffer送给上层填充

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

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

相关文章

嵌入式开发——ADC开发

学习目标 了解ADC开发流程掌握采样方式能够使用ADC进行芯片内部通道进行采样能够使用ADC对外部电路进行采样学习内容 GD32F4的ADC 特点: 16个外部模拟输入通道;1个内部温度传感通道(VSENSE);1个内部参考电压输入通道(VREFINT);1个外部监测电池VBAT供电引脚输入通道。ADC开…

00-Git 详解

Git 应用 一、Git概述 1.1 什么是Git git 是一个代码协同管理工具&#xff0c;也称之为代码版本控制工具&#xff0c;代码版本控制或管理的工具用的最多的&#xff1a; svn、 git。 SVN 是采用的 同步机制&#xff0c;即本地的代码版本和服务器的版本保持一致&#xff08;提…

Modbus RTU转Modbus TCP模块,RS232/485转以太网模块,YL102 多功能串口服务器模块

特点&#xff1a; ● Modbus RTU协议自动转换成Mobus TCP协议 ● 100M高速网卡&#xff0c;10/100M 自适应以太网接口 ● 支持 AUTO MDI/MDIX&#xff0c;可使用交叉网线或平行网线连接 ● RS232波特率从300到256000可设置 ● 工作方式可选择TCP Server, TCP Client, U…

【JavaScript】浮点数精度问题

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

【数据结构】 常见的八大排序算法

概述 排序有内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;这里八大排序就是内部排序&#xff0c;指直接插入&#xff0c;希尔&#xff0c;选择&#xff0c;堆排&#xff0c;冒泡&#xff0c;快排&#xff0c;归并&#xff0c;计数。 下面让我…

跨域是什么,如何解决跨域

文章目录 前言一、 什么是跨域&#xff1f;二、常见跨域问题三、如何解决跨域JSONP 和 CORS 跨域原理如何解决跨域&#xff08;方式&#xff09;前端解决跨域问题CORS反向代理JSONP 总结 前言 跨域是在开发中经常遇到的问题&#xff0c;那什么是跨域呢&#xff1f;及常见跨域的…

Spring Cloud Gateway + Nacos 实现动态路由

1、maven 依赖 主要依赖 <!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>案件差不多完整主要依赖 <!--Spring boot 依赖(微服务基…

Qt 5.9.4 转 Qt 6.6.1 遇到的问题总结(一)

最近公司对大家的开发的硬件环境进行了升级&#xff0c;电脑主机的配置、显示器&#xff08;两台大屏显示器&#xff09;变得的逼格高多了。既然电脑上的开发环境都需要重装&#xff0c;就打算把开发环境也升级到最新版本&#xff0c;要用就用最新版本。下面对升级后的开发环境…

10 款顶级的免费U盘数据恢复软件(2024 年 更新)

你曾经遇到过U盘无法访问的情况吗&#xff1f;现在我们教你如何恢复数据。 在信息时代&#xff0c;数据丢失往往会造成巨大的困扰。而USB闪存驱动器作为我们常用的数据存储设备&#xff0c;其重要性不言而喻。但是&#xff0c;U盘也可能会出现各种问题&#xff0c;如无法访问、…

C语言-第十七周做题总结-数组2

id&#xff1a;464 A.求矩阵各行元素之和 题目描述 本题要求编写程序&#xff0c;求一个给定的mn矩阵各行元素之和。 输入 输入第一行给出两个正整数m和n&#xff08;1≤m, n≤6&#xff09;。随后m行&#xff0c;每行给出n个整数&#xff0c;其间以空格分隔。 输出 每行…

3DV 2024 Oral | SlimmeRF:可动态压缩辐射场,实现模型大小和建模精度的灵活权衡

目前大多数NeRF模型要么通过使用大型模型来实现高精度&#xff0c;要么通过牺牲精度来节省内存资源。这使得任何单一模型的适用范围受到局限&#xff0c;因为高精度模型可能无法适应低内存设备&#xff0c;而内存高效模型可能无法满足高质量要求。为此&#xff0c;本文研究者提…

游戏任务系统实现思路

文章目录 一、需求介绍二、数据库设计3、代码部分实现 一、需求介绍 1、首先任务的类型不同&#xff0c;可以分为&#xff1a;日常任务、成长任务、活动任务等等。 2、当达到任务目标时&#xff0c;自动发放任务奖励。 3、任务需要后台可配置&#xff0c;例如&#xff1a;任务…