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

-f lavfi -i nullsrc=s=1280x720:d=5 就能凭空出一段空屏视频。 纯空画面也占字节,面积越大占得越多,1280×720 是 160×90 的七倍多。

📝 本节和前面几节的关系

上一节讲参数,这一节把它真编成文件。 空屏视频是剪片、垫片、占位最常用的一招。

一、命令行

一行就够:

ffmpeg -f lavfi -i nullsrc=s=1280x720:d=5 -c:v libx264 -pix_fmt yuv420p out.mp4

-f lavfi 说明这个输入不是文件,是滤镜源。-i 后面跟的是源的名字和参数。 -c:v libx264 不能省:lavfi 给的是原始帧,得有人把它压成 H.264。

-pix_fmt yuv420p 也建议显式写上,否则可能出现「电脑能放、手机打不开」。 这个坑在这一节末尾会细说。

二、三个尺寸量大小

各编 50 帧,看文件多大:

画布 帧数 文件大小 时长


160×90 50 4519 字节 2.00 秒 320×240 50 11047 字节 2.00 秒 1280×720 50 35147 字节 2.00 秒

纯空的画面也占空间,而且面积越大占得越多:最大那块是最小那块的 7.8 倍。 因为编码器不认「这是空的」,它照样要把每一块的残差算一遍、熵编码一遍。

面积比是 64 倍(1280×720 对 160×90),字节只差 7.8 倍, 因为大片纯色里的重复块能被压得很狠。这也是「纯色压缩比高」的具体体现。

三、它到底用在哪

三种典型用法:

用法 说明


垫片 两段视频之间的过渡空白 画布 当 overlay 的底,往上贴东西 占位 测试脚本时先出一段能播的视频

还有一种是「凑时长」:片头片尾要求整分钟,差几秒就拿空屏补上。 补的时候记得用 color 而不是 nullsrc,否则补出来带绿味。

四、两个坑

第一,忘写 duration 就无限长。 它不会自己停,命令行会一直跑到你按 Ctrl+C,磁盘能被写满。

第二,-pix_fmt 最好显式写上。 libx264 能接好几种像素格式,ffmpeg 只会挑一个跟输入最接近的。 挑到 yuv444p 时电脑上的播放器还能放,手机与浏览器就打不开了。

两个坑的共同点是「不报错」。 前者是命令不结束,后者是文件出来了却放不了——都得靠自己检查,命令一声不吭。

五、换成 color 也一样

把 nullsrc 换成 color=c=black,其余一个字不用改—— 因为它们是同一种东西:lavfi 源。参数名都叫 size、rate、duration。

所以这一节学的不只是 nullsrc,而是「怎么用 lavfi 造一个输入」。 testsrc、smptebars、color、nullsrc 全是这一套,换名字就换内容。

testsrc 出彩条,smptebars 出标准测试图,都拿来做画面校验。 要「一段能播的东西」,随便挑一个都比空文件强。

六、动手 Demo:三块空画布,三个文件大小

下面这段代码把三个尺寸各编 50 帧,量字节与时长。 注意全程只从 nullsrc 取帧,一个文件都没读。

import os
import av

def make(dst, sid, w, h, nf):
    g = av.filter.Graph()
    s0 = g.add(u"nullsrc", sid)
    sk = g.add(u"buffersink")
    s0.link_to(sk)
    g.configure()
    oc = av.open(dst, u"w")
    st = oc.add_stream(u"libx264", rate=25)
    st.width, st.height = w, h
    st.pix_fmt = u"yuv420p"
    n = 0
    while n < nf:
        o = sk.pull()
        for pk in st.encode(o):
            oc.mux(pk)
        n += 1
    for pk in st.encode(None):
        oc.mux(pk)
    oc.close()
    c = av.open(dst)
    d = c.duration / 1e6
    c.close()
    return os.path.getsize(dst), d

for w, h in ((160, 90), (320, 240), (1280, 720)):
    nb, d = make(u"ns_%dx%d.mp4" % (w, h),
                 u"size=%dx%d:rate=25" % (w, h), w, h, 50)
    print(u"%-10s 50 帧  %d 字节  %.2f 秒" % (u"%dx%d" % (w, h), nb, d))

实跑输出:

--------------------------------------------------------------------
7.4.5 使用 nullsrc 生成一段空屏视频
--------------------------------------------------------------------

**① 命令行长这样**

`ffmpeg -f lavfi -i nullsrc=s=1280x720:d=5 -c:v libx264 -pix_fmt yuv420p out.mp4`

**`-f lavfi` 说明这个输入不是文件,是滤镜源。`-i` 后面跟的是源的名字和参数。**
**`-c:v libx264` 不能省:lavfi 给的是原始帧,得有人把它压成 H.264。**

**② 三个尺寸各编 50 帧,看文件多大**

  画布         帧数     文件大小       时长
  ------------------------------------------------
  160×90       50       4519 字节      2.00 秒
  320×240      50       11047 字节     2.00 秒
  1280×720     50       35147 字节     2.00 秒

**纯空的画面也占空间,而且面积越大占得越多:最大那块是最小那块的 7.8 倍。**
**因为编码器不认「这是空的」,它照样要把每一块的残差算一遍、熵编码一遍。**

**③ 它到底用在哪**

  用法     说明
  -----------------------------------------
  垫片     两段视频之间的过渡空白
  画布     当 overlay 的底,往上贴东西
  占位     测试脚本时先出一段能播的视频

**④ 两个坑**

**第一,忘写 duration 就无限长。**
**它不会自己停,命令行会一直跑到你按 Ctrl+C,磁盘能被写满。**

**第二,`-pix_fmt` 最好显式写上。**
**libx264 能接好几种像素格式,ffmpeg 只会挑一个跟输入最接近的。**
**挑到 yuv444p 时电脑上的播放器还能放,手机与浏览器就打不开了。**

**⑤ 换成 color 也一模一样**

**把 `nullsrc` 换成 `color=c=black`,其余一个字不用改——**
**因为它们是同一种东西:lavfi 源。参数名都叫 size、rate、duration。**

三行输出跟本节那张表逐条对上:4519、11047、35147,时长全是 2.00 秒。

50 帧除以 25 帧每秒正好 2 秒,所以时长一致、只有字节数在变。 字节数的增长远慢于面积的增长,这就是纯色可压缩的直观证据。

代码里是手动数满 50 帧就停,没有依赖 duration。 这样即使源是无限的也不会跑飞——比写 duration 更保险。

30 秒实验

  1. 换帧数:把 50 改成 250(10 秒),看字节是不是差不多线性增长。
  2. 换编码器:把 libx264 换成 mpeg4,看同一块空画布谁更小。
  3. 加音频:给它配一条 anullsrc,做成既有画面又有声的垫片。

常见误区

  1. 不写 duration 也不限帧。命令会一直跑,直到磁盘满。
  2. 省掉 -pix_fmt。可能出 yuv444p,手机与浏览器放不了。
  3. 以为空画面文件会很小。1280×720 五十帧也有三十多 KB。
自测三问(点开对答案) 1. `-f lavfi` 是什么意思? —— 说明这个输入是滤镜源,不是磁盘上的文件。 2. 为什么纯空画面还占字节? —— 编码器照样要算残差、做熵编码,纯色只是压得比较狠。 3. 1280×720 是 160×90 面积的 64 倍,为什么字节只差 7.8 倍? —— 大片纯色的重复块压缩率高,面积涨得比字节快。

相关笔记