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

2023-12-27 08:01
文章标签 android 框架 media 13 28 acodec

本文主要是介绍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送给上层填充

这篇关于Android 13 - Media框架(28)- ACodec(五)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/542419

相关文章

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

Python的端到端测试框架SeleniumBase使用解读

《Python的端到端测试框架SeleniumBase使用解读》:本文主要介绍Python的端到端测试框架SeleniumBase使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录SeleniumBase详细介绍及用法指南什么是 SeleniumBase?SeleniumBase

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component