🎬 第七章 FFmpeg命令行实现音视频特效及复杂滤镜应用
📄 一句话概括

两个 fade 串在一条链上,就是一整条片子的头尾。 音频那边叫 afade,参数名一模一样,别忘了写。

📝 本节和前面几节的关系

上一节把参数拆开讲了,这一节讲怎么把它们拼成一条能用的命令。 重点在三处:链上顺序、音频配套、目标色。

一、两个 fade 串起来

-vf 里用逗号连,两个 fade 各管一头:

ffmpeg -i s7.mp4 -vf "fade=t=in:st=0:d=0.5,fade=t=out:st=1.5:d=0.5" out.mp4

顺序不重要——两个 fade 作用在互不重叠的时间段,谁写在前面结果都一样。 但它们不能重叠:重叠的部分会被乘两次,出来的不是「淡」而是「更淡」。

重叠的后果很容易看错:你以为写错了方向,其实是系数被乘了两遍。 所以写之前先在心里过一遍:[0,0.5] 与 [1.5,2.0] 两段,中间 1 秒干净。

二、音频要配 afade

视频是 fade,音频是 afade,多一个 a 前缀:

视频 音频 作用


fade afade 音量从无到有 / 从有到无 t=in t=in 参数名一样 d / st d / st 参数名一样

ffmpeg -i s7.mp4 -vf fade=t=in:st=0:d=1 -af afade=t=in:st=0:d=1 out.mp4

只做视频不做音频,开头会出现「画面已经亮起来、声音还是满的」那种错位感。 两边都写一遍,只多几个字符,别省。

afade 还有个 curve 参数能换曲线(tri、qsin、hsin 等), 视频那边的 fade 没有这个参数。两边曲线不一致时,听起来与看起来会有一点点不同步。

三、淡到什么颜色

color 参数换目标色,实测第 0 帧就是那个颜色:

color 写什么 第 0 帧左上角 第 6 帧左上角


black [0, 0, 0] [0, 0, 0] white [255, 255, 255] [137, 137, 137] red [255, 0, 0] [137, 0, 0]

black 那一行第 6 帧还是黑的,因为素材左上角本身就是黑的; 换成 white 就能看清楚:第 0 帧纯白,第 6 帧已经掉到 137,一半过一点。

淡到白常用在「回忆闪回」「过曝转场」,淡到红用在「警报」「危险提示」。 颜色名与十六进制都认,0x336699 这种写法在 color 滤镜里也通用。

四、alpha 只在带 alpha 的格式里有效

alpha=1 时 fade 不动颜色,只把透明度从 0 拉到 255。 这只有在输出格式带 alpha 平面(yuva420p、rgba)时才有意义。

普通 yuv420p 没有 alpha 平面,写了等于没写—— 跟 7.8 倒影那条坑完全一样:format=rgba 不能省。

它的实际用途是「淡入一段带透明通道的素材再叠上去」, 比如把一条水印淡入到主画面上:先 format=rgba,再 fade=alpha=1,最后 overlay。

五、动手 Demo:一条链两头淡、三种目标色

下面这段代码先跑一遍完整的「头淡入、尾淡出」,看首尾各三帧的均值; 再把 color 换成黑、白、红三档,对比第 0 帧与第 6 帧。

import av
from fractions import Fraction

TB = Fraction(1, 25)
c0 = av.open(u"s7.mp4")
BASE = [fr.to_ndarray(format=u"yuv420p") for fr in c0.decode(video=0)]
c0.close()

def run(filters, n=50):
    g = av.filter.Graph()
    b = g.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
    p = b
    for name, args in filters:
        f = g.add(name, args) if args else g.add(name)
        p.link_to(f)
        p = f
    sk = g.add(u"buffersink")
    p.link_to(sk)
    g.configure()
    out = []
    for i in range(n):
        vf = av.VideoFrame.from_ndarray(BASE[i % len(BASE)], format=u"yuv420p")
        vf.pts = i
        vf.time_base = TB
        b.push(vf)
        while True:
            try:
                out.append(sk.pull().to_ndarray(format=u"rgb24"))
            except Exception:
                break
    b.push(None)
    while True:
        try:
            out.append(sk.pull().to_ndarray(format=u"rgb24"))
        except Exception:
            break
    return out

print(u"--- 淡入淡出写在一条链上")
o = run([(u"fade", u"t=in:st=0:d=0.5"),
         (u"fade", u"t=out:st=1.5:d=0.5")], 50)
