Android Media Framework(十五)ACodec - Ⅲ

news2024/11/14 21:50:05

这一篇我们一起来了解ACodec的Buffer分配流程。

1、initiateStart

首先对上一篇内容做一点补充,configureCodec执行完成后组件的状态没有变化,仍处在OMX_StateLoaded。因此,当我们调用initiateStart时,发出的消息将由ACodec::LoadedState来处理。

bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case ACodec::kWhatStart:
        {
            onStart();
            handled = true;
            break;
        }
    }
}        

onStart是ACodec::LoadedState内部的方法,调用此方法首先会发送命令给OMX组件,将状态切换至OMX_StateIdle,命令送出成功后就将ACodec状态切换至LoadedToIdleState。

void ACodec::LoadedState::onStart() {
    ALOGV("onStart");

    status_t err = mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
    if (err != OK) {
        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
    } else {
        mCodec->changeState(mCodec->mLoadedToIdleState);
    }
}

命令发出后OMX组件会进入到LoadedToIdleState这个中间状态等待buffer分配。

2、LoadedToIdleState

​ACodec进入到LoadedToIdleState后,buffer分配就开始了。如果分配成功,ACodec会等待组件状态切换完成的消息;如果分配失败则会用callback上抛error,同时清理已经分配的buffer。

void ACodec::LoadedToIdleState::stateEntered() {
    ALOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());

    status_t err;
    if ((err = allocateBuffers()) != OK) {
        ALOGE("Failed to allocate buffers after transitioning to IDLE state "
             "(error 0x%08x)",
             err);

        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
        // ...
        mCodec->changeState(mCodec->mLoadedState);
    }
}

buffer分配通过调用allocateBuffers方法完成:

status_t ACodec::LoadedToIdleState::allocateBuffers() {
    status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
    if (err != OK) {
        return err;
    }

    err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
    if (err != OK) {
        return err;
    }

    mCodec->mCallback->onStartCompleted();

    return OK;
}

ACodec会调用allocateBuffersOnPort先为input端口分配buffer,然后再为output端口分配buffer,因此我们看到的input buffer id总是比output buffer id小。

buffer分配完成后立刻调用CodecCallback通知MediaCodec start执行完成。这里提出一个小小的疑问:为什么没有等OMX组件的event发回来再调用CodecCallback呢?

3、allocateBuffersOnPort

本文将allocateBuffersOnPort分为两部分来了解:

  1. Decoder Output且使用NativeWindow的情况;
  2. 其他情况;

这一节我们先看“其他”,这部分比较简单。

进入函数体,首先会获取要分配的buffer size,这些信息都存储在组件的PortDefinition中,我们可以通过调用getParameter获取端口定义,从中拿到端口使用的buffer size。

    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = portIndex;

    err = mOMXNode->getParameter(
            OMX_IndexParamPortDefinition, &def, sizeof(def));

如果是Encoder且输入要用Metadata,即PortMode为kPortModeDynamicANWBuffer或kPortModeDynamicNativeHandle,这时候input buffer size需要重新计算,大小等于Metadata size。

    const IOMX::PortMode &mode = mPortMode[portIndex];
    size_t bufSize = def.nBufferSize;
    if (mode == IOMX::kPortModeDynamicANWBuffer) {
        bufSize = sizeof(VideoNativeMetadata);
    } else if (mode == IOMX::kPortModeDynamicNativeHandle) {
        bufSize = sizeof(VideoNativeHandleMetadata);
    }

接下来是对buffer size做校验,如果大小超过kMaxCodecBufferSize(8192 * 4096 * 4 bytes)就返回error。这段代码中我们看到DataConverter,这个类是做数据转换用的。

    size_t conversionBufferSize = 0;

    sp<DataConverter> converter = mConverter[portIndex];
    if (converter != NULL) {
        if (portIndex == kPortIndexInput) {
            conversionBufferSize = converter->sourceSize(bufSize);
        } else {
            conversionBufferSize = converter->targetSize(bufSize);
        }
    }

    size_t alignment = 32; 

    if (bufSize == 0 || max(bufSize, conversionBufferSize) > kMaxCodecBufferSize) {
        ALOGE("b/22885421");
        return NO_MEMORY;
    }

    size_t alignedSize = align(bufSize, alignment);
    size_t alignedConvSize = align(conversionBufferSize, alignment);
    if (def.nBufferCountActual > SIZE_MAX / (alignedSize + alignedConvSize)) {
        ALOGE("b/22885421");
        return NO_MEMORY;
    }

下面这部分是在获取Allocator,在PortMode被设置为kPortModePresetSecureBuffer的情况下,会要求组件分配buffer。而在其他情况下,则是使用TAllocator来分配buffer。

    if (mode != IOMX::kPortModePresetSecureBuffer) {
        mAllocator[portIndex] = TAllocator::getService("ashmem");
        if (mAllocator[portIndex] == nullptr) {
            ALOGE("hidl allocator on port %d is null",
                    (int)portIndex);
            return NO_MEMORY;
        }
    }

