Android 13 - Media框架(29)- MediaCodec(四)

上一节我们了解了如何通过 onInputBufferAvailable 和 getInputBuffer 获取到 input buffer index,接下来我们一起学习上层如何拿到buffer并且向下写数据的。

1、获取 input Buffer

获取 MediaCodec 中的 buffer 有两种方式,一种是调用 getInputBuffers 获取端口上所有的buffer,另一种是根据索引获取某一个 buffer。

1.1、getInputBuffers

getInputBuffers 和 getOutputBuffers 实现方式相同,都是发送一条 kWhatGetBuffers 消息,阻塞获取 buffer 数组:

status_t MediaCodec::getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);msg->setInt32("portIndex", kPortIndexInput);msg->setPointer("buffers", buffers);sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}
        case kWhatGetBuffers:{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));// 如果不是 executing 状态 或者 是异步的状态直接返回errorif (!isExecuting() || (mFlags & kFlagIsAsync)) {PostReplyWithError(replyID, INVALID_OPERATION);break;} else if (mFlags & kFlagStickyError) {PostReplyWithError(replyID, getStickyError());break;}int32_t portIndex;CHECK(msg->findInt32("portIndex", &portIndex));Vector<sp<MediaCodecBuffer> > *dstBuffers;CHECK(msg->findPointer("buffers", (void **)&dstBuffers));dstBuffers->clear();// If we're using input surface (either non-persistent created by// createInputSurface(), or persistent set by setInputSurface()),// give the client an empty input buffers array.if (portIndex != kPortIndexInput || !mHaveInputSurface) {if (portIndex == kPortIndexInput) {mBufferChannel->getInputBufferArray(dstBuffers);} else {mBufferChannel->getOutputBufferArray(dstBuffers);}}(new AMessage)->postReply(replyID);break;}

处理 getInputBuffers 消息之前会先判断当前的状态是否是 executing?在之前的学习中我们了解到start 之后,buffer 才会全部分配完成,所以这个方法的调用需要在start之后。另外还会判断MediaCodec是否在异步模式下运行,如果是则会直接报错,意味着异步模式是不允许上层获取到所有buffer的。

getInputBuffer 获取到的 buffer 数组是直接从 ACodecBufferChannel 中获得的,并不会从 MediaCodec 存储的内容中获得。

1.1、getInputBuffer

getInputBuffer 和 getOutputBuffer 以及 getOutputFormat 的实现方式相同,只不过函数调用回传的内容不一样:

status_t MediaCodec::getInputBuffer(size_t index, sp<MediaCodecBuffer> *buffer) {sp<AMessage> format;return getBufferAndFormat(kPortIndexInput, index, buffer, &format);
}

内部实现 getBufferAndFormat 并没有使用 AMessage 机制,直接使用锁来进行同步:

status_t MediaCodec::getBufferAndFormat(size_t portIndex, size_t index,sp<MediaCodecBuffer> *buffer, sp<AMessage> *format) {// 检查传出参数是否为 nullif (buffer == NULL) {ALOGE("getBufferAndFormat - null MediaCodecBuffer");return INVALID_OPERATION;}// 检查传出参数是否为 nullif (format == NULL) {ALOGE("getBufferAndFormat - null AMessage");return INVALID_OPERATION;}// 清除 返回值 中的内容buffer->clear();format->clear();// 调用必须检查状态是否为 isExecutingif (!isExecuting()) {ALOGE("getBufferAndFormat - not executing");return INVALID_OPERATION;}// we do not want mPortBuffers to change during this section// we also don't want mOwnedByClient to change during thisMutex::Autolock al(mBufferLock);std::vector<BufferInfo> &buffers = mPortBuffers[portIndex];if (index >= buffers.size()) {ALOGE("getBufferAndFormat - trying to get buffer with ""bad index (index=%zu buffer_size=%zu)", index, buffers.size());return INVALID_OPERATION;}const BufferInfo &info = buffers[index];if (!info.mOwnedByClient) {ALOGE("getBufferAndFormat - invalid operation ""(the index %zu is not owned by client)", index);return INVALID_OPERATION;}*buffer = info.mData;*format = info.mData->format();return OK;
}

