当前位置: 首页 > news >正文

Android 13 - Media框架(28)- MediaCodec(三)

上一节我们了解到 ACodec 执行完 start 流程后,会把所有的 input buffer 都提交给 MediaCodec 层,MediaCodec 是如何处理传上来的 buffer 呢?这一节我们就来了解一下这部分内容。

1、ACodecBufferChannel::fillThisBuffer

ACodec 通过调用 ACodecBufferChannel::fillThisBuffer 把input buffer传递给 MediaCodc,传入参数为 buffer id:

void ACodecBufferChannel::fillThisBuffer(IOMX::buffer_id bufferId) {ALOGV("fillThisBuffer #%d", bufferId);std::shared_ptr<const std::vector<const BufferInfo>> array(std::atomic_load(&mInputBuffers));// 遍历buffer数组,查找对应ACodecBufferChannel::BufferInfoBufferInfoIterator it = findBufferId(array, bufferId);if (it == array->end()) {ALOGE("fillThisBuffer: unrecognized buffer #%d", bufferId);return;}// 如果存在解密/解扰,那么需要设置input formatif (it->mClientBuffer != it->mCodecBuffer) {it->mClientBuffer->setFormat(it->mCodecBuffer->format());}// 调用callbackmCallback->onInputBufferAvailable(std::distance(array->begin(), it),it->mClientBuffer);
}

fillThisBuffer 很简单,主要步骤如下:

  1. 遍历buffer数组,根据bufferid查找对应ACodecBufferChannel::BufferInfo,从而获得mClientBuffer;我们这里再回顾一下,在不用解密/解扰的模式下,mClientBuffer和mCodecBuffer其实是指向同一个MediaCodecBuffer的,解密/解扰的模式那么mClientBuffer和mCodecBuffer指向的则不是同一块MediaCodecBuffer了;
  2. 如果mClientBuffer和mCodecBuffer不是指向同一块MediaCodecBuffer,那么需要给 mClientBuffer 设置默认的 input format;
  3. 调用 onInputBufferAvailable 将消息回传给 MediaCodec;

这里有一点很容易让人忽略,为什么调用onInputBufferAvailable时,传递的index要用std::distance来计算呢?

std::distance应该计算的是 ACodecBufferChannel::BufferInfo 在数组中的位置,也就是数组索引,所以传递给 MediaCodec 用的 index 其实是 ACodecBufferChannel 的buffer数组索引,它和buffer id是两码事。

2、BufferCallback::onInputBufferAvailable

void BufferCallback::onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) {sp<AMessage> notify(mNotify->dup());notify->setInt32("what", kWhatFillThisBuffer);notify->setSize("index", index);notify->setObject("buffer", buffer);notify->post();
}

onInputBufferAvailable 会把回传的数组索引 以及 MediaCodecBuffer 重新封装到 AMessage中,最后交由 MediaCodec Handler 处理。

2、kWhatFillThisBuffer

                case kWhatFillThisBuffer:{// 将拿到的 MediaCodec 加入到列表当中/* size_t index = */updateBuffers(kPortIndexInput, msg);// 如果正在处理以下事件,则直接将所有的buffer返回给Codecif (mState == FLUSHING|| mState == STOPPING|| mState == RELEASING) {returnBuffersToCodecOnPort(kPortIndexInput);break;}// 如果 csd buffer 不为空,则先写入csd bufferif (!mCSD.empty()) {ssize_t index = dequeuePortBuffer(kPortIndexInput);CHECK_GE(index, 0);// If codec specific data had been specified as// part of the format in the call to configure and// if there's more csd left, we submit it here// clients only get access to input buffers once// this data has been exhausted.status_t err = queueCSDInputBuffer(index);if (err != OK) {ALOGE("queueCSDInputBuffer failed w/ error %d",err);setStickyError(err);postActivityNotificationIfPossible();cancelPendingDequeueOperations();}break;}// CCodec 使用的,暂时略过if (!mLeftover.empty()) {ssize_t index = dequeuePortBuffer(kPortIndexInput);CHECK_GE(index, 0);status_t err = handleLeftover(index);if (err != OK) {setStickyError(err);postActivityNotificationIfPossible();cancelPendingDequeueOperations();}break;}// 如果使用的是异步模式if (mFlags & kFlagIsAsync) {// 并且输入不是surface,输入是surface的情况我们暂时不看if (!mHaveInputSurface) {// 状态是 flushed,则暂时不处理该input buffer,等待重新启动if (mState == FLUSHED) {mHavePendingInputBuffers = true;} else {// 调用onInputBufferAvailable将input buffer返回给上层onInputBufferAvailable();}}} else if (mFlags & kFlagDequeueInputPending) {// 如果是同步模式,并且处在阻塞等待的状态,收到input buffer,发送消息结束阻塞CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));// 增加阻塞等待计数,使得kWhatDequeueInputTimedOut无效++mDequeueInputTimeoutGeneration;mFlags &= ~kFlagDequeueInputPending;mDequeueInputReplyID = 0;} else {postActivityNotificationIfPossible();}break;}

kWhatFillThisBuffer 消息处理流程中的内容稍有一点多,我们有选择的对内容进行展开:

  • 将拿到的 MediaCodec 加入到列表当中,这里的列表有两个,一个是用来记录 ACodecBufferChannel 中所有的 buffer(分为input / output 两个数组)mPortBuffers;第二个列表是用来记录可用的input/output buffer的 mAvailPortBuffers,同样分为input / output 两个数组,这里面记录的是可用的索引。
size_t MediaCodec::updateBuffers(int32_t portIndex, const sp<AMessage> &msg) {CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);size_t index;CHECK(msg->findSize("index", &index));sp<RefBase> obj;CHECK(msg->findObject("buffer", &obj));sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());{Mutex::Autolock al(mBufferLock);if (mPortBuffers[portIndex].size() <= index) {mPortBuffers[portIndex].resize(align(index + 1, kNumBuffersAlign));}mPortBuffers[portIndex][index].mData = buffer;}mAvailPortBuffers[portIndex].push_back(index);return index;
}
  • ==这里有一点非常重要,如果没看懂很容易对接下来的内容产生疑惑:==将传来的MediaCodecBuffer记录到 mPortBuffers 中时,这里会有一个隐式转换,用 MediaCodecBuffer 创建了一个 MediaCodec::BufferInfo,好家伙,人手一个bufferinfo是吧。
    struct BufferInfo {BufferInfo();sp<MediaCodecBuffer> mData;bool mOwnedByClient;};MediaCodec::BufferInfo::BufferInfo() : mOwnedByClient(false) {}

