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.mzph.cn/news/579877.shtml

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

相关文章

【第2讲】原理介绍和权限开通

系列文章目录 第1讲:Python环境的下载和安装第2讲:免费开通权限第3讲:1行代码,自动发正文第4讲:1行代码,自动发正文+附件第5讲:自动批量发送第6讲:1行代码,自动下载邮件的附件提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录…

「GPT」G、P、T分别是啥意思?

G意为Generative &#xff1a;生成式 比如&#xff0c;生成式的分类器&#xff08;模型&#xff09;包括---- generative classifiers: naive Bayes classifier and linear discriminant analysis 与之对应的为判别式----- discriminative model: logistic regression P意为…

基于 Linux 的批量上传本地 Git 仓库到 Github 的实践

基于 Linux 的批量上传本地 Git 仓库到 Github 的实践 一、需求二、上传本地 Git 仓库2.1 初始版本2.2 优化版本 三、 GitHub 创建空仓库3.1 初始版本3.2 优化版本 一、需求 app目录下的每个文件夹都是一个git仓库&#xff0c;如何使用shell脚本将所有git仓库都上传到github上…

VOL_dev问题随记----question++!!!

目录 1.问题&#xff1a;代码生成&#xff0c;快捷编辑不生效(linktrue没用)2.问题&#xff1a;代码生成后&#xff0c;后端报文件以重复定义3.问题&#xff1a;控制台报using无法引用类包4.问题&#xff1a;代码生成后&#xff0c;生成业务类库后无效&#xff0c;系统出现未找…

什么是redis雪崩

Redis雪崩是指在使用Redis作为缓存数据库时&#xff0c;由于某种原因导致Redis服务器不可用或性能严重下降&#xff0c;从而导致大量的请求集中到数据库服务器上&#xff0c;甚至直接导致数据库服务器崩溃。 当Redis服务器出现雪崩时&#xff0c;原本应该被缓存的数据无法从缓…

Android Audio实战——AudioTrack分析(二十六)

AudioTrack 是用于播放音频数据的类,可以使用 set 方法设置音频数据,然后使用 start 来启动播放。而 pause 和 stop 则是用于控制播放过程的方法。不管 APP 使用什么播放音频文件,最终都是由 AudioTrack 将数据传递下去。 一、源码分析 1、主要方法 构造函数:创建 AudioT…

GPT在企业自动化方面的应用

GPT和类似的自然语言处理技术在企业自动化方面有许多应用。以下是一些主要的应用领域&#xff1a; 自动化客户支持&#xff1a; 使用GPT可以构建自动化的客户支持系统&#xff0c;能够理解和生成自然语言文本&#xff0c;回答常见问题&#xff0c;解决用户的疑问和问题。 自动…

记一次Mac端mysql重置密码

在执行mysql命令的时候&#xff0c;报如下的错误&#xff0c;表示不支持mysql命令&#xff1a; zsh: command not found: mysql 1. 先查看mysql服务是否存在 在系统偏好设置中查看&#xff1a; 2. 发现mysql服务已经在运行&#xff0c;可能因为/usr/local/bin目录下缺失mysq…

如何使用设计模式来解决类与类之间调用过深的问题。

我们将使用责任链模式和装饰者模式的组合。 考虑一个简化的餐厅订单处理系统&#xff0c;其中包括服务员&#xff08;Waiter&#xff09;、厨师&#xff08;Chef&#xff09;和收银员&#xff08;Cashier&#xff09;。订单从服务员开始&#xff0c;然后传递给厨师&#xff0c…

使用BeanDefinition来构建Spring Bean并注入Spring上下文中

在做中间件产品的时候&#xff0c;为了给业务方一个好用的客户端&#xff0c;我们一般会提供一个自定义的xxx-spring-boot-starter&#xff0c;那么我们就可能涉及到将自己的客户端中的某个类初始化并注入到Spring上下文中去。为了更标准化去初始化这个类&#xff0c;让Spring来…

Windows10系统下CGAL的安装编译

文章目录 环境与依赖库1、环境2、依赖库Boost库安装步骤Qt安装步骤CGAL5.6安装步骤CGAL5.6编译环境与依赖库 1、环境 Windows10系统Visual Studio 20222、依赖库 Boost库版本:1.73Qt库版本:5.12CGAL版本:5.6Boost库安装步骤 boost是CGAL的强制依赖库。 1、下载boost_1_73…

ubuntu22.04搭建RTSP服务器

大致命令如下&#xff1a; git clone --depth 1 gitgithub.com:ZLMediaKit/ZLMediaKit.git sudo apt-get install build-essential sudo apt-get install cmake #除了openssl,其他其实都可以不安装 sudo apt-get install libssl-dev sudo apt-get install libsdl-dev sudo apt…

无法获取前置摄像头的预览图像?【Bug已解决-鸿蒙开发】

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结HarmonyOS和OpenHarmony区别和联系项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了无法获取前置摄像头的预览图像的问题。 问题:前置摄像头…

(一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。

本文简单介绍一下ClickHouse 中的 MaterializedMySQL 数据库引擎的使用方法、设置、特性和限制。以下是具体的内容和步骤&#xff1a; 介绍和使用说明: MaterializedMySQL 是一个实验性的数据库引擎。它允许创建一个 ClickHouse 数据库&#xff0c;其中包含了 MySQL 中的所有表…

linux基于用户身份对资源访问进行控制的解析及过程

linux中用户分为三类 1.超级用户&#xff08;root&#xff09; 拥有至高无上的权限 2.普通用户 人为创建、权限小&#xff0c;权限受到控制 3.程序用户 运行程序的用户&#xff0c;不是给人使用的&#xff0c;给程序使用的&#xff0c;一般不给登录&#xff01; 组账…

【LeetCode刷题笔记(13-1)】【Python】【回文数】【反转整数】【简单】

文章目录 引言回文数题目描述提示 题意分析解决方案1&#xff1a;【反转字符串】解决方案2&#xff1a;【反转整数】题外话结束语 9. 回文数 引言 编写通过所有测试案例的代码并不简单&#xff0c;通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例&#xff0…

【AI】人工智能爆发推进器之卷积神经网络

目录 一、什么是卷积神经网络 1. 卷积层&#xff08;Convolutional Layer&#xff09; 2. 激活函数&#xff08;Activation Function&#xff09; 3. 池化层&#xff08;Pooling Layer&#xff09; 4. 全连接层&#xff08;Fully Connected Layer&#xff09; 5. 训练过程…

html table+css实现可编辑表格

要实现可编辑的 HTML 表格&#xff0c;你可以使用 JavaScript 和 HTML5 的 contenteditable 属性。 <!DOCTYPE html> <html> <head><style>table {border-collapse: collapse;width: 100%;}th, td {border: 1px solid black;padding: 8px;text-align:…

区块链白皮书:基础建设见成效,国产自主生态正发展壮大

“区块链是全球信任机器的重要组成部分”——比尔盖茨 随着科技的飞速发展&#xff0c;区块链技术已成为引领全球创新的重要力量。近日&#xff0c;中国信通院发布了《区块链白皮书&#xff08;2023年&#xff09;》&#xff0c;为我们揭示了这一领域的前沿动态和未来发展趋势…

JDK 14全景透视:每个Java开发者必知的新特性

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 JDK 14全景透视&#xff1a;每个Java开发者必知的新特性 前言&#xff1a;switch表达式标准化Switch表达式成为正式特性的意义&#xff1a;如何使用Switch表达式&#xff1a;注意事项&#xff1a; ins…