mBufferLock 这个锁是用来管理 MediaCodec 持有的 mPortBuffers 的,getInputBuffer 是直接从 mPortBuffers 中获取 buffer,所以需要加锁。至于为什么这里不用异步消息机制来写,还要再考究,个人感觉是差不多的,用异步消息机制可以省略锁的使用。

2、写入数据

上层拿到 input buffer(MediaCodecBuffer),向 buffer 中写入数据之后,需要通知 ACodec 数据已经写完了,ACodec 再紧接着通知 OMX Node 读取数据。我们这里看第一个步骤,如何通知 ACodec 数据已经写入完毕了呢?

看 MediaCodec 的头文件我们发现有两个相关的接口,一个是 queueInputBuffer,另一个是 queueSecureInputBuffer,这两个方法使用同一个消息,只不过传递的参数会不一样。

        case kWhatQueueInputBuffer:{sp<AReplyToken> replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (!isExecuting()) {PostReplyWithError(replyID, INVALID_OPERATION);break;} else if (mFlags & kFlagStickyError) {PostReplyWithError(replyID, getStickyError());break;}status_t err = UNKNOWN_ERROR;if (!mLeftover.empty()) {mLeftover.push_back(msg);size_t index;msg->findSize("index", &index);err = handleLeftover(index);} else {err = onQueueInputBuffer(msg);}PostReplyWithError(replyID, err);break;}

处理 kWhatQueueInputBuffer 时同样会先判断当前状态是否是executing的状态,接下来的过程会有一些 CCodec 相关的流程,我们这里暂时跳过,直接看 onQueueInputBuffer。

onQueueInputBuffer 的代码非常长,主要是考虑了 ACodec 以及 CCodec,普通流以及加密流这四种情况的组合,同样的我们忽略 CCodec 相关的部分:

status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {size_t index;size_t offset;size_t size;int64_t timeUs;uint32_t flags;CHECK(msg->findSize("index", &index));CHECK(msg->findInt64("timeUs", &timeUs));CHECK(msg->findInt32("flags", (int32_t *)&flags));std::shared_ptr<C2Buffer> c2Buffer;sp<hardware::HidlMemory> memory;sp<RefBase> obj;// ......else {CHECK(msg->findSize("offset", &offset));}const CryptoPlugin::SubSample *subSamples;size_t numSubSamples;const uint8_t *key = NULL;const uint8_t *iv = NULL;CryptoPlugin::Mode mode = CryptoPlugin::kMode_Unencrypted;// We allow the simpler queueInputBuffer API to be used even in// secure mode, by fabricating a single unencrypted subSample.CryptoPlugin::SubSample ss;CryptoPlugin::Pattern pattern;if (msg->findSize("size", &size)) {if (hasCryptoOrDescrambler()) {ss.mNumBytesOfClearData = size;ss.mNumBytesOfEncryptedData = 0;subSamples = &ss;numSubSamples = 1;pattern.mEncryptBlocks = 0;pattern.mSkipBlocks = 0;}} else if (!c2Buffer) {// 获取解密或者解扰需要的信息if (!hasCryptoOrDescrambler()) {ALOGE("[%s] queuing secure buffer without mCrypto or mDescrambler!",mComponentName.c_str());return -EINVAL;}CHECK(msg->findPointer("subSamples", (void **)&subSamples));CHECK(msg->findSize("numSubSamples", &numSubSamples));CHECK(msg->findPointer("key", (void **)&key));CHECK(msg->findPointer("iv", (void **)&iv));CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));int32_t tmp;CHECK(msg->findInt32("mode", &tmp));mode = (CryptoPlugin::Mode)tmp;size = 0;for (size_t i = 0; i < numSubSamples; ++i) {size += subSamples[i].mNumBytesOfClearData;size += subSamples[i].mNumBytesOfEncryptedData;}}if (index >= mPortBuffers[kPortIndexInput].size()) {return -ERANGE;}BufferInfo *info = &mPortBuffers[kPortIndexInput][index];sp<MediaCodecBuffer> buffer = info->mData;// ......if (buffer == nullptr || !info->mOwnedByClient) {return -EACCES;}// 检查 buffer 相关的信息if (offset + size > buffer->capacity()) {return -EINVAL;}// 将信息整合至 MediaCodecBuffer 中buffer->setRange(offset, size);buffer->meta()->setInt64("timeUs", timeUs);if (flags & BUFFER_FLAG_EOS) {buffer->meta()->setInt32("eos", true);}if (flags & BUFFER_FLAG_CODECCONFIG) {buffer->meta()->setInt32("csd", true);}if (mTunneled) {TunnelPeekState previousState = mTunnelPeekState;switch(mTunnelPeekState){case TunnelPeekState::kEnabledNoBuffer:buffer->meta()->setInt32("tunnel-first-frame", 1);mTunnelPeekState = TunnelPeekState::kEnabledQueued;ALOGV("TunnelPeekState: %s -> %s",asString(previousState),asString(mTunnelPeekState));break;case TunnelPeekState::kDisabledNoBuffer:buffer->meta()->setInt32("tunnel-first-frame", 1);mTunnelPeekState = TunnelPeekState::kDisabledQueued;ALOGV("TunnelPeekState: %s -> %s",asString(previousState),asString(mTunnelPeekState));break;default:break;}}status_t err = OK;// 如果是加密的流,并且不是 CCodec,调用 queueSecureInputBufferif (hasCryptoOrDescrambler() && !c2Buffer && !memory) {AString *errorDetailMsg;CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));// Notify mCrypto of video resolution changesif (mTunneled && mCrypto != NULL) {int32_t width, height;if (mInputFormat->findInt32("width", &width) &&mInputFormat->findInt32("height", &height) && width > 0 && height > 0) {if (width != mTunneledInputWidth || height != mTunneledInputHeight) {mTunneledInputWidth = width;mTunneledInputHeight = height;mCrypto->notifyResolution(width, height);}}}err = mBufferChannel->queueSecureInputBuffer(buffer,(mFlags & kFlagIsSecure),key,iv,mode,pattern,subSamples,numSubSamples,errorDetailMsg);if (err != OK) {mediametrics_setInt32(mMetricsHandle, kCodecQueueSecureInputBufferError, err);ALOGW("Log queueSecureInputBuffer error: %d", err);}} else {// 否则调用 queueInputBuffererr = mBufferChannel->queueInputBuffer(buffer);if (err != OK) {mediametrics_setInt32(mMetricsHandle, kCodecQueueInputBufferError, err);ALOGW("Log queueInputBuffer error: %d", err);}}if (err == OK) {// synchronization boundary for getBufferAndFormatMutex::Autolock al(mBufferLock);info->mOwnedByClient = false;info->mData.clear();statsBufferSent(timeUs, buffer);}return err;
}

删除掉 CCodec 的内容后,整体的内容变得简单很多,前面的部分是检查传入参数的正确性,中间的部分是将传入参数整合进 MediaCodecBuffer 中,后面的部分是通知 ACodec 数据已经写完。

如果码流结束,那么需要写入flag BUFFER_FLAG_EOS,这个 flag 写入有两种情况,一种是随着数据写入flag,另一种是单独写一个flag。

如果是要传 csd buffer,那么需要写入 flag BUFFER_FLAG_CODECCONFIG。csd buffer写入有两种,一种是在configure时传入csd 信息,input buffer到达后会自动帮我们写入 csd buffer;另一种是configure时不写,我们自己在第一个buffer到达时向内部写入csd信息,并且填入flag。

接下来讲一讲对 queueSecureInputBuffer 和 queueInputBuffer 的理解:

从queueSecureInputBuffer的名字来看,它是安全的流程中使用的,联想到之前我们会创建 secure component,很容易就会把这两个关联起来(创建secure组件后向下写入数据就要调用queueSecureInputBuffer),但是这个理解是不对的。queueSecureInputBuffer 这里的 secure 指的应该是码流本身是否是加密的,是否需要解密的意思。如果写入的是加密/加扰的码流,那么传递给decoder之前我们需要先做解密/解扰的动作,这个动作会在ACodecBufferChannel中完成,因此MediaCodec和ACodecBufferChannel都有queueSecureInputBuffer方法,用于处理解密/解扰的流程。用于存储加密/加扰数据的buffer其实是普通buffer,解密后的数据会存储到buffer handle中被保护起来