print(u"  第 0 帧 %.0f  第 6 帧 %.0f  第 13 帧 %.0f"
      % (o[0].mean(), o[6].mean(), o[13].mean()))
print(u"  第 37 帧 %.0f  第 44 帧 %.0f  第 49 帧 %.0f"
      % (o[37].mean(), o[44].mean(), o[49].mean()))

print(u"--- color 换目标色,看第 0 帧与第 6 帧的左上角")
for c in (u"black", u"white", u"red"):
    a = run([(u"fade", u"t=in:st=0:d=0.5:color=%s" % c)], 8)
    print(u"  color=%-6s 第 0 帧 %s  第 6 帧 %s"
          % (c, [int(v) for v in a[0][0, 0]], [int(v) for v in a[6][0, 0]]))

实跑输出:

--------------------------------------------------------------------
7.11.2 fade 滤镜的用法
--------------------------------------------------------------------

**① 淡入与淡出写在同一条链上**

**`-vf` 里用逗号连起来,两个 fade 各管一头:**

`ffmpeg -i s7.mp4 -vf "fade=t=in:st=0:d=0.5,fade=t=out:st=1.5:d=0.5" out.mp4`

**顺序不重要——两个 fade 作用在不同时间段,谁在前面结果都一样。**
**但要是两个时间段重叠了,后写的那个会把先写的效果再压一遍,那就不是「淡」而是「更淡」。**

**② 音频也要淡,滤镜名字加个 a**

  视频       音频       作用
  -------------------------------------------------
  fade       afade      音量从无到有 / 从有到无
  t=in       t=in       参数名一样
  d / st     d / st     参数名一样

**`ffmpeg -i s7.mp4 -vf fade=t=in:st=0:d=1 -af afade=t=in:st=0:d=1 out.mp4`**

**只做视频不做音频,开头会出现「画面已经亮起来、声音还是满的」那种错位感。**
**两边都写一遍,只多几个字符。**

**③ 淡到白色 / 淡到红色**

**`color` 参数换目标色,实测第一帧就是那个颜色:**

  color 写什么     第 0 帧左上角像素     第 6 帧左上角像素
  ------------------------------------------------------------
  black            [0, 0, 0]             [0, 0, 0]
  white            [255, 255, 255]       [137, 137, 137]
  red              [255, 0, 0]           [137, 0, 0]

**`color=white` 时第 0 帧是纯白,第 6 帧已经是一半亮度。**
**淡到白常用在「回忆闪回」,淡到红用在「警报」,都只是换个色名。**

**④ 只淡 alpha 通道**

**`alpha=1` 时 fade 不动颜色,只把透明度从 0 拉到 255。**
**这只有在输出格式带 alpha(yuva420p、rgba)时才有意义,**
**普通 yuv420p 没有 alpha 平面,写了等于没写——跟 7.8 倒影那条坑一模一样。**

前三行是片头:0 → 65 → 144,第 13 帧就满幅了。 后三行是片尾:143 → 77 → 22,一路往下掉,最后还剩两成。

片尾没掉到 0 是因为片子只有 50 帧,淡出从第 38 帧才开始, 剩下 12 帧只够走完 96%,最后一帧还剩一点亮度。

第二种的三行是目标色对比:black 那行第 6 帧看不出变化(素材左上角本来就黑), white 与 red 就能清楚看到「白/红打底,慢慢显出原画面」。

30 秒实验

  1. 删掉一个 fade:只留 t=in,看片尾是不是一直满幅。
  2. 重叠试试:把淡出的 st 改成 0,看两个 fade 叠在一起是什么样。
  3. 试 afade:给命令加 -af afade=t=in:st=0:d=0.5,听开头有没有渐强。

常见误区

  1. 只写视频不写音频。开头音画不同步,看着很别扭。
  2. 两个 fade 的时间段重叠。系数被乘两遍,出现「双重淡」。
  3. 对 yuv420p 用 alpha=1。没有 alpha 平面,写了等于没写。
自测三问(点开对答案) 1. 音频的对应滤镜叫什么? —— `afade`,参数名与 `fade` 一样。 2. 两个 fade 的时间段能重叠吗? —— 能写,但系数会乘两遍,效果不是「淡」而是「更淡」。 3. `alpha=1` 需要什么前提? —— 输出格式必须带 alpha 平面,比如 `yuva420p` 或 `rgba`。

相关笔记