用一张图表示一下 buffer之间的关系:
请添加图片描述

  • 如果正在处理release/stop/release,则直接将所有的buffer返回给Codec,MediaCodec 不会持有任何 buffer;
void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex, bool isReclaim) {CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);Mutex::Autolock al(mBufferLock);if (portIndex == kPortIndexInput) {mLeftover.clear();}for (size_t i = 0; i < mPortBuffers[portIndex].size(); ++i) {BufferInfo *info = &mPortBuffers[portIndex][i];if (info->mData != nullptr) {sp<MediaCodecBuffer> buffer = info->mData;if (isReclaim && info->mOwnedByClient) {ALOGD("port %d buffer %zu still owned by client when codec is reclaimed",portIndex, i);} else {info->mOwnedByClient = false;info->mData.clear();}mBufferChannel->discardBuffer(buffer);}}mAvailPortBuffers[portIndex].clear();
}
  • returnBuffersToCodecOnPort 会遍历所有 MediaCodec 记录的 BufferChannel 中的 buffer,这里之所以要遍历记录的buffer,是因为可能刚开始解码,还有buffer没有传给MediaCodec流程就结束了;MediaCodecBuffer 的 mOwnedByClient 指的是 buffer 是否被上层 app 所持有;

相关文章:

Android 13 - Media框架(28)- MediaCodec(三)

上一节我们了解到 ACodec 执行完 start 流程后&#xff0c;会把所有的 input buffer 都提交给 MediaCodec 层&#xff0c;MediaCodec 是如何处理传上来的 buffer 呢&#xff1f;这一节我们就来了解一下这部分内容。 1、ACodecBufferChannel::fillThisBuffer ACodec 通过调用 A…...

Azure 学习总结

文章目录 1. Azure Function1.1 Azure Function 概念1.2 Azure Function 实现原理1.3 Azure Function 本地调试1.4 Azure Function 云部署 2. Azure API Managment 概念 以及使用2.1 Azure API 概念2.2 Azure API 基本使用 3. Service Bus 应用场景及相关特性3.1 Service Bus 基…...

数据库是否可以直接作为数据仓库的数据源

在数据仓库使用数据时&#xff0c;我们是否可以直接将数据库作为数据源&#xff1f;如果使用了&#xff0c;会存在哪些问题&#xff1f; 数据库中存储的是业务数据&#xff0c;存储方式是行式存储&#xff1b;而数据仓库中数据是以列式存储的&#xff1b;如果数据仓库要想使用…...

IntelliJ IDE 插件开发 | (四)开发一个时间管理大师插件

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听IntelliJ IDE 插件开发 |&#xff08;四&#xff09;开发一…...

【ChatGPT 默认强化学习策略】PPO 近端策略优化算法

PPO 近端策略优化算法 PPO 概率比率裁剪 演员-评论家算法演员-评论家算法&#xff1a;多智能体强化学习核心框架概率比率裁剪&#xff1a;逐步进行变化的方法PPO 目标函数的设计重要性采样KL散度 PPO 概率比率裁剪 演员-评论家算法 论文链接&#xff1a;https://arxiv.org…...

