Android 13 - Media框架(28)- 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.rhkb.cn/news/225220.html

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

相关文章

《LIO-SAM阅读笔记》1.IMU预积分模块

前言&#xff1a; LIO-SAM是一个多传感器融合的紧耦合SLAM框架&#xff0c;融合的传感器类型有雷达、IMU和GPS&#xff0c;其中雷达和IMU在LIO-SAM框架中必须使用的。LIO-SAM的优化策略采用了GTSAM库&#xff0c;GTSAM库采用了因子图的优化方法&#xff0c;其提供了一些列C的外…

华为——使用ACL限制内网主机访问外网网站示例

组网图形 图1 使用ACL限制内网主机访问外网网站示例 ACL简介配置注意事项组网需求配置思路操作步骤配置文件 ACL简介 访问控制列表ACL&#xff08;Access Control List&#xff09;是由一条或多条规则组成的集合。所谓规则&#xff0c;是指描述报文匹配条件的判断语句&#…

BIT-6-指针(C语言初阶学习)

1. 指针是什么 2. 指针和指针类型 3. 野指针 4. 指针运算 5. 指针和数组 6. 二级指针 7. 指针数组 1. 指针是什么&#xff1f; 指针是什么&#xff1f; 指针理解的2个要点&#xff1a; 指针是内存中一个最小单元的编号&#xff0c;也就是地址平时口语中说的指针&#xff0c;通常…

Xcode 编译速度慢是什么原因?如何提高编译速度?

作为一个开发者&#xff0c;我们都希望能够高效地开发应用程序&#xff0c;而编译速度是影响开发效率的重要因素之一。然而&#xff0c;有时候我们会发现在使用 Xcode 进行开发时&#xff0c;译速度非常慢&#xff0c;这给我们带来了不少困扰。那么&#xff0c;为什么 Xcode 的…

Mybatis之Mapper动态代理方式

目录 一、 Mapper动态代理 二.、Mapper动态代理规范 三、Mapper.xml映射文件 1.在src目录下创建mapper文件&#xff0c;在mapper文件下定义mapper接口 2、在StudentMapper接口中编写方法 3、Mapper.xml(映射文件) 四、测试 的Mapper动态代理开发规范 Mapper接口开发方法…

新版IDEA中Git的使用(三)

说明&#xff1a;前面介绍了在新版IDEA中Git的基本操作、分支操作&#xff0c;本文介绍一下在新版IDEA中&#xff0c;如何回滚代码&#xff1b; 分以下三个阶段来介绍&#xff1a; 未Commit的文件&#xff1b; 已经Commit&#xff0c;但未push的文件&#xff1b; 已经push的…

为什么深度学习神经网络可以学习任何东西

下图你所看到的&#xff0c;是著名的曼德尔布罗特集&#xff0c;我们可以见证这个集合呈现出的复杂形态&#xff1a; 要理解神经网络如何学习曼德尔布罗特集&#xff0c;我们首先需要从最基础的数学概念讲起&#xff1a;什么是函数&#xff1f;函数本质上是一个将输入转化为输出…

uniapp APP应用程序iOS没有上架到苹果应用商店如何整包更新?

随着移动互联网的快速发展&#xff0c;uni-app 作为一种跨平台开发框架&#xff0c;受到了广泛欢迎。然而&#xff0c;有时候开发者可能会遇到一个问题&#xff1a;如何为已经发布到苹果应用商店的 uni-app APP 进行整包更新&#xff1f;尤其是当应用还没有上架到苹果应用商店时…

网络通信协议

WebSocket通信 WebSocket是一种基于TCP的网络通信协议&#xff0c;提供了浏览器和服务器之间的全双工通信&#xff08;full-duplex&#xff09;能力。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接&#xff…

UDP信号多个电脑的信息传输测试、配置指南

最近要做一个东西&#xff0c;关于一个软件上得到的信号&#xff0c;如何通过连接的局域网&#xff0c;将数据传输出去。我没做过相关的东西&#xff0c;但是我想应该和软件连接数据库的过程大致是差不多的&#xff0c;就一个ip和一个端口号啥的。 一.问题思路 多个设备同时连…

