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

Android codec2 视频框架 之输入buffer

文章目录

      • 输入端的内存管理
      • 输入数据包buffer结构体的转换

在这里插入图片描述
主要的流程如上, 申请内存在CCodecBufferChannel,申请之后回调到MediaCodec。然后应用从MediaCodec获取 将解码数据放到buffer中,CCodecBufferChannel在将这块buffer 送到componet模块。

输入端的内存管理

  • 内部解码输入buffer的申请个数以及获取方式

mediacodec 中会申请一部分(默认情况下是4个)待解码的buffer。

status_t CCodecBufferChannel::requestInitialInputBuffers() {if (mInputSurface) {return OK;}size_t numInputSlots = mInput.lock()->numSlots;struct ClientInputBuffer {size_t index;sp<MediaCodecBuffer> buffer;size_t capacity;};std::list<ClientInputBuffer> clientInputBuffers;{Mutexed<Input>::Locked input(mInput);while (clientInputBuffers.size() < numInputSlots) {ClientInputBuffer clientInputBuffer;if (!input->buffers->requestNewBuffer(&clientInputBuffer.index,&clientInputBuffer.buffer)) {break;}}}其中在构造函数中定义了
constexpr size_t kSmoothnessFactor = 4;
input->numSlots = kSmoothnessFactor;

这个buffer 外部有两种方式可以获取到。

  1. 直接调用dequeueInputBuffer。
  2. 设置回调到Mediacodec,有buffer 可用的时候 回调到callback中。
    输入输出都可以这样做, 在NuPlayer 中是设置回调到mediacodec,然后mediacodec回调回来。nuplayer中是在MediaCodec 有bufer 可用的时候 handleAnInputBuffer 从source读取数据,这个是一个新的 ABuffer buffer,读到数据后将会有拷贝的动作 将ABuffer拷贝到MediaCodecBuffer中。
sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
mCodec->setCallback(reply);
  • 输入buffer的申请、存储

在CCodecBufferChannel中 requestInitialInputBuffers 将调用input->buffers->requestNewBuffer申请到index和buffer。这些buffer也同时存储到input->buffers中。然后通过回调 回调到Mediacodec的kWhatFillThisBuffer,FillThisBuffer的 updateBuffers 存储buffer到mPortBuffers,存储index 到mAvailPortBuffers。 如果有设置callback的话,会把index 返回给注册callback的地方。如果是getInputBuffer 那么获取的是CCodecBufferChannel的input->buffers.

上述的回调有两个地方会调用

  1. InitialInputBuffers的时候。
  2. 是feedInputBufferIfAvailable的时候。而feedInputBufferIfAvailable 在onWorkDone, discardBuffer、renderOutputBuffe、onInputBufferDone等都可会调用。
MediaCodec.cppstatus_t MediaCodec::init(const AString &name) {mBufferChannel->setCallback(std::unique_ptr<CodecBase::BufferCallback>(new BufferCallback(new AMessage(kWhatCodecNotify, this))));
}ccodec.cppvoid CCodec::start() {(void)mChannel->requestInitialInputBuffers();
}MediaCodec.cpp
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();
}
  • 申请的内存不够的情况会怎么处理?

在nuplayer中拷贝解码数据到mediacodec的时候 会判断从codec取出来的buffer 够不够 不够的话会报错。而这个buffer 大小的申请也是外部设置的,一般是在解析的时候能够知道 最大是多少。比如下面的MP4解析的代码中会获取box 中sample的最大值,然后依据这个值设定输入的buffer的最大值。

bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
CHECK(msg->findSize("buffer-ix", &bufferIx));
CHECK_LT(bufferIx, mInputBuffers.size());
sp<MediaCodecBuffer> codecBuffer = mInputBuffers[bufferIx];sp<ABuffer> buffer;
bool hasBuffer = msg->findBuffer("buffer", &buffer);if (needsCopy) {
if (buffer->size() > codecBuffer->capacity()) {
handleError(ERROR_BUFFER_TOO_SMALL);
mDequeuedInputBuffers.push_back(bufferIx);
return false;
}
}status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
reply->setBuffer("buffer", accessUnit);
}sp<Codec2Buffer> LinearInputBuffers::Alloc(
const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format) {
int32_t capacity = kLinearBufferSize;
(void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
}size_t max_size;
err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);if (max_size != 0) {
if (max_size > SIZE_MAX - 10 * 2) {
ALOGE("max sample size too big: %zu", max_size);
return ERROR_MALFORMED;
}
AMediaFormat_setInt32(mLastTrack->meta,
AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, max_size + 10 * 2);
}
  • PipelineWatcher控制外部输入buffer的速度