【银行测试】金融银行-理财项目面试/分析总结(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行理财相关的项…...

张江智荟毁约offer

毕业8年后&#xff0c;找工作被国企歧视学历&#xff01;已经收到了offer&#xff0c;在入职前一周被通知要撤回offer&#xff0c;拒绝录用&#xff0c;理由居然是他们只要本科211以上的人 这是我今天&#xff08;2023-12-26&#xff09;亲身经历的事&#xff0c;听说过面试前…...

ubuntu 系统终端颜色设置

1 开启终端颜色 # 第一步&#xff1a; 在 ~/.bashrc 中设置 force_color_promptyes# 第二步&#xff1a; 执行 source ~/.bashrc2 对于精减的 .bashrc 在 ~/.bashrc 中添加以下内容&#xff0c;再执行 source ~/.bashrc &#xff1a; # uncomment for a colored prompt, if…...

【Vue】class与style绑定

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…...

大厂前端面试题总结(百度、字节跳动、腾讯、小米.....),附上热乎面试经验!

先简单介绍下自己&#xff0c;我“平平无奇小天才”一枚&#xff0c;毕业于南方普通985普通学生&#xff0c;有幸去百度、字节面试&#xff0c;感觉大公司就是不一样&#xff0c;印象最深的是字节&#xff0c;所以有必要总结一下面试经验&#xff0c;以及面试中遇到的一些问题&…...

EXPLORING DIFFUSION MODELS FOR UNSUPERVISED VIDEO ANOMALY DETECTION 论文阅读

EXPLORING DIFFUSION MODELS FOR UNSUPERVISED VIDEO ANOMALY DETECTION 论文阅读 ABSTRACT1. INTRODUCTION2. RELATEDWORK3. METHOD4. EXPERIMENTAL ANALYSIS AND RESULTS4.1. Comparisons with State-Of-The-Art (SOTA)4.2. Diffusion Model Analysis4.3. Qualitative Result…...

当 ML 遇到 DevOps:如何理解 MLOps

近年来&#xff0c;人工智能 &#xff08;AI&#xff09; 和机器学习 &#xff08;ML&#xff09; 已经席卷全球&#xff0c;几乎成为任何行业的重要组成部分&#xff0c;从零售和娱乐到医疗保健和银行业。这些技术能够通过分析大量数据实现运营自动化、降低成本和促进决策&…...

vue+element+springboot实现多张图片上传

1.需求说明 2.实现思路 3.el-upload组件主要属性说明 4.前端传递MultipartFile数组与服务端接收说明 5.完整代码 1.需求说明 动态模块新增添加动态功能,支持多张图片上传.实现过程中对el-upload组件不是很熟悉,踩了很多坑,当然也参考过别的文章,发现处理很…...

react使用useState更新数组失败

失败案例&#xff1a; const [addBox, setAddBox] useState([])const itemAdd (item) >{addBox.push(item);setAddBox(addBox)console.log(addBox,点击添加按钮)} 原因&#xff1a;react的useState hook监听的是浅监听 在 React 中&#xff0c;使用 useState Hook 来更新…...

《LIO-SAM阅读笔记》3.后端优化

前言&#xff1a; LIO-SAM后端优化部分写在了mapOptimization.cpp文件中&#xff0c;本部分主要进行了激光帧的scan-to-map匹配&#xff0c;回环检测以及关键帧的因子图优化。本部分主要有两个环节同步进行&#xff0c;一个单独开辟了回环检测线程&#xff0c;另外一个是lidar…...

mac下jd-gui提示没有找到合适的jdk版本

mac下jd-gui提示jdk有问题 背景解决看一下是不是真有问题了方法一&#xff1a;修改启动脚本方法二&#xff1a;设置launchd环境变量 扩展动态切jdk脚本(.bash_profile) 背景 配置了动态jdk后&#xff0c;再次使用JD-GUI提示没有找到合适的jdk版本。 解决 看一下是不是真有问题…...

FlinkSQL窗口实例分析

Windowing TVFs Windowing table-valued functions (Windowing TVFs)&#xff0c;即窗口表值函数 注意&#xff1a;窗口函数不可以单独使用&#xff0c;需要聚合函数&#xff0c;按照 window_start、window_end 分区&#xff0c;即存在&#xff1a;group by window_start,wind…...

18-网络安全框架及模型-信息系统安全保障模型

信息系统安全保障模型 1 基本概念 信息系统安全保障是针对信息系统在运行环境中所面临的各种风险&#xff0c;制定信息系统安全保障策略&#xff0c;设计并实现信息系统安全保障架构或模型&#xff0c;采取工程、技术、管理等安全保障要素&#xff0c;将风险减少至预定可接受的…...

Android 提取(备份)apk(安装包)

Android 提取(备份)apk(安装包) 一、通过安卓代码的方式 主要分三步&#xff1a; 根据应用找到包名根据包名获得apk提取apk 提取apk代码 private static final String BACKUP_PATH "/sdcard/backup1/"; private static final String APK ".apk";pri…...

gRPC-Go基础(4)metadata和超时设置

文章目录 0. 简介1. metadata1.1 metadata结构1.2 metadata创建1.3 客户端处理metadata1.4 服务端处理metadata1.5 metadata的传输 2. 超时设置2.1 客户端输出超时信息2.2 服务端端接收超时信息 3. 小结 0. 简介 Go在多个go routine之间传递数据使用的是Go SDK提供的context包…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...