3.1、BufferInfo

每次循环分配buffer之前都会先创建出一个BufferInfo出来,ACodec用它记录buffer信息和运行过程中buffer所处的状态。

    struct BufferInfo {
        enum Status {
            OWNED_BY_US,
            OWNED_BY_COMPONENT,
            OWNED_BY_UPSTREAM,
            OWNED_BY_DOWNSTREAM,
            OWNED_BY_NATIVE_WINDOW,
            UNRECOGNIZED,            // not a tracked buffer
        };

        static inline Status getSafeStatus(BufferInfo *info) {
            return info == NULL ? UNRECOGNIZED : info->mStatus;
        }

        IOMX::buffer_id mBufferID;
        Status mStatus;
        unsigned mDequeuedAt;

        sp<MediaCodecBuffer> mData;  // the client's buffer; if not using data conversion, this is
                                     // the codec buffer; otherwise, it is allocated separately
        sp<RefBase> mMemRef;         // and a reference to the IMemory, so it does not go away
        sp<MediaCodecBuffer> mCodecData;  // the codec's buffer
        sp<RefBase> mCodecRef;            // and a reference to the IMemory

        sp<GraphicBuffer> mGraphicBuffer;
        bool mNewGraphicBuffer;
        int mFenceFd;
        FrameRenderTracker::Info *mRenderInfo;

        // The following field and 4 methods are used for debugging only
        bool mIsReadFence;
        // Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored.
        void setReadFence(int fenceFd, const char *dbg);
        void setWriteFence(int fenceFd, const char *dbg);
        // Log error, if the current fence is not a read/write fence.
        void checkReadFence(const char *dbg);
        void checkWriteFence(const char *dbg);
    };

BufferInfo的各个字段意义如下:

4、allocateOutputMetadataBuffers

5、allocateOutputBuffersFromNativeWindow

6、IdleToExecutingState

关注公众号《青山渺渺》阅读全文
请添加图片描述

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

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

相关文章

SVN使用教程 - 快速上手

参考视频&#xff1a; SVN使用教程 - 快速上手 一、SVN简介 1、SVN的功能 &#xff08;1&#xff09;SVN是一种代码版本管理工具&#xff0c;它能记住程序员每次修改的内容&#xff0c;可以查看所有的历史修改记录&#xff0c;可以将代码恢复到任何历史版本&#xff0c;可以恢…

【Mysql】第十一章 事务-重点(原子性+持久性+隔离性+一致性)

文章目录 一、概念1.查看事务支持版本-show engines2.事务提交方式-show variables like autocommit3.事务常见操作方式1.将mysql的默认隔离级别设置成读未提交&#xff0c;方便看到实验现象2.需要重启终端&#xff0c;进行查看隔离级别3.创建一个银行用户表4.演示 - 证明事务的…

【Transformer】关于RNN以及transformer的相关介绍

文章目录 RNNTransformer是干什么的&#xff1f;什么是 Word Embedding &#xff1f;什么是 Word2vec &#xff1f;CBOW(Continuous Bag-of-Words Model)Skip-gram(Continuous Skip-gram Model)Word2vec 的优缺点 Transformer整体架构注意力机制self-attention&#xff08;自注…

生成式人工智能助力6G核心技术

崔曙光 加拿大皇家科学院 加拿大工程院双院院士 主要工作&#xff1a;适配改造人工智能算法&#xff0c;来满足通信网络性能 从基础LLM到专用LLM&#xff1a;四个必须面对的问题 如何选择合适的基础LLM规模如何让基础LLM读懂专用领域信息如何避免基础LLM的幻觉现象&#xf…

第9天 xxl-job

使用xxl-job需要建表 引入依赖 添加配置 Bean public XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor new XxlJobSpringExecutor();xxlJo…

sql注入——sqlilabs16-26

文章目录 less-163.注入 less-172.数据库名2.1 floor报错注入数据库名 3.查到数据表3.1floor 报错注入数据表 4.查取列名4.1 floor报错注入 列名 5.查取内容 less-181.添加X-Forwarded-For测试2修改User-Agent测试3.查数据表名4.查数据列5.查取数据 less-192.查数据库3.查数据表…

医疗大健康解决方案HIS方案

本篇接上篇文章医疗大健康解决方案HIS方案-CSDN博客&#xff0c;介绍第二部分区域医疗解决方案。 依托腾讯云优势&#xff0c;联合合作伙伴&#xff0c;连接政府、医疗服务机构、医药研发与流通、康养等&#xff0c;构建医疗大健康产业云生态&#xff0c;助力数字化升级。 方…

小怡分享之数据结构基础知识准备

前言&#xff1a; &#x1f308;✨之前小怡给大家分享了JavaSE的知识&#xff0c;今天小怡要给大家分享一下数据结构基础知识。 一、初识集合框架 1.什么是集合框架 Java集合框架Java Collection Framework&#xff0c; 又称为容器container&#xff0c;是定义在Java.util 包…