监控输入buffer的情况,有buffer送入解码器的时候 mFramesInPipeline 存储buffer、index 和时间。送入componet 处理完成之后调用onWorkDone从队列中删除。而这个mFramesInPipeline队列的大小不能超过mInputDelay + mPipelineDelay + mOutputDelay + mSmoothnessFactor.默认是4,就是输入最多存储4块了,超过4块,就不会回调到外部,让外部送数据进来了。

    if (!items.empty()) {{Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);PipelineWatcher::Clock::time_point now = PipelineWatcher::Clock::now();for (const std::unique_ptr<C2Work> &work : items) {watcher->onWorkQueued(work->input.ordinal.frameIndex.peeku(),std::vector(work->input.buffers),now);}}err = mComponent->queue(&items);}while (!mPipelineWatcher.lock()->pipelineFull()) {sp<MediaCodecBuffer> inBuffer;size_t index;{Mutexed<Input>::Locked input(mInput);numActiveSlots = input->buffers->numActiveSlots();ALOGD("active:%d, numslot:%d", (int)numActiveSlots, (int)input->numSlots);if (numActiveSlots >= input->numSlots) {break;}if (!input->buffers->requestNewBuffer(&index, &inBuffer)) {ALOGE("[%s] no new buffer available", mName);break;}}ALOGE("[%s] new input index = %zu [%p]", mName, index, inBuffer.get());mCallback->onInputBufferAvailable(index, inBuffer);}

输入数据包buffer结构体的转换

  • MediaCodec 层
    ABuffer(Nuplayer)------>MediaCodecBuffer ----->C2Buffer
  1. Nuplayer: 拷贝解码数据到前面requestInitialInputBuffers申请的Codec2buffer(基类是MediaCodecBuffer)
  2. MediaCodec: Nuplayer中拷贝好的buffer queueInputBuffer到MediaCodec 中,MediaCodec要把这块buffer 传递到
    底下具体的componet需要要转换为一个c2buffer。这个c2buffer封装在c2work中 queue 到componet中。
  • componet层:
    是调用到simplec2componet 中,调用的是queue_nb。 在simpleC2的实现中是发送一个process的消息到looper
    执行processQueue,processQueue在调用到具体的解码componet的proces进行处理。
    std::unique_ptr<C2Work> work(new C2Work);work->input.ordinal.timestamp = timeUs;work->input.ordinal.frameIndex = mFrameIndex++;// WORKAROUND: until codecs support handling work after EOS and max output sizing, use timestamp// manipulation to achieve image encoding via video codec, and to constrain encoded output.// Keep client timestamp in customOrdinalwork->input.ordinal.customOrdinal = timeUs;work->input.buffers.clear();sp<Codec2Buffer> copy;bool usesFrameReassembler = false;if (buffer->size() > 0u) {Mutexed<Input>::Locked input(mInput);std::shared_ptr<C2Buffer> c2buffer;if (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) {return -ENOENT;}}err = mComponent->queue(&items);

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Perl语言用多线程爬取商品信息并做可视化处理
  • 网络安全之文件包含漏洞及其防护
  • Spring Boot 校验用户上传的图片文件
  • 安卓常见设计模式4------原型模式(Kotlin版)
  • 设备零部件更换ar远程指导系统加强培训效果
  • redis-cli 连接 sentinel架构的redis服务
  • 关于mac下pycharm旧版本没删除的情况下新版本2023安装之后闪退
  • 【AICFD案例教程】汽车外气动-AI加速
  • 一键创建PDF文档,高效管理您的文件资料
  • 202205(第13届)蓝桥杯Scratch图形化编程青少组(国赛_中级)真题
  • 【漏洞复现】BYTEVALUE智能流控路由器存在命令执行
  • DAY50 309.最佳买卖股票时机含冷冻期 + 714.买卖股票的最佳时机含手续费
  • with contextlib.suppress(ValueError)临时抑制指定的异常
  • LeetCode 17. 电话号码的字母组合 中等
  • 后端架构选择:构建安全强大的知识付费小程序平台
  • 【391天】每日项目总结系列128(2018.03.03)
  • CentOS 7 修改主机名
  • C学习-枚举(九)
  • C语言笔记(第一章:C语言编程)
  • Golang-长连接-状态推送
  • Python_网络编程
  • SpringBoot几种定时任务的实现方式
  • Theano - 导数
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 排序(1):冒泡排序
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 无服务器化是企业 IT 架构的未来吗?
  • Linux权限管理(week1_day5)--技术流ken
  • ​【经验分享】微机原理、指令判断、判断指令是否正确判断指令是否正确​
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • #include
  • (C语言)共用体union的用法举例
  • (libusb) usb口自动刷新
  • (编译到47%失败)to be deleted
  • (第一天)包装对象、作用域、创建对象
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (规划)24届春招和25届暑假实习路线准备规划
  • (利用IDEA+Maven)定制属于自己的jar包
  • (七)c52学习之旅-中断
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • .aanva
  • .h头文件 .lib动态链接库文件 .dll 动态链接库
  • .NET Core 发展历程和版本迭代
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • /bin/rm: 参数列表过长"的解决办法
  • [2016.7 Day.4] T1 游戏 [正解:二分图 偏解:奇葩贪心+模拟?(不知如何称呼不过居然比std还快)]
  • [AIGC] 开源流程引擎哪个好,如何选型?
  • [BetterExplained]书写是为了更好的思考(转载)
  • [BZOJ5250][九省联考2018]秘密袭击(DP)