☰
Linux FFmpeg深度解析之avcodec_default_get_encode_buffer调用流程与实战(二十八)
2026/11/5 5:28:07 网站建设 项目流程

简介:CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀

人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • 🌻1.前言
      • 🌻2.应用场景和用法
        • 2.1默认编码缓冲区分配
        • 2.2函数原型与参数约束
      • 🌻3.调用流程剖析
        • 3.1回调接收包大小并分配引用缓冲区
        • 3.2自定义回调与默认实现的边界
      • 🌻4.实战案例
        • 4.1包装默认回调并限制包大小
        • 4.2在打开编码器前注册回调
        • 4.3完整代码片段
      • 🌻5.总结

🌻1.前言

本篇目的:理解avcodec_default_get_encode_buffer()如何为编码器输出包分配带引用计数的缓冲区,分析它与自定义回调和AVPacket生命周期的关系,并通过回调包装示例观察默认分配器的调用位置。

编码器生成压缩数据时,需要一块可写的输出内存。FFmpeg通过AVCodecContext.get_encode_buffer回调提供这块内存,默认实现就是avcodec_default_get_encode_buffer()。

默认实现会根据AVPacket.size分配AVBufferRef,再让AVPacket.data指向缓冲区数据。它只负责建立输出包缓冲区,不调用编码器,也不负责写文件或封装容器。

编码器确定输出包大小

调用get_encode_buffer回调

avcodec_default_get_encode_buffer

分配AVBufferRef

设置AVPacket.data和buf

编码器写入压缩数据

🌻2.应用场景和用法

2.1默认编码缓冲区分配

大多数应用不需要直接调用avcodec_default_get_encode_buffer()。编码器内部通过get_encode_buffer回调申请缓冲区,默认情况下该回调由FFmpeg设置为默认实现。

自定义回调适合需要接入特定内存池、统计分配行为或控制编码缓冲区来源的场景。自定义实现必须正确填写AVPacket.data和AVPacket.buf,保证缓冲区满足对齐与填充要求,并确保回调可被多个编码线程并发调用。

场景做法关注点
常规软件编码使用FFmpeg默认回调让FFmpeg管理包数据的引用计数
统计编码包分配包装默认回调并记录申请大小保留默认的内存和引用管理
接入自定义内存池自定义get_encode_buffer返回有效AVBufferRef并实现线程安全
编码器不支持DR1自定义回调中调用默认回调不能直接提供其他来源的缓冲区
2.2函数原型与参数约束

函数原型如下:

intavcodec_default_get_encode_buffer(AVCodecContext*s,AVPacket*pkt,intflags);
参数或返回值含义
s编码器上下文
pkt输出包;调用前pkt->size已由编码器设置
flags编码器提供的缓冲区用途标记
0成功分配,pkt->buf和pkt->data已经设置
负值包大小或包当前状态不合法,或分配失败

默认实现会检查包大小是否为负数或超过允许上限,也会拒绝已有pkt->data或pkt->buf的包。随后,它为包数据和AV_INPUT_BUFFER_PADDING_SIZE尾部填充分配引用计数缓冲区。

编码器内部的ff_get_encode_buffer()负责调用回调,并检查回调是否同时设置了data和buf。成功后,它会清零尾部填充;失败时释放包引用。输出包交给调用者后,由调用者通过av_packet_unref()等接口管理生命周期。

🌻3.调用流程剖析

3.1回调接收包大小并分配引用缓冲区

编码器知道当前输出包所需大小后,内部辅助函数会把大小写入AVPacket.size,再调用AVCodecContext.get_encode_buffer。默认情况下,这个函数指针指向avcodec_default_get_encode_buffer()。

默认实现首先检查大小范围和AVPacket是否为空。通过检查后,使用av_buffer_realloc()为pkt->size + AV_INPUT_BUFFER_PADDING_SIZE分配缓冲区,把返回的引用保存在pkt->buf,并将pkt->data指向缓冲区数据区。

否

是

编码器给出pkt size

内部辅助函数调用回调

大小合法且包为空

返回错误

分配带尾部填充的AVBufferRef

设置pkt data和buf

返回编码器填充数据

内部辅助函数还会确认回调没有只设置裸数据指针而遗漏AVBufferRef。这保证编码器输出包拥有可追踪的缓冲区引用,后续可以安全地移动、引用或释放。

3.2自定义回调与默认实现的边界

avcodec_default_get_encode_buffer()是公开的默认回调,应用可以在自定义实现中调用它复用默认内存分配行为。例如先限制允许的最大包大小,再转交默认分配器。

如果编码器没有AV_CODEC_CAP_DR1能力,自定义回调必须调用默认实现,不能自行返回其他来源的缓冲区。支持自定义分配的编码器也要求回调满足线程安全、数据对齐、填充空间和引用计数等约束。

自定义分配内存时,pkt->data必须指向pkt->buf所管理的存储区。只设置pkt->data裸指针并不够,因为编码器框架需要通过引用对象维护输出数据的生命周期。

🌻4.实战案例

4.1包装默认回调并限制包大小

示例回调对申请尺寸进行上限检查,通过后调用avcodec_default_get_encode_buffer()完成分配。该方案适合加入简单的容量策略,同时继续使用FFmpeg的标准引用计数缓冲区。

4.2在打开编码器前注册回调

应在avcodec_open2()打开编码器前设置AVCodecContext.get_encode_buffer。编码器开始工作后,内部产生输出包并申请缓冲区时会进入该回调。

4.3完整代码片段
#include<libavcodec/avcodec.h>#include<libavutil/error.h>/* 限制单个编码包大小,随后交给FFmpeg默认分配器 */staticintchecked_get_encode_buffer(AVCodecContext*ctx,AVPacket*pkt,intflags){constint64_tmax_packet_size=16*1024*1024;/* pkt->size由编码器在调用回调前填写 */if(pkt->size<0||pkt->size>max_packet_size)returnAVERROR(EINVAL);/* 默认实现负责AVBufferRef和pkt->data的正确关联 */returnavcodec_default_get_encode_buffer(ctx,pkt,flags);}/* 编码器配置完成后、avcodec_open2之前注册: * * ctx->get_encode_buffer = checked_get_encode_buffer; * ret = avcodec_open2 (ctx, codec, NULL); * * 后续编码器申请输出包缓冲区时,会执行checked_get_encode_buffer。 */

该回调只增加包大小策略,内存仍由FFmpeg默认分配器管理。若改为自定义内存池,还必须返回有效的AVBufferRef,保证缓冲区长度包含尾部填充,并处理编码线程并发调用。

🌻5.总结

avcodec_default_get_encode_buffer()根据编码器预先填写的包大小分配引用计数缓冲区,并设置AVPacket.data与AVPacket.buf供编码器写入输出。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询