目录
[二、swr_convert 函数原型](#二、swr_convert 函数原型)
[① SwrContext *s](#① SwrContext *s)
[② out\[\]/ in\[\]------ 不是 void*](#② out[]/ in[]—— 不是 void*)
[packed(S16 / S32 / U8)](#packed(S16 / S32 / U8))
[planar(FLTP / S16P)](#planar(FLTP / S16P))
[③ in_count/ out_count单位是什么?](#③ in_count/ out_count单位是什么?)
[五、最经典的坑:残留 FIFO(90%人踩)](#五、最经典的坑:残留 FIFO(90%人踩))
[正确 flush 姿势](#正确 flush 姿势)
[六、AVFrame 流派的"标准模板"](#六、AVFrame 流派的“标准模板”)
[解码 → swr → SDL / D3D](#解码 → swr → SDL / D3D)
[七、S16 packed → SDL 最小完整例子](#七、S16 packed → SDL 最小完整例子)
[九、swr_convert 内部在干嘛](#九、swr_convert 内部在干嘛)
觉得有用,就请您帮忙点赞转发收藏吧,您的鼓励是我创作的动力,多谢看官。
由于能力水平有限,文中的错误或不严谨的地方在所难免,还请批评指正。
swr_convert是 FFmpeg 中libswresample库的核心函数,用于执行音频重采样操作。它能够将音频流的采样率 、采样格式 (如从浮点转为整型)或声道布局(如从单声道转为立体声)进行转换,且不会改变音频的播放时长。
做音频播放 / 录音 / 转码的人,迟早都会撞上这一句:
swr_convert(swr_ctx,
out_buf, out_samples,
in_buf, in_samples);
抄能跑,但一问:**为什么返回值是输出帧数?in_samples 怎么给?planar 怎么摆?残留数据在哪?**
今天把 swr_convert拆干净,不绕术语。
一、先给一句话定性
swr_convert 不是"格式转换器",是音频流水线工人:
负责 采样率 / 声道布局 / 样本格式 / 位深 全部打包干完。
核心任务只有三个:
重采样(SRC)
声道重排(channel layout)
packed ↔ planar
二、swr_convert 函数原型
int swr_convert(struct SwrContext *s,
uint8_t *const out[], int out_count,
const uint8_t *const in[], int in_count);
看着简单,其实 4 组语义:
上下文
输出地址 / 想要多少样本
输入数据 / 实际多少样本
三、参数逐个拆(重点在"样本不是字节")
① SwrContext *s
一次初始化,反复用:
swr_alloc_set_opts(nullptr,
AV_CH_LAYOUT_STEREO, // out layout
AV_SAMPLE_FMT_S16, // out fmt
48000, // out rate
AV_CH_LAYOUT_5POINT1_BACK, // in layout
AV_SAMPLE_FMT_FLTP, // in fmt
44100,
0, nullptr);
layout + rate + fmt 缺一不可
② out[]/ in[]------ 不是 void*
packed(S16 / S32 / U8)
uint8_t* out[1] = { (uint8_t*)pcm16 };
swr_convert(s, out, ...);
planar(FLTP / S16P)
uint8_t* out[2];
out[0] = left;
out[1] = right;
规则:
-
planar = datai 每声道一块
-
packed = data0 全挤一起
AVFrame::data 原样传就行
③ in_count/ out_count单位是什么?
单声道样本数(per channel sample count)
| 参数 | 意思 |
|---|---|
| in_count | 输入每个声道多少帧 |
| out_count | 我最多能吃多少帧 |
不是字节
不是 packet size
例:1024 帧立体声:
in_count = 1024;
// FLTP: data[0][1024], data[1][1024]
四、返回值到底什么意思?
int ret = swr_convert(...);
ret = 本次输出的 per-channel sample 数
| ret | 含义 |
|---|---|
| >0 | 输出样本数 |
| 0 | FIFO 里攒着呢 |
| <0 | 出错 |
常见误解:
"我给了 1024,为啥返回 882?"
因为:
48000 → 44100
ratio = 44100/48000 ≈ 0.91875
五、最经典的坑:残留 FIFO(90%人踩)
现象
-
最后几秒声音没了
-
每次 flush 都有一小截
原因
swr 内部有 FIFO,不够一帧不吐
正确 flush 姿势
while ((ret = swr_convert(swr, out, out_max, nullptr, 0)) > 0) {
write_pcm(out, ret);
}
口诀:
in=nullptr, in_count=0 = 冲 FIFO
六、AVFrame 流派的"标准模板"
解码 → swr → SDL / D3D
AVFrame* dec = got_frame;
uint8_t* out[1] = { audio_buf };
int out_nb = swr_get_out_samples(swr, dec->nb_samples);
int ret = swr_convert(swr,
out, out_nb,
(const uint8_t**)dec->data,
dec->nb_samples);
// ret = 实际可播放样本数
swr_get_out_samples()先问大小,避免 alloc 太小
七、S16 packed → SDL 最小完整例子
int dst_nb = swr_get_out_samples(swr_ctx, frame->nb_samples);
uint8_t* dst = (uint8_t*)malloc(dst_nb * 2 * 2);
uint8_t* out[1] = { dst };
swr_convert(swr_ctx, out, dst_nb,
(const uint8_t**)frame->data, frame->nb_samples);
SDL_QueueAudio(dev, dst, dst_nb * 4);
free(dst);
八、常见翻车表(血泪)
| 症状 | 真凶 |
|---|---|
| 声音加速 | in/out rate 反了 |
| 爆音 | planar 当 packed |
| 声道左右反 | layout 没设 |
| 最后没声 | 没 flush |
| 返回负 | out_count 太小 |
九、swr_convert 内部在干嘛
[in frame]
↓
delay / compensation
↓
重采样(polyphase filter)
↓
声道混合(downmix / upmix)
↓
planar ↔ packed
↓
[FIFO] → out
不是一帧进一帧出
十、一句话总结
swr_convert = 给样本数,吃样本数,返回输出样本数;
结束必须 nullptr/0 flush;planar 用 data\[\],永远别碰字节数。