片头黑场、中间静音怎么一键剪掉?ffmpeg 的 blackdetect/silencedetect 实战
下载了一堆录播,片头总有 5 秒黑屏、中间插播一段静音广告,手动剪太累了。其实 ffmpeg 自带两个"侦测器":blackdetect 和 silencedetect,能先把黑场/静音的位置找出来,再决定怎么切。这篇讲我怎么把它们用到批量处理上的,以及两个大坑。
先别急着切,先"探"一下
直接切容易翻车,因为剪切点必须落在关键帧上。所以正确姿势是两步:先用滤镜只探测不输出,把时间戳打出来,再根据结果精确切。
ffmpeg -i rec.mp4 -vf "blackdetect=d=0.5:pix_th=0.10" -an -f null -
d=0.5 表示"连续黑超过 0.5 秒才算",pix_th=0.10 是判定为黑的亮度阈值(0~1,越小越严格)。跑完会在 stderr 里吐:
[blackdetect @ ...] black_start:5.12 black_end:9.87 black_duration:4.75
静音同理,用音频滤镜:
ffmpeg -i rec.mp4 -af "silencedetect=n=-35dB:d=1.0" -vn -f null -
n=-35dB 是静音门槛,吵一点的录像设到 -30dB 更稳;d=1.0 是持续 1 秒以上才算静音。输出长这样:
[silencedetect @ ...] silence_start: 42.3 | silence_end: 48.1
坑一:探测结果在 stderr 不在 stdout
我第一次写脚本,傻乎乎去读 stdout,啥也没拿到。这俩滤镜只往 stderr 打日志,得 2>&1 重定向了用正则抓 black_start/silence_start。Python 里可以用 subprocess.run(..., stderr=subprocess.PIPE, text=True) 再 re.findall。
坑二:silence_start 没有配对的 end
如果静音出现在文件尾巴,你只会看到 silence_start 没有 silence_end——因为到结尾就截断了。这种情况把结尾当静音处理就行。
自动化:把"静音段"去掉
思路是把视频按静音边界切成多段,再 concat 回去。但音频切片用 -c copy 会不干净,所以我一般只对静音段做重编码裁切。一个偷懒的办法,用 silenceremove:
ffmpeg -i rec.mp4 -af "silenceremove=start_periods=1:stop_periods=-1:detection=avg:window=0.5:min_silence=1.0:dB=-35" out.mp4
silenceremove 直接吐掉静音、把有效声音拼起来,比"先探测再切再拼"省一步。代价是要重编码音频,慢一点。
黑场目前没有等价的一行滤镜,得靠探测结果去切视频;或者反过来,用 select 帧选择那种,但太折腾,我通常直接拿探测时间戳写脚本切。
批量脚本长啥样
核心就三行逻辑:
subprocess跑上面的探测命令,抓时间戳;- 把"有效段"算出来(总时长减去黑场/静音区间);
- 对每个有效段
-ss/-to切,最后 concat。
我踩过最狠的坑是:探测用的 -an/-vn 一定要加,否则音视频都跑滤镜,探测时间翻倍还容易把内存吃满。
一点心得
blackdetect/silencedetect 适合"粗剪"——把明显垃圾段干掉。真要精修还是得进剪辑软件。另外阈值别照搬我的,-35dB 对我家录音合适,你那边可能要 -40 才不误杀。先拿一段试,看 stderr 输出对不对得上画面,再批量跑。