Android 13 - Media框架(23)- ACodecBufferChannel

news2024/11/24 15:09:23

这一节我们将了解 ACodecBufferChannel

上一节我们了解到input buffer 和 output buffer 是如何分配的了,allocateBuffersOnPort 方法的最后会将ACodec::BufferInfo 中的 mData 成员组织成为数组,最后提交给 ACodecBufferChannel 管理。这一节我们将尝试了解ACodecBufferChannel 的作用,以及 ACodec::BufferInfo 中部分成员的作用。

1、ACodecBufferChannel

ACodecBufferChannel 介于 MediaCodec 和 ACodec之间,我们之前了解过 BufferChannelBase 的接口,基类中的接口主要是给 MediaCodec 调用的,我们后面要了解的CCodec中的BufferChannel同样也是继承于该基类。ACodecBufferChannel起着中介的作用,降低了 MediaCodec 和 ACodec 的耦合度,不过我还是会想为什么要有 BufferChannel 呢?除了降低耦合还干了什么呢?其实 BufferChannel 还有另外的功能,那就是解扰(descrambler)和解密(decrypto)的作用,只不过是我们平时不常用,所以就忽略它们了。解扰和解密的流程放在 BufferChannel 中可以简化 ACodec 的内容,让分工更加明确。

回到 ACodecBufferChannel 中来,头文件中声明的函数分为两部分,一部分是给MediaCodec使用的(BufferChannelBase中的接口),另一部分是给ACodec调用的,我们这里主要来看给ACodec调用的接口。

    struct BufferInfo {
        BufferInfo(
                const sp<MediaCodecBuffer> &buffer,
                IOMX::buffer_id bufferId,
                const sp<IMemory> &sharedEncryptedBuffer);

        BufferInfo() = delete;

        // Buffer facing MediaCodec and its clients.
        const sp<MediaCodecBuffer> mClientBuffer;
        // Buffer facing CodecBase.
        const sp<MediaCodecBuffer> mCodecBuffer;
        // OMX buffer ID.
        const IOMX::buffer_id mBufferId;
        // Encrypted buffer in case of secure input.
        const sp<IMemory> mSharedEncryptedBuffer;
    };

ACodeBufferChannel 中也声明了一个 BufferInfomCodecBuffer 指向的是ACodec中传递的mData,mClientBuffer指向的是回传给 MediaCodec 的 buffer,未什么要分成两个部分我们后面再说。

先来看 ACodec 是如何调用 setInputBufferArray 把 buffer 交给ACodecBufferChannel管理的,setInputBufferArray传入参数为一个BufferAndId数组,将MediaCodecBuffer和buffer-id组成成为BufferAndId,然后再传入ACodecBufferChannel。

void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
	...
    std::vector<const BufferInfo> inputBuffers;
    for (const BufferAndId &elem : array) {
        sp<IMemory> sharedEncryptedBuffer;
        if (hasCryptoOrDescrambler()) {
        	// 分配用于解密的buffer
            sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
        }
        // 创建ACodec::BufferInfo
        inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
    }
    std::atomic_store(
            &mInputBuffers,
            std::make_shared<const std::vector<const BufferInfo>>(inputBuffers));
}

ACodecBufferChannel::BufferInfo::BufferInfo(
        const sp<MediaCodecBuffer> &buffer,
        IOMX::buffer_id bufferId,
        const sp<IMemory> &sharedEncryptedBuffer)
    : mClientBuffer(
          (sharedEncryptedBuffer == nullptr)
          ? buffer
          : new SharedMemoryBuffer(buffer->format(), sharedEncryptedBuffer)),
      mCodecBuffer(buffer),
      mBufferId(bufferId),
      mSharedEncryptedBuffer(sharedEncryptedBuffer) {
}

setInputBufferArray 中会判断是否需要解密或者解扰,这些内容是在 MediaCodec configure过程中设定的,如果需要会分配出一块用于解密的buffer,最后会创建一个ACodec::BufferInfo对象。继续来看BufferInfo的构造函数,如果sharedEncryptedBuffer这个参数不为NULL,那么mClientBuffer将会使用这块新分配的用于解密的buffer,否则就直接使用ACodec传来的MediaCodecBuffer。

