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 很简单,主要步骤如下:
- 遍历buffer数组,根据bufferid查找对应ACodecBufferChannel::BufferInfo,从而获得mClientBuffer;我们这里再回顾一下,在不用解密/解扰的模式下,mClientBuffer和mCodecBuffer其实是指向同一个MediaCodecBuffer的,解密/解扰的模式那么mClientBuffer和mCodecBuffer指向的则不是同一块MediaCodecBuffer了;
- 如果mClientBuffer和mCodecBuffer不是指向同一块MediaCodecBuffer,那么需要给 mClientBuffer 设置默认的 input format;
- 调用 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 流程后,会把所有的 input buffer 都提交给 MediaCodec 层,MediaCodec 是如何处理传上来的 buffer 呢?这一节我们就来了解一下这部分内容。 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 基…...
数据库是否可以直接作为数据仓库的数据源
在数据仓库使用数据时,我们是否可以直接将数据库作为数据源?如果使用了,会存在哪些问题? 数据库中存储的是业务数据,存储方式是行式存储;而数据仓库中数据是以列式存储的;如果数据仓库要想使用…...

IntelliJ IDE 插件开发 | (四)开发一个时间管理大师插件
系列文章 IntelliJ IDE 插件开发 |(一)快速入门IntelliJ IDE 插件开发 |(二)UI 界面与数据持久化IntelliJ IDE 插件开发 |(三)消息通知与事件监听IntelliJ IDE 插件开发 |(四)开发一…...

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

【银行测试】金融银行-理财项目面试/分析总结(二)
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 银行理财相关的项…...

张江智荟毁约offer
毕业8年后,找工作被国企歧视学历!已经收到了offer,在入职前一周被通知要撤回offer,拒绝录用,理由居然是他们只要本科211以上的人 这是我今天(2023-12-26)亲身经历的事,听说过面试前…...
ubuntu 系统终端颜色设置
1 开启终端颜色 # 第一步: 在 ~/.bashrc 中设置 force_color_promptyes# 第二步: 执行 source ~/.bashrc2 对于精减的 .bashrc 在 ~/.bashrc 中添加以下内容,再执行 source ~/.bashrc : # uncomment for a colored prompt, if…...

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

大厂前端面试题总结(百度、字节跳动、腾讯、小米.....),附上热乎面试经验!
先简单介绍下自己,我“平平无奇小天才”一枚,毕业于南方普通985普通学生,有幸去百度、字节面试,感觉大公司就是不一样,印象最深的是字节,所以有必要总结一下面试经验,以及面试中遇到的一些问题&…...

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

vue+element+springboot实现多张图片上传
1.需求说明 2.实现思路 3.el-upload组件主要属性说明 4.前端传递MultipartFile数组与服务端接收说明 5.完整代码 1.需求说明 动态模块新增添加动态功能,支持多张图片上传.实现过程中对el-upload组件不是很熟悉,踩了很多坑,当然也参考过别的文章,发现处理很…...
react使用useState更新数组失败
失败案例: const [addBox, setAddBox] useState([])const itemAdd (item) >{addBox.push(item);setAddBox(addBox)console.log(addBox,点击添加按钮)} 原因:react的useState hook监听的是浅监听 在 React 中,使用 useState Hook 来更新…...
《LIO-SAM阅读笔记》3.后端优化
前言: LIO-SAM后端优化部分写在了mapOptimization.cpp文件中,本部分主要进行了激光帧的scan-to-map匹配,回环检测以及关键帧的因子图优化。本部分主要有两个环节同步进行,一个单独开辟了回环检测线程,另外一个是lidar…...

mac下jd-gui提示没有找到合适的jdk版本
mac下jd-gui提示jdk有问题 背景解决看一下是不是真有问题了方法一:修改启动脚本方法二:设置launchd环境变量 扩展动态切jdk脚本(.bash_profile) 背景 配置了动态jdk后,再次使用JD-GUI提示没有找到合适的jdk版本。 解决 看一下是不是真有问题…...
FlinkSQL窗口实例分析
Windowing TVFs Windowing table-valued functions (Windowing TVFs),即窗口表值函数 注意:窗口函数不可以单独使用,需要聚合函数,按照 window_start、window_end 分区,即存在:group by window_start,wind…...

18-网络安全框架及模型-信息系统安全保障模型
信息系统安全保障模型 1 基本概念 信息系统安全保障是针对信息系统在运行环境中所面临的各种风险,制定信息系统安全保障策略,设计并实现信息系统安全保障架构或模型,采取工程、技术、管理等安全保障要素,将风险减少至预定可接受的…...
Android 提取(备份)apk(安装包)
Android 提取(备份)apk(安装包) 一、通过安卓代码的方式 主要分三步: 根据应用找到包名根据包名获得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包…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...