Linux服务器基于NFS实现共享目录

NFS简介&#xff1a;NFS&#xff08;Network File System&#xff09;是一种分布式文件系统协议&#xff0c;允许用户通过网络访问远程计算机上的文件和目录&#xff0c;就像访问本地文件一样。NFS 最初由 Sun Microsystems 在 1984 年开发&#xff0c;现在已经成为类 Unix 系统…

SpringBoot企业人事管理系统-附源码与配套论文

1.1引言 随着计算机技术的飞速发展&#xff0c;计算机在各种单位机构管理中应用的普及﹐管理信息系统的开发在强调管理、强调信息的现代社会中也显得越来越重要。因此,利用计算机高效率地完成人事管理的日常事务&#xff0c;是适应现代各种单位机构制度要求、推动各种单位机构…

【项目】火灾烟雾检测管理系统。PyQT5+QT Designe+YOLOv8_ssod半监督算法+OpenCV

【项目】火灾烟雾检测管理系统。PyQT5QT DesigneYOLOv8_ssod半监督算法OpenCV 0.摘要1.引言2.烟雾检测算法2.0图像标注2.1 YOLOv8全监督算法结构2.2 Efficient-Teacher半监督算法结构 3.性能对比图4.源码、论文获取 0.摘要 火灾是常见而危险的自然灾害&#xff0c;不仅对人类生…

数值分析【3】

目录 第四章 插值 边角料&#xff1a; 分段二次插值——三个一插​编辑 三次样条插值 小结&#xff1a;等距看差分​编辑 第五章 最小二乘 第六章 数值积分 代数精度​编辑 第四章 插值 边角料&#xff1a; 分段二次插值——三个一插 三次样条插值 三次阳台函数是光滑…

Oracle一对多(一主多备)的DG环境如何进行switchover切换?

本文主要分享Oracle一对多(一主多备)的DG环境的switchover切换&#xff0c;如何进行主从切换&#xff0c;切换后怎么恢复正常同步&#xff1f; 1、环境说明 本文的环境为一主两备&#xff0c;数据库版本为11.2.0.4&#xff0c;主要信息如下&#xff1a; 数据库IPdb_unique_n…

落子“用户Happy”,vivo的“做活”与“长气”之道

有人说&#xff0c;中国手机行业&#xff0c;是名副其实的“Hard”模式。竞争焦灼&#xff0c;内卷不止。然而&#xff0c;这种主观的判断&#xff0c;也许从侧面反映出另一个客观事实&#xff1a;中国手机市场&#xff0c;凭借巨大的用户规模、多元化的消费倾向、自由展开的科…

从微软蓝屏事件聊到数据库系统中的纸牌屋

2024 年 7 月 19 日&#xff0c;全球约有 850 万台 Windows 电脑崩溃&#xff0c;无法重启&#xff0c;陷入蓝屏死机状态。这次故障影响了全球各地的企业和政府&#xff0c;波及运输、金融服务、医疗保健等绝大多数行业。 故障发生几小时后&#xff0c;蓝屏原因找到&#xff0…

Python 数组计算逻辑

a{1,2,3} b{2,3,4} 与 & 交集(取中) a&b{2, 3} 或 | 并集 (左中右) a&b{1,2,3,4} 差集 ^ 取左右 a^b {1,4} 减 - 取左 a - b {1} a-b {1}

同态加密和SEAL库的介绍(二)BFV 基础方案实现

写在前面&#xff1a; 本篇具体讲解如何使用 BFV 加密方案对加密的整数进行简单的计算&#xff08;一个多项式评估&#xff09;&#xff0c;来源是官方提供的示例。BFV 是比较常见的方案&#xff0c;在很多大模型推理的时候&#xff0c;都是将浮点数的权重和输入变换成…

新品周销量20W+,月GMV1300W+,黑马品牌如何实现快速突围?

随着视频号用户的不断增加&#xff0c;直播带货生态的不断发展&#xff0c;越来越多的品牌也开始入局视频号。 近期友望数据发现&#xff0c;不少新品牌在视频号上脱颖而出。比如服饰内衣行业品牌「瑰菲女神」&#xff0c;专注女性内衣裤行业&#xff0c;周销量近20W件&#xf…

Java 并发(二)—— AQS原理

AQS&#xff0c;全名AbstractQueuedSynchronizer。 抽象队列同步器定义多线程访问共享资源的同步模板&#xff0c;解决了实现自定义同步器时涉及的大量细节问题&#xff0c;简化开发两种同步状态&#xff1a;独占、共享核心组件&#xff1a;State变量、CLH变体队列、获取 / 释…

Leetcode每日刷题之75. 颜色分类(C++)

有接触过数据结构的同学应该知道排序有很多种类&#xff0c;我之前也出过一篇 排序大杂烩 的博客&#xff0c;其中包含了一部分排序的讲解&#xff0c;排序在我们学习编程的过程中有着至关重要的作用&#xff0c;不论是大部分新手刚开始接触的冒泡排序还是C库中的sort函数&…