swr 要你交代六个参数:进三个、出三个。 它不只转采样率,还顺手转格式与声道——三件事一起做。
10.2 讲的是音频本身,这一节进到「谁来转」这件事。 10.3 会把这个流程拆成六步细讲。
一、六个参数
输入三个、输出三个,少一个都不行。 侧 参数 含义
输入 format 进来的样本格式 输入 layout 进来的声道布局 输入 rate 进来的采样率 输出 format 要的样本格式 输出 layout 要的声道布局 输出 rate 要的采样率
PyAV 里只写输出那三个,输入那三个是从你送进去的帧上读的。 这是很贴心的简化,但也藏了一件事:输入变了它不会提醒你。 所以整条链上如果中途换了输入格式,最好重建一个重采样器,别指望它自己跟着变。
二、它一次能干三件事
一帧进去,三个属性同时变了。 项 进 出
格式 fltp s16 布局 stereo mono 采样率 44100 48000 样本数 441 480
441 个样本按 44100 算是 10 毫秒,480 个按 48000 算也是 10 毫秒。
时长没变,只是换了个刻度表达。
这也是为什么它的名字里除了 sample 还带 format 与 matrix:
libswresample 实际做了「重采样 + 重格式 + 重矩阵」三件事。
所以别把它当成「只改采样率的工具」,它是音频格式的总转换器——理解这一点,很多用法就顺了。
三、C 的五个函数
建、设、init、转、放,五行。 C 的 API PyAV 里
swr_alloc av.AudioResampler(...) swr_alloc_set_opts2 构造时传的三个参数 swr_init 构造里做掉了 swr_convert resample(frame) swr_free 交给垃圾回收
顺序不能乱:先设参数、再 init、然后才 convert。 跳过 init 直接崩——因为滤波器系数还没算出来。 PyAV 把这些压进了构造函数,所以你在 Python 里看不到 init 这一步。 但知道它在,就能理解为什么「构造完才能用」。
四、一帧进不了一帧出
这是音频与视频最大的手感差别。 送进去 第一帧直接出 理论该有
800 样本 @ 44100 → 8000 129 145
出的比理论少 16 个,因为滤波器肚子里还没攒满。这 16 个要等冲空包才吐出来。 所以「一帧进、一帧出」这种假设在音频上完全不成立。 代码里必须用循环收帧,不能用「转换一次拿一帧」。 这条与视频的 8.2 正好相反:视频是「送一个包可能得零帧或多帧」, 音频是「送一帧可能得零帧或一帧,且数量对不上」——两边都不能假设一一对应。
五、什么时候该重建
三种情况必须重建,不能复用。 情况 为什么
目标参数变了 内部系数按旧参数算的 输入格式变了 它不会自己发现 切换素材/音轨 滤波器状态是旧素材的
第三条最隐蔽:两个素材接起来播,中间不重建, 上一段留在滤波器里的那点尾巴会「漏」到下一段开头,听感是接缝处有一声轻响。 规矩很简单:换素材就重建。 反正构造一个重采样器很便宜。
六、动手 Demo:swr 要你交代哪三件事
一帧同时改格式、声道、采样率;再验一次「送多少不一定出多少」。
import numpy as np
import av
# 一帧同时改三样:fltp/stereo/44100 -> s16/mono/48000
a = np.zeros((2, 441), dtype=np.float32)
f = av.AudioFrame.from_ndarray(a, format="fltp", layout="stereo")
f.sample_rate = 44100
r = av.AudioResampler(format="s16", layout="mono", rate=48000)
out = list(r.resample(f)) + list(r.resample(None))
for x in out:
print(x.format.name, x.layout.name, x.sample_rate, x.samples)
# 送多少就出多少吗:8000/44100 -> 8000,理论 145
r2 = av.AudioResampler(format="s16", layout="mono", rate=8000)
g = av.AudioFrame(format="s16", layout="mono", samples=800)
g.sample_rate = 44100
o1 = r2.resample(g)
print(sum(x.samples for x in o1), int(round(800 * 8000 / 44100.0)))
实跑输出:
--------------------------------------------------------------------
swr 要你交代哪三件事
--------------------------------------------------------------------
**① 六个参数,进三个出三个**
侧 参数 含义
--------------------------------------
输入 format 进来的样本格式
输入 layout 进来的声道布局
输入 rate 进来的采样率
输出 format 要的样本格式
输出 layout 要的声道布局
输出 rate 要的采样率
**六个都交代清楚,它才知道该做什么。** 而 PyAV 里只写输出那三个——输入那三个是从你送的帧上读的。
**② 一口气改三样**
项 进 出
-------------------------------
格式 fltp s16
布局 stereo mono
采样率 44100 48000
样本数 441 480
**一帧进去,格式、声道、采样率三样同时变了。**
这就是 swr 的名字里「resample」之外还带「reformat」与「rematrix」的原因——**它一次能干三件事。**
**③ C 的 API 对照**
C 的 API PyAV 里
-----------------------------------------------------
swr_alloc av.AudioResampler(...)
swr_alloc_set_opts2 构造时传的三个参数
swr_init 构造里做掉了
swr_convert resampler.resample(frame)
swr_convert(NULL) resample(None) 冲空包
swr_free 交给垃圾回收
**五行 C 里要自己写的,PyAV 里两行就完了。** 但顺序不能乱:
**先设参数、再 init、然后才 convert**——跳过 init 就直接崩。
**④ 送多少就出多少吗**
项 值
-------------------------------------
送进去 800 样本 @ 44100
第一帧直接出 129 样本
理论该有 145 样本
**出得比理论少**——滤波器肚子里还没攒满(或还留着上一批的尾巴),
差的那 16 个样本要等冲空包才吐出来。**所以「一帧进一帧出」这种假设在音频上完全不成立。**
格式、声道、采样率三样一起变,样本数从 441 变 480,时长不变。 第一帧直出 129,理论 145,差的 16 个要等冲空包。
30 秒实验
- 把目标再改成
format="fltp", layout="stereo", rate=48000,看输出 shape。 - 送一个 44100 的帧、目标也是 44100,看输出样本数是不是原样。
- 连着送三帧,看第一帧与后两帧的输出样本数是否不同。
常见误区
- 以为 swr 只改采样率。 格式与声道一起改。
- 以为一帧进一帧出。 数量对不上是常态。
- 输入格式变了还复用同一个重采样器。 它不会提醒你。
自测三问(点开对答案)
1. swr 要交代哪六个参数? —— 输入与输出各三个:format、layout、rate。 2. 它一次能做几件事? —— 三件:重采样、换格式、重映射声道。 3. 什么时候必须重建重采样器? —— 目标参数变、输入格式变、换素材时都要重建。- ☐ (本节完,下一节 10.3.1 SwrContext使用步骤解析)