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

nullsrc 三个参数:size、rate、duration,默认 320×240、25 帧、不限时长。 它的三个平面全是 0,不是标准黑,要黑幕得用 color。

📝 本节和前面几节的关系

上一节用它造了一块布,这一节把它的参数与那个「不是黑」的坑讲清。 这是本章唯一一个「凭空造画面」的源,后面还会反复用到。

一、三个参数

参数少得意外:

参数 含义 默认值


size / s 画布尺寸 320×240 rate / r 帧率 25 duration / d 出多少秒 不限 sar 像素宽高比 1

每个参数都有个短名:s、r、d,命令行里写短的省事。 sar 是像素的宽高比,一般不用管,除非要出变形宽银幕那种特殊画面。

二、实测默认值与帧数

四种写法各数一遍,看能吐出多少帧:

写法 尺寸 帧数


nullsrc 什么都不写 320×240 数到 300 还没完 size=160x90:rate=25 160×90 数到 300 还没完 再加 duration=2 160×90 50 帧就停 再加 duration=3 160×90 75 帧就停

2 秒 × 25 帧 = 50 帧,3 秒 × 25 帧 = 75 帧,一秒不差。 不写 duration 就是无限长,沙箱里数到 300 帧还没停——它是真无限。

落盘的时候必须自己限帧或者写 -t,否则命令会一直跑下去。 这是 nullsrc 最容易闯的祸:以为跑完了,其实还在往磁盘里写。

三、底不是标准黑

把平面拆出来看,差别一眼就出来了:

源 Y 平面 U/V 平面 转成 RGB


nullsrc 全是 0 全是 0 [0, 135, 0] color=c=black 全是 16 全是 128 [0, 0, 0]

nullsrc 三个平面全是 0。Y=0 是「比黑还黑」,色度 0 是「没有颜色信息」, 转到 RGB 会带出一层绿味([0, 135, 0]),跟真正的黑([0, 0, 0])不是一回事。

要一块真黑底,用 color=c=black;nullsrc 适合当占位、当底衬,不适合当黑幕。 限制范围(16 到 235)里 Y=16 才是黑,Y=0 是超出范围的值,播放器会当非法数据处理。

四、常见用法

它主要出现在三个地方:

场景 为什么用它


拼画布、扩边 凭空给一块指定大小的底 当占位轨 接在 concat 里凑流数 压测编码器 不限时长,想跑多久跑多久

注意它跟 color=black 不是替代关系:一个给「空白」,一个给「黑色」。 要做黑幕、黑边、黑场,一律用 color;要一块什么都没有的底,才用 nullsrc。

五、动手 Demo:数帧数、拆平面

下面这段代码把四种写法的帧数各数一遍,再把两个源的平面最小值打出来。 最后那段就是「nullsrc 不是黑」的直接证据。

import av
from fractions import Fraction

TB = Fraction(1, 25)
c0 = av.open(u"s7.mp4")
fr = next(c0.decode(video=0))
base = fr.to_ndarray(format=u"yuv420p")
full = fr.to_ndarray(format=u"rgb24")
W, H = fr.width, fr.height
c0.close()

def probe(name, args, cap=300):
    g = av.filter.Graph()
    s0 = g.add(name, args)
    sk = g.add(u"buffersink")
    s0.link_to(sk)
    g.configure()
    n = 0
    info = None
    while n < cap:
        try:
            o = sk.pull()
        except Exception:
            break
        if info is None:
            pl = []
            for i, p in enumerate(o.planes):
                vw = o.width if i == 0 else o.width // 2
                v = bytes(p)[:vw]
                pl.append((min(v), max(v)))
            info = (o.width, o.height, pl)
        n += 1
    return n, info

print(u"--- 帧数")
for args in (u"", u"size=160x90:rate=25", u"size=160x90:rate=25:duration=2",
             u"size=160x90:rate=25:duration=3"):
    n, info = probe(u"nullsrc", args)
    print(u"%-34s %d 帧 最小 %d" % (args or u"(默认)", n, info[2][0][0]))

