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

「任意顺序」不用改 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 秒实验

  1. 换顺序:把 [3, 2, 1, 0] 改成 [1, 3, 0, 2],先猜结果再跑。
  2. 加一路:改成三路四格(第三格不接),看空格处是什么色。
  3. 改判断:把 name() 的容差从 6 改成 2,看还认不认得出来。

常见误区

  1. 以为要改 layout 才能换顺序。改接的顺序就够了。
  2. 把 order[i] 读成「第 i 格放第几路」。它反过来了。
  3. 拿 RGB 写死比较。YUV 来回转会飘一两格,必须给容差。
自测三问(点开对答案) 1. 想让第 2 格显示第 3 路的画面,怎么接? —— 把第 3 路接到槽 1,也就是 `order[2] = 1`。 2. 顺序接错会报错吗? —— 不会,照出片,只是内容错位。 3. 验证顺序最稳的办法是什么? —— 每格用不同内容(比如不同亮度),量中心均值看是否互不相同。

相关笔记