两秒片子,头尾各淡 0.5 秒,实测两头各 13 帧过渡。 前后要接别的片段时,得先垫一段黑场再淡入。
7.11.1 讲参数、7.11.2 讲写法,这一节把它们拼成一条能直接用的成品命令。 黑场那一步用到 7.4.6 的 color 滤镜。
一、头尾各淡 0.5 秒
完整命令(视频与音频都淡):
ffmpeg -i s7.mp4 -vf "fade=t=in:st=0:d=0.5,fade=t=out:st=1.5:d=0.5" \
-af "afade=t=in:st=0:d=0.5,afade=t=out:st=1.5:d=0.5" head.mp4
两秒片子上,0~0.5 秒淡入、1.5~2.0 秒淡出,中间 1 秒是干净的。
二、实测首尾曲线
把首尾各取六帧,看均值爬升与下坠:
位置 画面均值 位置 画面均值
开头第 0 帧 0 结尾第 38 帧 143 开头第 3 帧 33 结尾第 41 帧 110 开头第 6 帧 65 结尾第 44 帧 77 开头第 9 帧 99 结尾第 47 帧 43 开头第 12 帧 132 结尾第 49 帧 22 开头第 13 帧 144 — —
第 0 帧全黑、第 13 帧满幅,正好 13 个帧距;结尾从第 38 帧开始掉, 第 49 帧只剩 22,也是 12 个帧距。两边对称,都是 0.5 秒。
片尾没到 0 是因为片子结束得早:淡出从 1.5 秒起算,走到 2.0 秒才归零,
而第 50 帧已经到头了,差半帧。真做成品时把 d 收小一点,或者让素材多给几帧。
三、先垫黑场再淡入
如果这段前面还要接别的片子,直接淡入会在接缝处突跳。 标准做法是「黑场 0.3 秒 + 淡入 0.5 秒」:
ffmpeg -f lavfi -i color=c=black:s=160x90:r=25:d=0.3 -i s7.mp4 \
-filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" out.mp4
color 就是 7.4.6 讲过的纯色源,d=0.3 造 0.3 秒黑场;
concat=n=2 表示两段拼接,v=1:a=0 表示只接视频、不接音频。
为什么垫黑而不是直接淡:黑场给观众一个「这里该停一下」的信号, 两段片子之间不垫东西,观感上像被硬切了一刀。
四、三个容易踩的点
做片子时最常见的三个错:
现象 原因
淡入没生效 st 写大了,前几帧已经过了淡化期
淡出没到全黑 d 超出了片长,最后停在半黑
两头都在闪 两个 fade 的时间段重叠了
第二条最容易犯:两秒的片子写 st=1.5:d=1.0,淡化要跑到 2.5 秒,
可片子 2.0 秒就结束了,最后只淡到一半。写之前先算一遍 st+d 有没有超片长。
第三条的判断办法:把输出逐帧取均值,如果曲线是「先降后升」的 V 形, 那就是两个 fade 叠了——单次淡入只会单调上升。
五、动手 Demo:头尾曲线、偏移帧数统计
下面这段代码把「头淡入、尾淡出」跑一遍,首尾各取六帧看曲线; 最后统计有多少帧偏离了原帧,验证过渡长度是不是 0.5 秒。
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
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"--- 片头:前 14 帧")
for i in (0, 3, 6, 9, 12, 13):
print(u" 第 %2d 帧 %.0f" % (i, o[i].mean()))
print(u"--- 片尾:后 12 帧")
for i in (38, 41, 44, 47, 49):
print(u" 第 %2d 帧 %.0f" % (i, o[i].mean()))
print(u"--- 头尾各有几帧在变")
ref = run([], 50)
def dev(i):
return o[i].mean() / max(ref[i].mean(), 1e-6) < 0.98
n_in = sum(1 for i in range(25) if dev(i))
n_out = sum(1 for i in range(25, 50) if dev(i))
print(u" 前 25 帧偏离 %d 帧,后 25 帧偏离 %d 帧,合计 %d 帧"
% (n_in, n_out, n_in + n_out))
实跑输出:
--------------------------------------------------------------------
7.11.3 fade 滤镜的案例
--------------------------------------------------------------------
**① 一条完整的片头片尾**
**两秒片子,头尾各淡 0.5 秒:**
`ffmpeg -i s7.mp4 -vf "fade=t=in:st=0:d=0.5,fade=t=out:st=1.5:d=0.5" \`
`-af "afade=t=in:st=0:d=0.5,afade=t=out:st=1.5:d=0.5" head.mp4`
**实测首尾各取六帧,看均值爬升与下坠:**
位置 第几帧 画面均值
----------------------------------------
开头第 0 帧 0 0
开头第 3 帧 3 33
开头第 6 帧 6 65
开头第 9 帧 9 99
开头第 12 帧 12 132
开头第 13 帧 13 144
结尾第 38 帧 38 143
结尾第 41 帧 41 110
结尾第 44 帧 44 77
结尾第 47 帧 47 43
结尾第 49 帧 49 22
**第 0 帧是 0、第 13 帧满幅;结尾从第 38 帧开始往下掉,第 49 帧只剩 22。**
**两头都留了大约 13 帧的过渡,正好是 0.5 秒。**
**② 为什么要留「黑场」而不是直接淡**
**纯淡入淡出是「从黑到画面」,本质上是拿一段黑盖上去。**
**如果前后还要接别的片段,一般写成「黑场 0.3 秒 + 淡入 0.5 秒」,**
**这样接缝处有黑垫着,转场不会突跳。**
**③ 用 color 生成纯黑场,再 concat 拼起来**
`ffmpeg -f lavfi -i color=c=black:s=160x90:r=25:d=0.3 -i s7.mp4 \`
`-filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" out.mp4`
**`color` 那条就是 7.4.6 讲过的纯色源,`d=0.3` 造 0.3 秒黑场。**
**`concat=n=2` 表示两段拼接,`v=1:a=0` 表示只接视频不接音频。**
**④ 三个容易踩的点**
现象 原因
------------------------------------------------------
淡入没生效 `st` 写大了,前几帧已经过了淡化期
淡出没到全黑 `d` 超出了片长,最后停在半黑
两头都在闪 两个 fade 的时间段重叠了
**第二条最常见:两秒的片子写 `st=1.5:d=1.0`,1.0 秒的淡化要跑到 2.5 秒,**
**可片子 2.0 秒就结束了,最后只淡到一半。写之前先算一遍 `st+d` 有没有超片长。**
片头那六行是 0 → 33 → 65 → 99 → 132 → 144,标准的线性爬升。
片尾那五行是 143 → 110 → 77 → 43 → 22,同样是线性下坠, 只是起点在第 38 帧,还没走完片子就结束了。
最后一行把「偏离原帧超过 2%」的帧分段统计出来:前面 13 帧、后面 11 帧。 合计 24 帧,比「13+13」少了 2 帧,因为片尾只走到第 49 帧,淡出没走完。
30 秒实验
- 改时长:把两处
d=0.5改成d=0.3,看过渡是不是缩到 8 帧。 - 改成淡白:给淡出加
color=white,看片尾是不是变白。 - 加黑场:用
color造 0.3 秒黑场再 concat,看总长变成多少。
常见误区
st+d超过片长。淡出到不了全黑,看着像没做完。- 直接淡入不加黑场。接别的片段时接缝会突跳。
- 两个 fade 的时间段重叠。曲线会变成 V 形,不是单调的。
自测三问(点开对答案)
1. 0.5 秒淡入在 25 帧率上占几帧? —— 13 个帧距,第 0 帧到第 13 帧。 2. 前后要接别的片段,怎么处理接缝? —— 先垫 0.3 秒黑场,再淡入,用 `color` 加 `concat`。 3. 怎么判断两个 fade 叠了? —— 逐帧均值曲线变成 V 形——先降后升,而单次淡入只会单调上升。- ☐ 7.11 完,下一节 7.12 黑白效果