万界星空科技生产管理MES系统中的工时管理

工时管理的重大意义 1.提高生产效率 通过工时管理&#xff0c;企业可以更加精确地掌握研发人员的工时情况&#xff0c;及时调整项目进度和人力安排&#xff0c;提高生产效率。 2.降低人力成本 通过工时管理&#xff0c;企业可以更加精确地核算研发人员的工时费用&#xff0c…

【面试】Java中的多种设计模式(十种主要设计模式)

Java中的多种设计模式&#xff08;十种主要设计模式&#xff09; 文章概述 设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。它是软件工程中常见问题的解决方案的一种描述或模板。设计模式可以提供一种通用的、可重用的解决方案&#xff0c;帮助开发…

你好!Apache Seata

北京时间 2023 年 10 月 29 日&#xff0c;分布式事务开源项目 Seata 正式通过 Apache 基金会的投票决议&#xff0c;以全票通过的优秀表现正式成为 Apache 孵化器项目&#xff01; 根据 Apache 基金会邮件列表显示&#xff0c;在包含 13 个约束性投票 (binding votes) 和 6 个…

生存分析序章2——生存分析之Python篇:lifelines库入门

目录 写在开头1. 介绍 lifelines 库1.1 lifelines库简介1.2 安装与环境配置 2. 数据准备2.1 数据格式与结构2.2 处理缺失数据2.3 对异常值的处理 3. Kaplan-Meier 曲线3.1 使用 lifelines 绘制生存曲线3.2 曲线解读3.3 额外补充 4. Cox 比例风险模型4.1 lifelines 中的 Cox 模型…

使用python netmiko模块批量配置Cisco、华为、H3C路由器交换机(支持 telnet 和 ssh 方式)

0. 当前环境 外网电脑Python版本&#xff1a;3.8.5&#xff08;安装后不要删除安装包&#xff0c;以后卸载的时候用这个&#xff09;外网电脑安装netmiko第三方库&#xff1a;cmd中输入pip install netmiko内网电脑环境&#xff1a;无法搭建python环境&#xff0c;需外网电脑完…

Yolov5水果分类识别+pyqt交互式界面

Yolov5 Fruits Detector Yolov5 是一种先进的目标检测算法&#xff0c;可以应用于水果分类识别任务。结合 PyQT 框架&#xff0c;可以创建一个交互式界面&#xff0c;使用户能够方便地上传图片并获取水果分类结果。以下将详细阐述 Yolov5 水果分类识别和 PyQT 交互式界面的实现…

C/C++常见面试题(四)

C/C面试题集合四 目录 1、什么是C中的类&#xff1f;如何定义和实例化一个类&#xff1f; 2、请解释C中的继承和多态性。 3、什么是虚函数&#xff1f;为什么在基类中使用虚函数&#xff1f; 4、解释封装、继承和多态的概念&#xff0c;并提供相应的代码示例 5、如何处理内…

【Azure 架构师学习笔记】- Power Platform(1) - 简介

本文属于【Azure 架构师学习笔记】系列。 本文属于【Power Platform】系列。 Power Platform 它是一个SaaS平台&#xff0c;支持和延伸M365&#xff0c; Dynamics 365和Azure甚至其他第三方服务。主要提供低代码&#xff0c;自动化&#xff0c;数据驱动和定制化业务逻辑的服务…

【开源】基于Vue+SpringBoot的新能源电池回收系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户档案模块2.2 电池品类模块2.3 回收机构模块2.4 电池订单模块2.5 客服咨询模块 三、系统设计3.1 用例设计3.2 业务流程设计3.3 E-R 图设计 四、系统展示五、核心代码5.1 增改电池类型5.2 查询电池品类5.3 查询电池回…

65内网安全-域环境工作组局域网探针

这篇分为三个部分&#xff0c;基本认知&#xff0c;信息收集&#xff0c;后续探针&#xff0c; 基本认知 分为&#xff0c;名词&#xff0c;域&#xff0c;认知&#xff1b; 完整架构图 名词 dwz称之为军事区&#xff0c;两个防火墙之间的区域称之为dwz&#xff0c;但安全性…