print(u"--- 平面值")
for name, args in ((u"nullsrc", u"size=160x90:rate=25"),
                  (u"color", u"c=black:s=160x90:r=25")):
    n, info = probe(name, args, 1)
    print(u"%-8s Y=%s U=%s V=%s" % ((name,) + tuple(x[0] for x in info[2])))

实跑输出:

--------------------------------------------------------------------
7.4.4 nullsrc 参数说明
--------------------------------------------------------------------

**① 三个参数就够用**

  参数             含义           默认值
  -------------------------------------------
  size / s         画布尺寸       320×240
  rate / r         帧率           25
  duration / d     出多少秒       不限
  sar              像素宽高比     1

**② 实测默认值与帧数**

  写法                    尺寸        数到几帧就停
  -------------------------------------------------------
  nullsrc 什么都不写      320×240     数到 300 还没完
  size=160x90:rate=25     160×90      数到 300 还没完
  ...:duration=2          160×90      50 帧就停
  ...:duration=3          160×90      75 帧就停

**2 秒 × 25 帧 = 50 帧,3 秒 × 25 帧 = 75 帧,一秒不差。**
**不写 duration 就是无限长,沙箱里数到 300 帧还没停——它是真无限,**
**落盘的时候必须自己限帧或者写 `-t`,否则命令会一直跑下去。**

**③ 一个必须知道的坑:nullsrc 的底不是标准黑**

  源                Y 平面      U/V 平面     转成 RGB
  ----------------------------------------------------------
  nullsrc           全是 0      全是 0       [0, 135, 0]
  color=c=black     全是 16     全是 128     [0, 0, 0]

**nullsrc 三个平面全是 0。Y=0 是「比黑还黑」,色度 0 是「没有颜色信息」,**
**转到 RGB 会带出一层绿味([0, 135, 0]),跟真正的黑([0, 0, 0])不是一回事。**
**要一块真黑底,用 `color=c=black`;nullsrc 适合当占位、当底衬,不适合当黑幕。**

**④ 常见用法**

  场景             为什么用它
  ---------------------------------------------
  拼画布、扩边     凭空给一块指定大小的底
  当占位轨         接在 concat 里凑流数
  压测编码器       不限时长,想跑多久跑多久

**注意它跟 `color=black` 不是替代关系:一个给「空白」,一个给「黑色」。**

前两行都是「数到 300 还没完」,因为没写 duration; 后两行分别在 50 与 75 停下,跟 2 秒、3 秒对上。

最后两行:nullsrc 三个平面的最小值全是 0,color 的 Y 最小值是 16。 16 就是有限范围里的黑,0 是非法值,这就是差别。

代码里 probe() 用 p.line_size 与 p.buffer_size 算出一行多少字节, 再按行切出来求最小值——因为平面之间可能有对齐填充,不能直接整个 buffer 取 min。

30 秒实验

  1. 限帧:给前两行加 duration=1,看是不是变成 25 帧。
  2. 换色:把 c=black 换成 c=white,看 Y 平面变成 235 还是 255。
  3. 量 sar:写 sar=2,看输出帧的 sample_aspect_ratio 变成多少。

常见误区

  1. 以为 nullsrc 就是黑屏。它的平面全是 0,转 RGB 会带绿味。
  2. 忘写 duration。命令会一直跑,磁盘能写满。
  3. 拿 nullsrc 当黑幕用。要黑幕用 color=c=black。
自测三问(点开对答案) 1. nullsrc 的默认尺寸与帧率? —— 320×240、25 帧每秒。 2. duration=3 能出多少帧? —— 75 帧(25 帧每秒乘 3 秒)。 3. 为什么说 nullsrc 不是标准黑? —— 它三个平面全是 0,转成 RGB 是 `[0, 135, 0]`,带绿味。

相关笔记