secure组件可以使用queueInputBuffer吗?当然可以了,这种情况下上层的buffer使用的就是底层创建的buffer handle,我们需要用单独的api才能完成数据拷贝/移动,整个流程数据都是被保护的。

再抛出一个问题,当使用queueSecureInput buffer时,一定要使用secure组件吗?答案不是的哦,如果使用的是secure组件,那么解密出来的清流就是受保护的。如果使用的是non-secure组件,那么清流是不受保护的,之前的加密也就没有意义了。

请添加图片描述

如图所示,前面两列整个流程中传递的都是清流,MediaCodecBuffer都是指向同一个缓冲区。最后一列上层写给MediaCodec的是加密流,进入到ACodecBufferChannel后会进行解密,把buffer写到mCodecBuffer中。

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

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

相关文章

Jsonpath - 数据中快速查找和提取的强大工具

JSON&#xff08;JavaScript Object Notation&#xff09;在现代应用程序中广泛使用&#xff0c;但是如何在复杂的JSON数据中 查找和提取所需的信息呢&#xff1f; JSONPath是一种功能强大的查询语言&#xff0c;可以通过简单的表达式来快速准确地定位和提取JSON数据。本文将介…

如何将内容转化为流量?媒介盒子分享

软文营销就是将内容转为流量的常见做法&#xff0c;但是有许多企业在做内容时往往一头雾水导致效果不佳&#xff0c;做内容不是光靠写就可以的&#xff0c;还需要做好选题、类型、结构等&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;如何将内容转化为流量。 一、 确定内…

【SpringBoot开发】之商城项目案例(购物车相关操作)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。&#x1f3af…

2023最新租号平台系统源码支持单独租用或合租使用

这是一款租号平台源码&#xff0c;采用常见的租号模式。目前网络上还很少见到此类类型的源码。 平台的主要功能如下&#xff1a; 支持单独租用或采用合租模式&#xff1b; 采用易支付通用接口进行支付&#xff1b; 添加邀请返利功能&#xff0c;以便站长更好地推广&#xf…

每天刷两道题——第三天

1.1两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09; 输入&#xff1a;[1,2,3,4] 输出&#xff1a;[2,1,4,3…

祝贺首届兽医专业学位研究生创新创业大赛圆满闭幕

为进一步贯彻落实科教兴国、农业强国发展战略&#xff0c;适应兽医行业、产业发展需求&#xff0c;提高兽医专业学位研究生实践创新能力&#xff0c;推动兽医领域高层次应用型人才培养供给侧改革&#xff0c;促进兽医专业学位教育高质量发展&#xff0c;12月28日由中国学位与研…

什么是分布式锁

想象一下&#xff0c;如果没有分布式锁&#xff0c;多个分布式节点同时涌入一个共享资源的访问时&#xff0c;就像一群饥肠辘辘的狼汇聚在一块肉前&#xff0c;谁都想咬一口&#xff0c;最后弄得肉丢了个精光&#xff0c;大家都吃不上。 而有了分布式锁&#xff0c;就像给这块肉…

Python轴承故障诊断 (九)基于VMD+CNN-BiLSTM的故障分类

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Python轴承故障诊断 (一)短时傅里叶变换STFT Python轴承故障诊断 (二)连续小波变换CWT_pyts 小波变换 故障-CSDN博客 Python轴承故障诊断 (三)经验模态分解EMD_轴承诊断 …

JAVAEE初阶相关内容第二十弹--HTTP协议

写在前&#xff1a;2024年啦&#xff01;新的一年要努力学习啦 本篇博客围绕HTTP协议&#xff0c;对HTTP协议进行了解&#xff0c;需要理解其工作过程&#xff0c;对HTTP协议格式要清楚&#xff0c;通过抓包工具进行协议分析&#xff0c;认识“方法”、“请求报头”&#xff0c…

爬虫与反爬-localStorage指纹(某易某盾滑块指纹检测)(Hook案例)

概述&#xff1a;本文将用于了解爬虫中localStorage的检测原理以及讲述一个用于检测localStorage的反爬虫案例&#xff0c;最后对该参数进行Hook断点定位 目录&#xff1a; 一、LocalStorage 二、爬虫中localStorage的案例&#xff08;以某盾滑块为例&#xff09; 三、如何…

