「任意顺序」不用改 layout,改输入接进去的顺序就行。 xstack 不检查顺序是否合理,接错了照出片,只是内容错位。
7.10.2 的四宫格是「按输入顺序排」,这一节把顺序变成可调参数。 监控墙换主画面、前后对比换左右,都靠这一手。
一、顺序就是位置
四路纯色按不同顺序接进 xstack,看每格显示什么:
接的顺序 第 1 格 第 2 格 第 3 格 第 4 格
0 1 2 3 红 绿 蓝 白 3 2 1 0 白 蓝 绿 红 0 2 1 3 红 蓝 绿 白
3 2 1 0 整整齐齐倒了过来,0 2 1 3 只换了中间两格。
说明格子位置完全由「接的顺序」决定,跟 -i 的序号一点关系都没有。
只在代码里改一个数组,就能把九宫格排成任意布局,layout 一个字不用动。 这是本节最实用的一句话。
二、命令行怎么写
命令行的写法同理,方括号的顺序就是格子顺序:
ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 -i d.mp4 \
-filter_complex "[3:v][2:v][1:v][0:v]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0" out.mp4
[3:v][2:v][1:v][0:v] 这个顺序就是格子顺序:
排第一的进第 1 格,排第二的进第 2 格。-i 的顺序只决定哪个编号对应哪个文件。
所以换顺序有两种改法:改 -i 的顺序,或者改方括号的顺序。
推荐改方括号——文件路径那部分通常是从配置里读的,不好动。
三、这个特性用来干什么
三个典型场景:
场景 怎么用
监控墙 按通道号摆位置,改顺序不用改 layout 前后对比 原片放左上、处理版放右下 随机轮播 每段随机一个顺序做转场
监控墙是最典型的:十六路信号里哪个通道报警,就把它换到第 1 格。 只改输入顺序这一处,layout 一个字都不用动,改完立刻生效。
前后对比也一样:想「左原右处理」就接 0 1,想「左处理右原片」就接 1 0,
剪片时不用导出两条不同版本。
四、顺序错了不报错
这一点必须记住:xstack 不检查你接的顺序合不合理,接上就摆。 顺序错了片子照样出,只是内容错位,播到一半才发现就晚了。
所以验证顺序得靠「每格内容不同」。 上面那张表用四种颜色就是为了这个:颜色一样,量不出顺序。
真实素材上验证顺序,最稳的办法是给每路挂一个能改亮度的滤镜(比如 lutyuv),
量四格中心均值,四个数互不相同就说明顺序对了。7.10.5 就是这么做的。
五、动手 Demo:三组顺序,量四格颜色
下面这段代码用四路纯色拼四宫格,按三组不同顺序接进去, 再把每格中心色认回颜色名。顺序一换,格子里的颜色跟着换。
import av
import numpy as np
L4 = u"0_0|w0_0|0_h0|w0_h0"
COL = [u"red", u"green", u"blue", u"white"]
def quad(order):
g = av.filter.Graph()
srcs = [g.add(u"color", u"c=%s:s=80x45:r=25" % c) for c in COL]
xs = g.add(u"xstack", u"inputs=4:layout=%s" % L4)
sk = g.add(u"buffersink")
for i, p in enumerate(srcs):
p.link_to(xs, 0, order[i])
xs.link_to(sk)
g.configure()
return sk.pull().to_ndarray(format=u"rgb24")
def name(rgb):
ref = {u"红": (253, 0, 0), u"绿": (0, 127, 0),
u"蓝": (0, 0, 254), u"白": (255, 255, 255)}
for k, v in ref.items():
if all(abs(rgb[i] - v[i]) <= 6 for i in range(3)):
return k
return u"其他"
def mid(a, i, cw=80, ch=45):
j, k = i // 2, i % 2
blk = a[j * ch + ch // 2 - 5:j * ch + ch // 2 + 5,
k * cw + cw // 2 - 5:k * cw + cw // 2 + 5]
return tuple(int(v) for v in blk.reshape(-1, 3).mean(axis=0))
print(u"--- 换接的顺序,格子内容跟着换")
for order in ([0, 1, 2, 3], [3, 2, 1, 0], [0, 2, 1, 3]):
a = quad(order)
got = [name(mid(a, i)) for i in range(4)]
print(u" 接 %s -> %s" % (u" ".join(str(x) for x in order), got))
实跑输出:
--------------------------------------------------------------------
7.10.3 实现【四宫格】的任意顺序
--------------------------------------------------------------------
**① 顺序就是格子位置**
**四路纯色,按不同顺序接进 xstack,看每格显示什么:**
接的顺序 第 1 格 第 2 格 第 3 格 第 4 格
------------------------------------------------------------
0 1 2 3 红 绿 蓝 白
3 2 1 0 白 蓝 绿 红
0 2 1 3 红 蓝 绿 白
**`3 2 1 0` 那行整整齐齐倒了过来,`0 2 1 3` 只换了中间两格。**
**所以「任意顺序」不需要改 layout,改输入接的顺序就行。**
**② 命令行长什么样**
`ffmpeg -i a.mp4 -i b.mp4 -i c.mp4 -i d.mp4 \`
`-filter_complex "[3:v][2:v][1:v][0:v]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0" \`
`out.mp4`
**`[3:v][2:v][1:v][0:v]` 这个顺序就是格子顺序:**
**方括号排第一的进第 1 格,排第二的进第 2 格,跟输入的 `-i` 序号无关。**
**③ 这个特性用来干什么**
场景 怎么用
----------------------------------------------------
监控墙 按通道号摆位置,改顺序不用改 layout
前后对比 原片放左上、处理版放右下
随机轮播 每段用脚本随机一个顺序
**监控墙是最典型的:十六路信号哪个通道报警就把它换到第 1 格,**
**只改输入顺序这一处,layout 一个字都不用动。**
**④ 顺序写错不会报错**
**xstack 不检查你接的顺序合不合理,接上就摆。**
**顺序错了结果照出,只是内容错位——这种事只能靠「每格内容不同」来验证。**
**这也是上面那张表要用四种颜色的原因:颜色一样,量不出顺序。**
三行输出跟本节那张表完全一致。第一行是默认顺序,第二行整个倒过来,
第三行只换中间两格——0 2 1 3 的意思是「第 0 路进第 0 格,第 2 路进第 1 格」。
代码里 order[i] 是「第 i 路要去的格子号」,别把它读成「第 i 格放第几路」。
这两个说法互为逆序,等大的四格看不出差别,十六格就要仔细了。
name() 用 ±6 的容差认色,因为 YUV 来回转一趟数值会飘一两格。
写死 == 比较 RGB 在这里一定失败。
30 秒实验
- 换顺序:把
[3, 2, 1, 0]改成[1, 3, 0, 2],先猜结果再跑。 - 加一路:改成三路四格(第三格不接),看空格处是什么色。
- 改判断:把
name()的容差从 6 改成 2,看还认不认得出来。
常见误区
- 以为要改 layout 才能换顺序。改接的顺序就够了。
- 把
order[i]读成「第 i 格放第几路」。它反过来了。 - 拿 RGB 写死比较。YUV 来回转会飘一两格,必须给容差。
自测三问(点开对答案)
1. 想让第 2 格显示第 3 路的画面,怎么接? —— 把第 3 路接到槽 1,也就是 `order[2] = 1`。 2. 顺序接错会报错吗? —— 不会,照出片,只是内容错位。 3. 验证顺序最稳的办法是什么? —— 每格用不同内容(比如不同亮度),量中心均值看是否互不相同。- ☐ (本节完,下一节 7.10.4 使用 FFmpeg 实现【九宫格】)