音频倍速用 atempo,它真去重采样,不像 setpts 那样光改时间戳。 合法区间是 0.5 到 100,越界报的是 Result too large 这种驴唇不对马嘴的错。
上一节把画面改快了,声音还没动。这一节把声音那一半补上。 两个都懂了,7.3.3 才能把它们接在一起。
一、换了个滤镜:atempo
命令只换一个滤镜名:
ffmpeg -i s7.mp4 -af atempo=2.0 fast.m4a
atempo 不能像 setpts 那样光改时间戳——声音是连续波形, 硬把时间戳压一半,播放器会当噪声放。它得真去重采样,把波形压紧或拉松。
所以音频这一侧没有「撞车」问题,但也因此没有「零成本」这回事: 每一个采样点都得重新算一遍。
二、七个 tempo 实测
同一段 89088 点的音轨,换七个 tempo 值各跑一遍:
tempo 入多少点 出多少点 占原比例
0.5 89088 176862 198.5% 0.8 89088 110935 124.5% 1.0 89088 89088 100.0% 1.5 89088 59630 66.9% 2.0 89088 44032 49.4% 2.5 89088 34816 39.1% 3.0 89088 29948 33.6%
tempo=2.0 出来 44032 点,占 49.4%,不是正好一半。 因为 atempo 是 512 点一块处理的,边界那一块会多留或少留。
0.5 那一行是 198.5% 而不是 200%,同样差这一块的零头。 倍数越极端,零头占比越大,3.0 那行理论 33.3%、实测 33.6%。
三、越界报的错很误导
往下越过 0.5 会怎样:
atempo=0.4 → OverflowError [Errno 34] Result too large
tempo 的合法区间是 0.5 到 100。0.4 越过了下界,
但报的不是「参数无效」,而是 Result too large。
因为它内部先算了个比值再按比值分配缓冲,除以 0.4 之后缓冲区大得离谱。 这个报错信息跟真正的原因差得很远,照着它查会一路查偏。
记住一条就够:看到 Result too large 先回头看 tempo 是不是小于 0.5。
四、想更慢就把两段串起来
0.5 已经是下限了,再慢就得串:
-af atempo=0.5,atempo=0.5
链 出多少点 占原比例
atempo=0.5 176862 198.5% atempo=0.5 接 atempo=0.5 352612 395.8%
串两段 0.5,长度变成 395.8%,差不多就是四倍,等于 tempo=0.25。 命令行里的逗号就是「串起来」,物理上等于两个滤镜前后接力。
两段都带了误差,所以 395.8% 而不是 400%。 三段、四段也能串,误差会一层层滚上去,但听感上察觉不到。
五、一个容易混的点:音高
atempo 只改速度,不改音高。 改成 2 倍速,人声还是原来的调门,只是说话变快了。
这是它跟「改采样率」最大的区别: 改采样率会让声音变尖(像快进的磁带),atempo 不会。
想要「快放带升调」的效果,得另配 asetrate 与 aresample,那是另一套玩法。
atempo 保住音高,正是它慢(要重采样)的原因。
六、动手 Demo:七个 tempo、一次越界、一次串联
下面这段代码把七个 tempo 值各跑一遍,数入点与出点。 再试 0.4 看它报什么,最后把两段 0.5 串起来。
import av
from fractions import Fraction
def atempo(tempos):
g = av.filter.Graph()
src = g.add_abuffer(sample_rate=44100, format=u"s16", layout=u"stereo",
time_base=Fraction(1, 44100))
p = src
for t in tempos:
f = g.add(u"atempo", t)
p.link_to(f)
p = f
sk = g.add(u"abuffersink")
p.link_to(sk)
g.configure()
nin = nout = 0
c0 = av.open(u"s7.mp4")
rs = av.AudioResampler(format=u"s16", layout=u"stereo", rate=44100)
for fr in c0.decode(audio=0):
for f2 in rs.resample(fr):
src.push(f2)
nin += f2.samples
while True:
try:
o = sk.pull()
except Exception:
break
nout += o.samples
src.push(None)
while True:
try:
o = sk.pull()
except Exception:
break
nout += o.samples
c0.close()
return nin, nout
print(u"%-6s %-10s %-10s %s" % (u"tempo", u"入", u"出", u"占原比例"))
for t in (u"0.5", u"0.8", u"1.0", u"1.5", u"2.0", u"2.5", u"3.0"):
n, o = atempo([t])
print(u"%-6s %-10d %-10d %.1f%%" % (t, n, o, 100.0 * o / n))
print(u"--- 越界")
try:
atempo([u"0.4"])
except Exception as ex:
print(u"atempo=0.4 报错 %s %s" % (type(ex).__name__, ex))
print(u"--- 串两段")
n1, o1 = atempo([u"0.5"])
n2, o2 = atempo([u"0.5", u"0.5"])
print(u"单段 0.5 %d 点 %.1f%%" % (o1, 100.0 * o1 / n1))
print(u"0.5 接 0.5 %d 点 %.1f%%" % (o2, 100.0 * o2 / n2))
实跑输出:
--------------------------------------------------------------------
7.3.2 音频倍速
--------------------------------------------------------------------
**① 音频这一侧换了个滤镜:atempo**
`ffmpeg -i s7.mp4 -af atempo=2.0 fast.m4a`
**`atempo` 不能像 setpts 那样光改时间戳——声音是连续波形,**
**硬把时间戳压一半,播放器会当噪声放。它得真去重采样,把波形压紧或拉松。**
**② 实测各个 tempo 值**
tempo 入多少点 出多少点 占原比例
------------------------------------------------
0.5 89088 176862 198.5%
0.8 89088 110935 124.5%
1.0 89088 89088 100.0%
1.5 89088 59630 66.9%
2.0 89088 44032 49.4%
2.5 89088 34816 39.1%
3.0 89088 29948 33.6%
**源音轨 89088 个采样点。tempo=2.0 出来 44032 点,占 49.4%。**
**比例不是正好一半,因为 atempo 是 512 点一块处理的,边界那一块会多留或少留。**
**③ 低于 0.5 直接报错**
`atempo=0.4` → `OverflowError [Errno 34] Result too large`
**tempo 的合法区间是 0.5 到 100。0.4 越过了下界,报的不是「参数无效」,**
**而是 `Result too large`——它内部先算了个比值再分配缓冲,除以 0.4 之后缓冲区大得离谱。**
**这个报错信息跟真正的原因差得很远,很容易查偏方向。**
**④ 想更慢,就把两段 atempo 串起来**
链 出多少点 占原比例
------------------------------------------------------
atempo=0.5 176862 198.5%
atempo=0.5 接 atempo=0.5 352612 395.8%
**串两段 0.5,长度变成 395.8%,差不多就是四倍,等于 tempo=0.25。**
**命令行写成 `-af atempo=0.5,atempo=0.5`,逗号就是「串起来」。**
**⑤ 一个容易混的点:音高**
**atempo 只改速度,不改音高;改成 2 倍速,人声还是原来的调门,只是说话变快。**
**想要「快放带升调」的效果,得另配 `asetrate` 与 `aresample`,那是另一套玩法。**
七行数字跟本节那张表逐条对上,注意 0.5 是 198.5% 而不是 200%。
atempo([u"0.4"]) 那一行打的错是 OverflowError,
错误类型跟「参数越界」这四个字一点关系都没有,这就是它误导人的地方。
最后两行是单段与串联的对比:352612 除以 89088 得 395.8%。 串起来不是简单相加,是两个比值相乘,再加上各自的零头。
30 秒实验
- 试上界:把 3.0 换成 100,看还能不能跑、出来多少点。
- 串三段:写
atempo=0.5三次,看占比是不是接近 800%。 - 换采样率:把源换成 48000 Hz,看点数比例变不变。
常见误区
- 以为 tempo 上界是 2.0。现在能到 100,2.5、3.0 都能跑。
- 把
Result too large当成内存问题。它其实是 tempo 小于 0.5。 - 以为串联是相加。两个 0.5 是四倍,不是 1.0。
自测三问(点开对答案)
1. atempo 的合法区间是多少? —— 0.5 到 100。低于 0.5 会报 `Result too large`。 2. 为什么 tempo=2.0 出来不是正好一半的点? —— 它按 512 点一块处理,边界那一块会多留或少留。 3. 想要 0.25 倍速怎么写? —— `atempo=0.5,atempo=0.5`,两段串起来。- ☐ (本节完,下一节 7.3.3 音视频同时倍速)