到这我们应该就可以猜测了,如果上层需要对input进行解密/解扰,那么数据也需要加密传输,因此需要一块受保护的buffer,因此需要单独分配buffer给上层使用(buffer的拷贝过程也是加密的)。上层把填好数据的buffer(mClientBuffer)送给 BufferChannel,在这里会对数据进行解密,再将解密后的数据拷贝要input buffer当中(mCodecBuffer),最后送给 OMX 组件,因此这里使用两个MediaCodecBuffer来处理需要加密解密的情况,这种情况下mClientBuffer和mCodecBuffer指向的内容是不同的。

但是如果只是普通播放,那么mClientBuffer和mCodecBuffer会指向同一个地址,使用 == 判断是就会返回 true 了。

我们去阅读 ACodecBufferChannel::queueInputBuffer 和 ACodecBufferChannel::queueSecureInputBuffer 大致能能理解上面的意思了:

status_t ACodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
    std::shared_ptr<const std::vector<const BufferInfo>> array(
            std::atomic_load(&mInputBuffers));
    BufferInfoIterator it = findClientBuffer(array, buffer);
    if (it == array->end()) {
        return -ENOENT;
    }
    if (it->mClientBuffer != it->mCodecBuffer) {
        // Copy metadata from client to codec buffer.
        it->mCodecBuffer->meta()->clear();
        int64_t timeUs;
        CHECK(it->mClientBuffer->meta()->findInt64("timeUs", &timeUs));
        it->mCodecBuffer->meta()->setInt64("timeUs", timeUs);
        int32_t eos;
        if (it->mClientBuffer->meta()->findInt32("eos", &eos)) {
            it->mCodecBuffer->meta()->setInt32("eos", eos);
        }
        int32_t csd;
        if (it->mClientBuffer->meta()->findInt32("csd", &csd)) {
            it->mCodecBuffer->meta()->setInt32("csd", csd);
        }
    }
    ALOGV("queueInputBuffer #%d", it->mBufferId);
    sp<AMessage> msg = mInputBufferFilled->dup();
    msg->setObject("buffer", it->mCodecBuffer);
    msg->setInt32("buffer-id", it->mBufferId);
    msg->post();
    return OK;
}

我们来看普通模式,这时候mClientBuffer和mCodecBuffer中的mData指向的内容是相同的,所以不需要做buffer拷贝,但是可能有人又要问了,之前不是说 == 判断 是返回true的吗,这里要注意,== 返回true指的是把 MediaCodecBuffer 中的 mBuffer 进行比较。

简单来说是数据块相同,但是 meta 信息还是独立的,所以每次调用 queueInputBuffer 都是要拷贝 meta 数据的。

queueSecureInputBuffer 比较长,这里就不贴代码了,这里面主要做的就是把secure input buffer 中的数据安全的拷贝到 ACodec input buffer 当中。

