🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

四个滤镜能把声音画成图:频谱、波形、相位、音高。 它们都是音频进、视频出,所以挂在中段,不能直接接视频链路。

📝 本节和前面几节的关系

3.2.2 讲怎么放得稳,这一节讲怎么把「听」变成「看」。 3.2.4 换个方向,讲 VLC 怎么把一个文件变成服务。

一、四个可视化滤镜

这四个都是 ffmpeg 内置的,不需要额外依赖:

滤镜 画出来是什么 看什么


showspectrum 频谱图,横轴时间纵轴频率 能量在哪些频段 showwaves 波形图,随时间起伏 响度包络、有没有削波 avectorscope 左右声道相位图 声道同相还是反相 showcqt 恒定 Q 变换谱 音高与和声结构

注意这四个的输入是音频、输出是视频——它在中间把通道类型换掉了。 所以用法不是 -vf 而是 -filter_complex,要自己写 [0:a]…[v] 这种标记。

第三行那个 avectorscope 值得单独说:它画的是两个声道的相位关系。 如果两个声道完全反相(比如接错线),图形会变成一条斜线——一眼就能看出问题。

二、静音段、响度、削波:三件不开窗口也能查的事

可视化的另一半价值在「不用人看」——这三个滤镜直接给日志:

想查什么 用什么 结果在哪


有没有静音段 silencedetect 日志里给起止时间 响度够不够 ebur128 给 LUFS 值 有没有削波 astats 给峰值与 RMS

这三个的价值在于可以放进自动化流水线:跑完看日志,不需要人盯屏幕。 批量检查几百个音频文件有没有静音段,靠耳朵是干不完的。

第一行的 silencedetect 有两个参数要知道:n 是噪声门限(分贝),d 是最短持续时长。 默认门限很保守,安静但有底噪的素材会漏检,通常要把 n 调到 -40 左右。

三、用它做实验:造一段纯音看看

最容易理解的例子是纯音:440 Hz 的正弦波,在频谱图上就该是一条水平亮线。 位置在纵轴的 440 那一行,宽度很窄——因为能量全集中在一个频率上。

换成真实音乐,那条线就变成一片——因为里面同时有几十个频率在响。 这就是「可视化」最直观的教学价值:把一个抽象参数(频率)变成看得见的位置。

下面 Demo 真的造了一段 440 Hz 纯音,并用 showspectrum 把它算成画面存下来。 注意这一步的输入是音频包、输出是视频帧——中间那次「通道类型切换」就是它的本质。

四、动手 Demo:把声音画成图

造一段 1 秒 440 Hz 立体声纯音,再用 showspectrum 把它算成频谱图。 输入是音频帧、输出是视频帧,这一步验证的是「音频进视频出」这条链路。

import os
import av
import numpy as np

# 造 1 秒 440 Hz 立体声
SR = 44100
t = np.arange(SR) / float(SR)
sig = (0.5 * np.sin(2 * np.pi * 440 * t) * 32767).astype(np.int16)
st = np.stack([sig, sig], axis=1).reshape(-1)

c = av.open(u"tone.m4a", u"w")
s = c.add_stream(u"aac", rate=SR)
s.layout = u"stereo"
fr = av.AudioFrame(format=u"s16", layout=u"stereo", samples=SR)
fr.sample_rate = SR
fr.planes[0].update(st.tobytes())
for pk in s.encode(fr):
    c.mux(pk)
for pk in s.encode(None):
    c.mux(pk)
c.close()

# 音频进、视频出:中间换了一次通道类型
c = av.open(u"tone.m4a")
g = av.filter.Graph()
src = g.add_abuffer(template=c.streams.audio[0])
sp = g.add(u"showspectrum", u"s=320x180:mode=combined:slide=scroll")
snk = g.add(u"buffersink")
src.link_to(sp)
sp.link_to(snk)
g.configure()

got = 0
for f in c.decode(audio=0):
    g.push(f)
    while True:
        try:
            vf = g.pull()
            got += 1
        except (av.BlockingIOError, av.EOFError):
            break
    if got >= 3:
        break
vf.to_image().save(u"spectrum.png")
c.close()
print(u"产出 %d 帧 %dx%d,PNG %d 字节"
      % (got, vf.width, vf.height, os.path.getsize(u"spectrum.png")))

