目录
[一、AVFrame 本质一句话定义](#一、AVFrame 本质一句话定义)
[二、AVFrame 数据操作函数总览表(核心 8 个)](#二、AVFrame 数据操作函数总览表(核心 8 个))
[1️. av_frame_alloc()------ 只造壳](#1️. av_frame_alloc()—— 只造壳)
[2️. av_frame_get_buffer()------ 真正分配数据](#2️. av_frame_get_buffer()—— 真正分配数据)
[3️. av_frame_ref()------ 引用拷贝(最容易误解)](#3️. av_frame_ref()—— 引用拷贝(最容易误解))
[4️. av_frame_make_writable()------ FFmpeg 的"深拷哲学](#4️. av_frame_make_writable()—— FFmpeg 的“深拷哲学)
[5️. av_frame_clone()------ 一行式深拷](#5️. av_frame_clone()—— 一行式深拷)
[6️. av_frame_copy()------ 只拷数据(不碰壳)](#6️. av_frame_copy()—— 只拷数据(不碰壳))
[7️. av_buffersrc_add_frame*()对 frame 的影响](#7️. av_buffersrc_add_frame*()对 frame 的影响)
[8️. av_frame_unref()vs av_frame_free()](#8️. av_frame_unref()vs av_frame_free())
[四、AVFrame 音频数据访问速查](#四、AVFrame 音频数据访问速查)
[五、FFmpeg 音频帧"生命周期铁三角"](#五、FFmpeg 音频帧“生命周期铁三角”)
[铁律 3 条](#铁律 3 条)
觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。
由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。
本文不是 API 翻译,而是FFmpeg 音频开发者踩坑 3 年后的生命周期总结。
围绕
AVFrame在 解码 → swr 重采样 → filter 滤镜 → 播放/编码 链路中的真实行为,逐条拆解av_frame_get_buffer / av_frame_ref / av_frame_make_writable / av_buffersrc_add_frame等高频函数的底层语义,直击 planar / packed 杂音、swr_convert 闪退、double free、破音等"玄学 Bug"的根因。全文给出 唯一正确用法 + 生命周期铁三角模型,适合做播放器 / 转码 / 实时音频的工程师收藏级参考。
核心看点
为什么
av_frame_ref()不是深拷贝,却最容易写崩
av_buffersrc_add_frame()后 frame 到底变没变(官方文档没说清)swr_convert 闪退的 90% 真因:frame ownership 污染
planar(FLTP)和 packed(S16)在 AVFrame 里的真实内存布局
FFmpeg 官方都不爱说的:**filter 输入帧能不能复用?**
一、AVFrame 本质一句话定义
**AVFrame = 媒体数据的"引用型描述符"**
不是 malloc 出来的裸 buffer
是
AVBufferRef + 元数据壳
二、AVFrame 数据操作函数总览表(核心 8 个)
| 函数 | 行为本质 | 是否深拷 | 使用场景 |
|---|---|---|---|
av_frame_alloc() |
分配壳 | 否 | 初始化 |
av_frame_get_buffer() |
分配 PCM buffer | 是 | swr / filter 输出 |
av_frame_ref() |
引用拷贝 | 否 | 传帧 / 浅拷贝 |
av_frame_clone() |
ref + writable | 是 | 存档帧 |
av_frame_make_writable() |
COW 深拷 | 是 | 写前安全 |
av_frame_copy() |
拷 PCM | 是 | 纯数据复制 |
av_frame_unref() |
减引用 | 否 | 归还 |
av_frame_free() |
unref + free | 否 | 析构 |
三、逐函数深度拆解(高分博客级)
1️. av_frame_alloc()------ 只造壳
AVFrame *f = av_frame_alloc();
做了什么:
-
分配
AVFrame结构体 -
data / buf 全 NULL
没干:
-
没 alloc PCM
-
没填格式
用途:声明"我准备要一帧"
2️. av_frame_get_buffer()------ 真正分配数据
f->format = AV_SAMPLE_FMT_FLTP;
f->nb_samples = 1024;
av_channel_layout_copy(&f->ch_layout, &stereo);
av_frame_get_buffer(f, 0);
内部:
-
按 planar / packed 算 stride
-
av_malloc_aligned() -
填
buf[] / data[] / extended_data
规则:
-
nb_samples必须先设 -
planar 自动多 plane
swr_convert / filter 输出唯一正确初始化方式
3️. av_frame_ref()------ 引用拷贝(最容易误解)
av_frame_ref(dst, src);
行为:
-
dst->data = src->data -
AVBufferRef refcount++
不拷贝 PCM
buffersrc / 队列 / 多线程传帧用
别拿 ref 后的 dst 当 swr 输出
4️. av_frame_make_writable()------ FFmpeg 的"深拷哲学
av_frame_make_writable(f);
内部逻辑:
refcount == 1 → 啥也不干
refcount > 1 → 新 malloc + memcpy
经典用法:
av_frame_ref(cp, src);
av_frame_make_writable(cp); // ✅ 安全可写
filter 前想"保留原帧又想写"的唯一正解
5️. av_frame_clone()------ 一行式深拷
AVFrame *cp = av_frame_clone(src);
等价伪码:
alloc → ref → make_writable
适合:
-
解码帧缓存
-
音频回调队列
比手写慢一丢丢(可忽略)
6️. av_frame_copy()------ 只拷数据(不碰壳)
av_frame_copy(dst, src);
前提:
-
dst / src 格式一致
-
dst 已
get_buffer
用途:
- 自己管理 buffer 时纯拷数据
7️. av_buffersrc_add_frame*()对 frame 的影响
av_buffersrc_add_frame(src_ctx, f);
frame 本身:
-
不改 data / pts
-
不改 buffer 内容
但:
-
filtergraph 内部
av_frame_ref() -
共享 buffer
正确姿势:
av_buffersrc_add_frame_flags(ctx, f, AV_BUFFERSRC_FLAG_KEEP_REF);
av_frame_unref(f); // 你负责
8️. av_frame_unref()vs av_frame_free()
av_frame_unref()
-
buf ref--
-
data / linesize 清 0
-
frame 可复用
av_frame_free()
- unref + av_free(frame)
规则:
| 场景 | API |
|---|---|
| 循环复用 | unref |
| 析构 | free |
| unique_ptr deleter | free |
四、AVFrame 音频数据访问速查
// planar
float *L = (float *)f->extended_data[0];
float *R = (float *)f->extended_data[1];
// packed
int16_t *pcm = (int16_t *)f->data[0];
// 字节数(正确)
int bytes = f->nb_samples *
av_get_bytes_per_sample(f->format) *
(av_sample_fmt_is_planar(f->format) ? 1 : f->ch_layout.nb_channels);
五、FFmpeg 音频帧"生命周期铁三角"
┌────────────┐ 解码 ┌────────────┐ swr ┌────────────┐
│ dec_frame │──────▶│ swr_frame │──────▶│ filt_frame │
│ (ref only) │ │ make_writable│ │ sink get │
└────────────┘ └────────────┘ └────┬───────┘
↓
av_frame_unref
铁律 3 条
-
一个 AVFrame 只干一件事
-
swr 输出 / filter 输入 / 解码帧 绝不混用
-
谁 alloc buffer 谁 unref
六、高频踩雷速诊表
| 现象 | 原因 |
|---|---|
| SIGSEGV swr_convert | frame buffer 被 filter ref 过 |
| 杂音 | planar → packed 解释错 |
| double free | free(data0) |
| 破音 | volume 超 ±1 + float |
| 偶发炸 | outSamples > nb_samples |
| 闪退 buffersrc | frame 没 get_buffer |
七、一句话总结
**AVFrame 不是 struct { uint8_tdata }*
是 shared_ptr<PCM> + 描述符
get_buffer= malloc
ref= share
make_writable= COW
unref= 还权
八、推荐记忆口诀
alloc 壳,get_buffer 肉
ref 借帧,writable 动手
filter 不抢,swr 不共用
unref 归位,free 收工