简介:CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课🚀
专题四:Android15快速自定义与集成音效实战课🚀
专题五:Android15音频策略实战课🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例)🚀
人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
- 🌻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指向缓冲区数据。它只负责建立输出包缓冲区,不调用编码器,也不负责写文件或封装容器。
🌻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指向缓冲区数据区。
内部辅助函数还会确认回调没有只设置裸数据指针而遗漏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供编码器写入输出。