CSP CCF 201512-2 消除类游戏 C++满分题解

解题思路&#xff1a; 1.用两个矩阵来存储原始值&#xff0c;其中一个永远不改变&#xff0c;只用来判断&#xff0c;这样可以防止消除某一行或某一列后影响其它行或列。 2.记录下每一行每个数字出现的次数和每一列每一个数字出现的次数。 3.只有当某一行或某一列的某个数出…

Ubuntu之修改时区/时间

1、查看当前时间及时区状态 sudo timedatectl status # 显示当前时区为Asia/Shanghai 2、查看当前系统时间 sudo date 3、查看当前系统时间及时区 sudo date -R # 显示当前时间及对应时区&#xff0c;时区为“0800”北京时区 4、修改硬件时间 修改日期格式&#xff1a…

51单片机项目(26)——基于51单片机的超声波测距protues仿真

1.功能设计 用51单片机做的超声波测距系统&#xff0c;用的传感器是HCSR04&#xff0c;将距离实时显示在LCD1602屏幕上&#xff01;&#xff01;内含keil工程 完整的protues文件 可以运行&#xff01;&#xff01;&#xff01; 仿真截图&#xff1a;&#xff08;有一丢丢的误差…

[Mac软件]ColorWell For Mac 7.4.0调色板生成器

美丽而直观的调色板和调色板生成器是任何Web或应用程序开发人员工具包的必要补充&#xff01; 创建无限数量的调色板&#xff0c;快速访问所有颜色信息和代码生成&#xff0c;用于应用程序开发&#xff0c;非常简单。可编辑调色板数据库允许您存档和恢复任何调色板&#xff0c…

x-cmd pkg | lazygit - git 命令的终端 UI

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 lazygit 由 Jesse Duffield 于 2018 年使用 Go 语言构建的 git 终端交互式命令行工具&#xff0c;旨在终端界面中便捷管理 git 存储库。 首次用户 使用 x lazygit 即可自动下载并使用 在终端运行 eval "$(curl …

一站式指南:了解和绘制项目基线

项目基线是记录下来的一个起点&#xff0c;用来建立项目的绩效指标&#xff0c;并将其与之前的基准进行比较。对于所有团队而言&#xff0c;基线是进行有效绩效测量的必需工具。 项目成功的每一步都要依赖于一个坚固的基线。研究表明&#xff0c;在48个大型项目中&#xff0c;…

图神经网络与分子表征:番外——等变术语

本文重点参考了这篇博客&#xff1a;https://nb.bohrium.dp.tech/detail/1342 看论文时经常会遇到 SE3, SO3, O3 等字样&#xff0c;云里雾里&#xff0c;难以理解。本文对这些术语进行集中解释。 文章目录 数学术语GNN 设计要求平移不变性排列、置换不变性镜面等变性旋转等变…

【热点】程序员会不会被人工智能取代?

前两周&#xff0c;ZA技术社区举办了一场主题为“未来&#xff0c;程序员职业会消失吗&#xff1f;”的Geek圆桌派&#xff0c;我作为业务研发的代表参加了此次直播。由于直播时间有限&#xff0c;未能充分表达我的观点&#xff0c;故写此篇博客进行详述。另一方面&#xff0c;…

bootstrap5开发房地产代理公司Hamilton前端页面

一、需求分析 房地产代理网站是指专门为房地产行业提供服务的在线平台。这些网站的主要功能是连接房地产中介机构、房产开发商和潜在的买家或租户&#xff0c;以促成买卖或租赁房产的交易。以下是一些常见的房地产代理网站的功能&#xff1a; 房源发布&#xff1a;房地产代理网…

双语!性能优越|融合黏菌和差分变异的量子哈里斯鹰算法SDMQHHO

前面的文章里卡卡介绍了哈里斯鹰优化算法(Harris Hawks Optimization, HHO).HHO是 Heidari等[1]于2019年提出的一种新型元启发式算法&#xff0c;设计灵感来源于哈里斯鹰在捕食猎物过程中的合作行为以及突然袭击的狩猎风格&#xff0c;具有需调参数少、原理简单易实现、局部搜索…