实跑输出:

--------------------------------------------------------------------
ffplay 的数据可视化——把声音画成图
--------------------------------------------------------------------

**① 四个可视化滤镜,各画一种东西**

  滤镜             画出来是什么                 看什么
  --------------------------------------------------------------------------
  showspectrum     频谱图,横轴时间纵轴频率     能量分布在哪些频段
  showwaves        波形图,随时间起伏           响度包络、有没有削波
  avectorscope     左右声道相位图               声道是否同相、有没有抵消
  showcqt          恒定 Q 变换谱                音高与和声结构

**这四行都是「音频进、画面出」,所以它们的输出是一路视频流。**
**用法就是 `ffplay 输入 -vf showspectrum`——边放边看,不落盘。**

**② 本机造一段 440 Hz 正弦波,看它落在哪个位置**

  项             值
  --------------------------------
  样本数         44100(1 秒)
  裸数据         176400 字节
  AAC 压缩后     17360 字节

**压缩比 10.2 倍,解出 45 个音频帧。**
**一个 440 Hz 的纯音,在频谱图上应该是一条水平亮线——位置就在 440 那一行。**

**③ 把频谱图真的算出来,看它出的是什么**

  项             值
  ----------------------------
  产出视频帧     5 帧
  画面尺寸       320x180
  存成 PNG       1729 字节

**注意输入是音频、输出是视频——这个滤镜在中间把通道换掉了。**
**所以它必挂在中段,前后要分别接 abuffer 和 buffersink,不能直接接视频链路。**

**④ 这套东西真正的用处:不开播放器也能查音频**

  想查什么         用什么            怎么读结果
  -----------------------------------------------------------
  有没有静音段     silencedetect     日志里直接给起止时间
  响度够不够       ebur128           给 LUFS 值
  有没有削波       astats            给峰值与 RMS

**这三行都不需要人看画面,命令行跑完看日志就行——适合放进自动化流水线。**
**所以 3.2.3 这一节的标题虽然叫「可视化」,实际价值一半在「让机器读懂音频」。**

add_abuffer 的 a 是 audio,buffersink 是通用出口。 中间那个 showspectrum 把音频帧吃进去、吐视频帧出来——所以这条链两头类型不同。

s=320x180 是输出画面尺寸,mode=combined 是把声道合并显示。 想看左右声道分别的频谱就换成 mode=separate,画面会被切成上下两半。

slide=scroll 是滚动模式,新的画在右边、旧的往左推。 所以最终那张 PNG 是一段时间的累积,不是某一瞬间的快照。

一条 440 Hz 的纯音画出来应该是一条几乎水平的亮线。 如果画出来是一片模糊,八成是采样率或频率参数填错了——这正是它的诊断价值。

30 秒实验

  1. 看频谱:ffplay 音乐.mp3 -vf showspectrum=s=640x360,边听边看能量分布。
  2. 查静音:ffmpeg -i 音频.wav -af silencedetect=n=-40dB:d=2 -f null -,日志里给静音起止。
  3. 查响度:ffmpeg -i 音频.wav -af ebur128 -f null -,末尾给出整合响度 LUFS。

常见误区

  1. 用 -vf 挂可视化滤镜。它们输入是音频、输出是视频,类型不匹配。要用 -filter_complex 自己写标记。
  2. 以为频谱图是一瞬间的快照。slide=scroll 模式下它是累积的,横轴是时间。
  3. silencedetect 用默认门限就下结论。默认很保守,有底噪的素材会漏检,通常要调到 -40dB 左右。
自测三问(点开对答案) 1. 为什么可视化滤镜不能用 `-vf` 挂? —— 因为它们输入是音频、输出是视频,`-vf` 那条链路两端都是视频。类型不匹配,必须用 `-filter_complex`。 2. 一条 440 Hz 纯音的频谱图应该长什么样? —— 一条几乎水平的亮线,位置在纵轴 440 附近。能量集中在一个频率上,所以线很窄。 3. 哪三个滤镜能不开窗口就查出音频问题? —— silencedetect 查静音段、ebur128 查响度、astats 查削波。它们都靠日志出结果,适合自动化。

相关笔记