这里抛出一个问题,是否使用 queueSecureInputBuffer 来输入 buffer,那么就一定用的 secure 组件呢?答案不是的。

    } else if (mCrypto != NULL) {
        hardware::drm::V1_0::DestinationBuffer destination;
        if (secure) {
            destination.type = DrmBufferType::NATIVE_HANDLE;
            destination.secureMemory = hidl_handle(secureHandle);
        } else {
            destination.type = DrmBufferType::SHARED_MEMORY;
            IMemoryToSharedBuffer(
                    mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
        }

        hardware::drm::V1_0::SharedBuffer source;
        IMemoryToSharedBuffer(it->mSharedEncryptedBuffer, mHeapSeqNum, &source);

        result = mCrypto->decrypt(key, iv, mode, pattern,
                source, it->mClientBuffer->offset(),
                subSamples, numSubSamples, destination, errorDetailMsg);

        if (result < 0) {
            return result;
        }

        if (destination.type == DrmBufferType::SHARED_MEMORY) {
            memcpy(it->mCodecBuffer->base(), mDecryptDestination->unsecurePointer(), result);
        }

从代码中我们可以看出来,将secure input 解密到目标buffer时分为两种情况,一种是secure的情况,需要解密到 handle 指向的buffer中,另一种情况就是解密到一块非安全的buffer中,最后拷贝到ACodec分配的普通buffer中,这种方式上层同样无法接触到ACodec分配的buffer,因此也是安全的。

2、ACodec::BufferInfo

了解了以上内容,我们再回头看 ACodec::BufferInfo,同样的里面会有两块 MediaCodecBuffer,之所以要这样设计,是因为还考虑了 DataConverter 的情况,DataConverter 用于数据转换,ACodec 中用作与 Audio 解码后的 PCM 数据转换(我们不做深入研究)。因为涉及到转换,所以需要一个 MediaCodecBuffer 做中转,不过一般情况下 mData 和 mCodecData 指向的是同一块 MediaCodecBuffer。

最后给出一张示意图:
请添加图片描述

有了它我们大致就能理解为什么 ACodec 和 ACodecBufferChannel 中为什么要有那么多 MediaCodecBuffer 指针了。

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

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

相关文章

MySQL数据库基本操作语言:SQL

SQL 概述 SQL是一 种用于操作数据库的语言&#xff0c;SQL适用于所有关系型数据库。 MySQL、Oracle、 SQLServer是一 一个数据库软件,这些数据库软件支持标准SQL,也就是通过SQL可以使用这些软件,不过每一个数据库系统会在标准SQL的基础 上扩展自己的SQL语法。 大部分的NoSQ…

Flink电商实时数仓(三)

DIM层代码流程图 维度层的重点和难点在于实时电商数仓需要的维度信息一般是动态的变化的&#xff0c;并且由于实时数仓一般需要一直运行&#xff0c;无法使用常规的配置文件重启加载方式来修改需要读取的ODS层数据&#xff0c;因此需要通过Flink-cdc实时监控MySql中的维度数据…

GraphPad Prism 10 for Mac v10.0.0.3 安装教程

GraphPad Prism GraphPad Prism是一款非常专业强大的科研医学生物数据处理绘图软件&#xff0c;它可以将科学图形、综合曲线拟合&#xff08;非线性回归&#xff09;、可理解的统计数据、数据组织结合在一起&#xff0c;除了最基本的数据统计分析外&#xff0c;还能自动生成统…

ansible(二)

模块七&#xff1a; hostname模块&#xff0c;修改主机名 模块八&#xff1a; copy模块&#xff1a;用于复制指定主机的文件到远程主机的模块&#xff08;必须要用绝对路径&#xff09; 常用的参数&#xff1a; Dest:指出要复制的文件在哪&#xff08;去哪&#xff09;&am…

C语言—每日选择题—Day59

指针相关博客 打响指针的第一枪&#xff1a;指针家族-CSDN博客 深入理解&#xff1a;指针变量的解引用 与 加法运算-CSDN博客 第一题 1. 以下关于 typedef 正确的描述是&#xff08;&#xff09;【多选】 A&#xff1a;用typedef可以定义各种类型别名&#xff0c;但不能定义变量…

CSS:元素显示模式与背景

CSS&#xff1a;元素显示模式与背景 元素显示模式什么是元素显示模式块级元素 block行内元素 inline行内块元素 inline-block元素显示模式对比元素显示模式转换 display 背景背景颜色 background-color背景图片 background-image背景平铺 background-repeat背景图片位置 backgr…

使用Swift Package Manager (SPM)实现xcframework分发

Swift Package Manager (SPM) 是苹果官方提供的用于管理 Swift 项目的依赖关系和构建过程的工具。它是一个集成在 Swift 编程语言中的包管理器&#xff0c;用于解决在开发过程中管理和构建包依赖项的需求。 Package结构 一个 Package&#xff08;包&#xff09;由 Swift 源码…

CSS 网页制作-学成在线

1、 准备工作 1.1 项目目录 网站根目录是指存放网站的第一层文件夹&#xff0c;内部包含当前网站的所有素材&#xff0c;包含HTML、CSS、图片、JavaScript等等。 1.2 版心效果 可以发现都是呈现版心居中的效果&#xff0c;但是每次都写一次太麻烦了&#xff0c;可以把版心居中…

Android应用-flutter使用Positioned将控件定位到底部中间

文章目录 场景描述示例解释 场景描述 要将Positioned定位到屏幕底部中间的位置&#xff0c;你可以使用MediaQuery来获取屏幕的高度&#xff0c;然后设置Positioned的bottom属性和left或right属性&#xff0c;一般我们left和right都会设置一个值让控制置于合适的位置&#xff0…

鸿蒙应用开发初体验 HelloWorld

9 月 25 日&#xff0c;华为常务董事、终端 BG CEO、智能汽车解决方案 BU 董事长余承东华为秋季全场景新品发布会上介绍了鸿蒙系统的最新进展&#xff1a;HarmonyOS 4 发布后&#xff0c;短短一个多月升级用户已经超过 6000 万&#xff0c;成为史上升级速度最快的 HarmonyOS 版…

Python数据科学视频讲解:包裹法—递归特征消除

4.4 包裹法—递归特征消除 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解4.4节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#xff0c;包括数据…

【已解决】vs2015下c++对sqlite的操作

本博文源于笔者操作sqlite3&#xff0c;借鉴了很多文章的思路&#xff0c;这里并整理了c常用的对数据库的操作供大家点赞收藏以后备用。包含了&#xff1a;c对sqlite3的创建数据库、创建数据表、写入数据表、读取数据表、删除数据表。也包括了最基础的让c运行sqlite3.内容供读者…

gem5 RubyPort: mem_request_port作用与连接 simple-MI_example.py

简介 回答这个问题&#xff1a;RubyPort的口下&#xff0c;一共定义了六个口&#xff0c;分别是mem_request_port&#xff0c;mem_response_port&#xff0c;pio_request_port&#xff0c;pio_response_port&#xff0c;in_ports, interrupt_out_ports&#xff0c;他们分别有什…

长三角安防行业盛会“2024杭州安博会”4月份在杭州博览中心召开

作为中国安防行业的盛会&#xff0c;2024杭州安博会将于4月份在杭州国际博览中心隆重召开。本届安博会将汇聚全球最先进的安防技术和产品&#xff0c;为来自世界各地的安防从业者、爱好者以及投资者提供一个交流、展示和合作的平台。 据了解&#xff0c;2024杭州安博会将会展示…

one wire(单总线)FPGA代码篇

一.引言 单总线&#xff08;OneWire&#xff09;是一种串行通信协议&#xff0c;它允许多个设备通过一个单一的数据线进行通信。这个协议通常用于低速、短距离的数字通信&#xff0c;特别适用于嵌入式系统和传感器网络。 二.one wire通信优点缺点 优点&#xff1a; 单一数据线…

[CVPR-23] Instant Volumetric Head Avatars

[paper | code | proj] 本文提出INSTA。INSTA是一种backward mapping方法。该方法基于NeRF建立标准空间&#xff0c;形变空间&#xff08;任意表情&#xff09;通过映射回标准空间&#xff0c;实现渲染。为实现形变空间中任意点向标准空间的映射&#xff0c;对形变空间中的任意…

PySpark中DataFrame的join操作

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

Vue 保留富文本中包含指定字符串所在的行

需求描述 如下图所示&#xff0c;想保留所有包含『张三』所在的行 最终实现效果 先看一下富文本的源码 <p>任务1 张三</p> <p>任务2 张三</p> <p>任务3 李四</p> <p>任务4 李四</p> &l…

「微服务模式」七种微服务反模式

什么是微服务 流行语经常为进化的概念提供背景&#xff0c;并且需要一个良好的“标签”来促进对话。微服务是一个新的“标签”&#xff0c;它定义了我个人一直在发现和使用的领域。文章和会议描述了一些事情&#xff0c;我慢慢意识到&#xff0c;过去几年我一直在发展自己的个人…

JMeter常见配置及常见问题修改

一、设置JMeter默认打开字体 1、进入安装目录&#xff1a;apache-jmeter-x.x.x\bin\ 2、找到 jmeter.properties&#xff0c;打开。 3、搜索“ languageen ”&#xff0c;前面带有“#”号.。 4、去除“#”号&#xff0c;并修改为&#xff1a;languagezh_CN 或 直接新增一行&…