一个文件两条流,视频挂 -vf、音频挂 -af,各走各的。 scale 改像素,atempo 改时长,两者别混。
前三节都在处理视频。实际文件里还有一条音频流,它也能挂滤镜。 两条流并行处理,是转码命令的常态。
一、两条流,两条链
一条命令里同时动视频和音频,就是这么写的:
ffmpeg -i s7.mp4 -vf scale=80:45 -af atempo=2.0 out.mp4
-vf 管视频,-af 管音频,两个参数各管一条流。
它们是并行的两条链,不是一个接一个。
并行的意思是:视频那条链拿不到音频的帧,音频那条也拿不到视频的。
所以两条链的滤镜不能混用——-vf atempo 会报「找不到滤镜」。
二、实测:一边缩、一边变速
同一个素材,视频缩一半、音频快一倍:
流 处理前 滤镜 处理后
视频 50 帧 scale=80:45 50 帧 音频 89088 点 atempo=2.0 44032 点
视频帧数一个没少——缩放不改帧数,只改每一帧的大小。 音频点数几乎减半——变速改的就是「多久放完」,点数必然跟着变。
44032 不是精确的一半,占原来的 49.4%。 因为 atempo 按 512 点一块处理,最后凑不满一块的尾料被丢掉了。
三、为什么一个不变、一个变
这两件事看起来矛盾,其实是同一个道理的两面:
滤镜 改的是什么 帧数或点数
scale 每帧的像素 不变 atempo 每秒塞多少内容 变
scale 属于「改画面」:一帧还是一帧,只是像素少了。 时间轴一点没动,所以帧数不变。
atempo 属于「改时间」:两秒的内容压进一秒。 内容总量没变,塞得更密,所以点数减半、时长减半。
分不清这两类,就会犯「用 scale 想把视频变短」这种错。 想让画面播得快,改的是时间戳,不是尺寸。
四、同时变速要两条流一起改
想让整段视频快一倍,视频那边用 setpts,音频那边用 atempo:
-vf setpts=0.5*PTS -af atempo=2.0
两个都得改。只改视频,画面快了一倍,声音还是原来的长度, 两条流就对不齐,越到后面差得越多。
7.3 会专门讲这一对参数怎么配,以及 setpts 那个必踩的坑: 把时间戳乘 0.5 会让两帧撞到同一个时间点,封装器直接拒绝。
复杂滤镜里,两条流写在一张图上,各起一个标签:
-filter_complex "[0:v]scale=80:45[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]"
这时不再用 -vf 与 -af,两条链都在图里,再用 -map 挑出要的标签。
两种写法效果一样,选哪种看哪条链更复杂。
五、动手 Demo:一条视频链、一条音频链,各量各的
下面这段代码给视频挂 scale、给音频挂 atempo,两条链各跑一遍。 再补一个不带滤镜的对照,确认点数变化真是滤镜带来的。
import av
from fractions import Fraction
SRC = u"s7.mp4"
def audit(tempo=None, w=160, h=90):
g = av.filter.Graph()
prev = g.add_abuffer(sample_rate=44100, format=u"s16",
layout=u"stereo", time_base=Fraction(1, 44100))
f = g.add(u"atempo", tempo) if tempo else g.add(u"anull")
sk = g.add(u"abuffersink")
prev.link_to(f)
f.link_to(sk)
g.configure()
rs = av.AudioResampler(format=u"s16", layout=u"stereo", rate=44100)
c0 = av.open(SRC)
nin = 0
nout = 0
for fr in c0.decode(audio=0):
for rf in rs.resample(fr):
prev.push(rf)
nin += rf.samples
while True:
try:
nout += sk.pull().samples
except Exception:
break
prev.push(None)
while True:
try:
nout += sk.pull().samples
except Exception:
break
c0.close()
return nin, nout
a, b = audit()
print(u"音频 原样 入 %d 出 %d 比例 %.3f" % (a, b, b / float(a)))
a, b = audit(u"2.0")
print(u"音频 2 倍速 入 %d 出 %d 比例 %.3f" % (a, b, b / float(a)))
g = av.filter.Graph()
p = g.add_buffer(width=160, height=90, format=u"yuv420p",
time_base=Fraction(1, 25))
sc = g.add(u"scale", u"80:45")
sk = g.add(u"buffersink")
p.link_to(sc)
sc.link_to(sk)
g.configure()
c0 = av.open(SRC)
n = 0
for fr in c0.decode(video=0):
vf = av.VideoFrame.from_ndarray(fr.to_ndarray(), format=u"yuv420p")
vf.pts = n
vf.time_base = Fraction(1, 25)
p.push(vf)
n += 1
print(u"视频 原样 %d 帧" % n)
m = 0
while True:
try:
sk.pull()
m += 1
except Exception:
break
print(u"视频 缩放后喂 %d 帧,出来 %d 帧" % (n, m))
c0.close()
实跑输出:
--------------------------------------------------------------------
7.1.4 流和滤镜的结合使用
--------------------------------------------------------------------
**① 一个文件里通常有两条流:一条视频、一条音频。**
**两条流可以各挂一条滤镜链,互不干扰。**
`ffmpeg -i s7.mp4 -vf scale=80:45 -af atempo=2.0 out.mp4`
**`-vf` 管视频,`-af` 管音频。这是两条并行的链,不是一条。**
**② 实测:视频缩一半,音频快一倍**
流 处理前 滤镜 处理后
--------------------------------------------------
视频 50 帧 scale=80:45 50 帧
音频 89088 点 atempo=2.0 44032 点
**视频帧数一个没少——缩放不改帧数,只改每一帧的大小。**
**音频点数少了一半——变速改的就是「多久放完」,点数必然跟着变。**
**准确说是 89088 的 49.4%,不是整整一半:**
**atempo 按 512 点一块处理,最后凑不满一块的那点尾料被丢掉了。**
**③ 为什么视频没变短、音频变短了**
**这两件事看起来矛盾,其实是同一个道理的两面:**
滤镜 改的是什么 帧数或点数
--------------------------------------------
scale 每帧的像素 不变
atempo 每秒塞多少内容 变
**scale 属于「改画面」,一帧还是一帧,只是变小了。**
**atempo 属于「改时间」,两秒的内容压进一秒,点数字节都得减半。**
**④ 音视频同时变速要注意配平**
**想让整段视频快一倍,视频那边要用 setpts,音频那边要用 atempo,两个都得改:**
`-vf setpts=0.5*PTS -af atempo=2.0`
**只改视频,画面快了一倍,声音还是原来的长度,两条流就对不齐了。**
**7.3 会专门讲这一对参数怎么配,以及 setpts 那个必踩的坑。**
**⑤ 复杂滤镜里两条流写在一张图上**
`-filter_complex "[0:v]scale=80:45[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]"`
**这时不再用 -vf 与 -af,两条链都写在图里,各自起个标签,再用 -map 挑出来。**
**两种写法效果一样,选哪种看哪条链更复杂。**
第一行比例是 1.000,说明不带滤镜时点数一点不动——这就是对照组。 第二行才掉到 0.494,证明点数变化确实是 atempo 带来的。
视频那边喂进去 50 帧、出来 50 帧,一帧不少。 缩放只改每帧的大小,不改帧的个数。
有个细节:视频这条链必须先 push 完再 pull,中间的帧会堆在 sink 里。 所以本段代码先数完帧数,最后一次性把 sink 拉空。
30 秒实验
- 加三倍速:把
atempo=2.0换成3.0,看是报错还是照样跑。 - 换音量:把 atempo 换成
volume=0,听(或量)输出是不是全零。 - 只改一边:视频加 setpts、音频不动,量两条流的总时长差多少。
常见误区
- 用 scale 想缩短视频。scale 只改像素,时间轴原封不动。
- 只改视频时间戳。音频还在原速,两条流越走越错位。
- video 滤镜挂到 -af 上。两条链的帧类型不同,滤镜根本不认。
自测三问(点开对答案)
1. `-vf` 与 `-af` 是两条串联的链吗? —— 不是,是并行的两条链,各处理各的流,互不交接。 2. 为什么缩放后帧数不变? —— 缩放改的是每帧的尺寸,不碰时间戳,所以帧数原样。 3. atempo 输出为什么不是精确的一半? —— 它按 512 点一块处理,凑不满一块的尾料被丢掉。- ☐ (本节完,下一节 7.2.1 使用 scale 实现缩放)