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 秒实验
- 限帧:给前两行加
duration=1,看是不是变成 25 帧。 - 换色:把
c=black换成c=white,看 Y 平面变成 235 还是 255。 - 量 sar:写
sar=2,看输出帧的 sample_aspect_ratio 变成多少。
常见误区
- 以为 nullsrc 就是黑屏。它的平面全是 0,转 RGB 会带绿味。
- 忘写 duration。命令会一直跑,磁盘能写满。
- 拿 nullsrc 当黑幕用。要黑幕用
color=c=black。
自测三问(点开对答案)
1. nullsrc 的默认尺寸与帧率? —— 320×240、25 帧每秒。 2. duration=3 能出多少帧? —— 75 帧(25 帧每秒乘 3 秒)。 3. 为什么说 nullsrc 不是标准黑? —— 它三个平面全是 0,转成 RGB 是 `[0, 135, 0]`,带绿味。- ☐ (本节完,下一节 7.4.5 使用 nullsrc 生成一段空屏视频)