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

九宫格的 layout 是九个点,第三行要写 h0+h1 才是对的起点。 九格必须等大,一旦不等,画布会歪成 480×202 这种怪尺寸。

📝 本节和前面几节的关系

四宫格是两行两列,九宫格多一行一列,坐标的加法多一层。 这一节的重点不是九个坐标,而是「为什么必须等大」。

一、九个坐标

九宫格是三行三列,九个点排成一张表:

行与列 第 1 列 第 2 列 第 3 列


第 1 行 0_0 w0_0 w0+w1_0 第 2 行 0_h0 w0_h0 w0+w1_h0 第 3 行 0_h0+h1 w0_h0+h1 w0+w1_h0+h1

横着看是加宽,竖着看是加高。第三行要写 h0+h1: 第一行的高度是 h0、第二行是 h1,两行叠起来才是第三行的起点。

九格等大时 h0 = h1 = 90,第三行起点就是 180。 这个 180 不是猜的,是「前两行高度之和」。

二、480×270 是怎么来的

九路 160×90 拼成 480×270:

格 输入 颜色 格 输入 颜色 格 输入 颜色


1 red 红 2 green 绿 3 blue 蓝 4 white 白 5 red 红 6 green 绿 7 blue 蓝 8 white 白 9 red 红

九格按 红绿蓝白 循环,量出来的中心色一路对上,九格没有一格错位。 480 是 160×3、270 是 90×3,尺寸也是整齐的三倍关系。

三倍宽高三倍高,等于面积九倍。 九路 1080p 拼成九宫格直接就是 5760×3240,普通机器编不动。

三、九格必须等大

w0、w1、h0、h1 只认第 0 路与第 1 路,这是硬限制:

九格尺寸 输出尺寸


全是 160×90 480×270 三列依次 160×90、80×45、40×22 480×202

第二行那个 480×202 就歪了:宽不是三列相加,高也对不上。 w0+w1 只加到第二路的宽,第三路到底多宽它根本不知道。

所以规矩很硬:进 xstack 之前,九路必须先 scale 成同一尺寸。 想让九路保持各自比例,就先 scale 到同一个框里再 pad 补边, 这样九格等大、内容也不变形。

四、实际用途与开销

九宫格最常见的三个用途:

用途 为什么是九格


短视频同屏 九宫格是最常见的竖屏拼法 监控九路 3×3 正好铺满一块屏 多机位预览 一眼看全九个角度

开销这件事得说清楚:九路一起解码、一起拼,解码与内存的开销大致是单路的九倍。 另外 xstack 默认按最短的那路收尾,某一路短了整段就跟着短。

真要长期跑,先降分辨率:九格每格 320×180 就够看,不必按原片尺寸拼。 拼完 960×540,编码压力比 5760×3240 差了三十多倍。

五、动手 Demo:九格颜色、尺寸不一致的后果

下面这段代码把九路纯色拼成九宫格,量每行三格的中心色; 再把三列的尺寸改成不一样,看输出会变成多大。

import av
import numpy as np

L9 = (u"0_0|w0_0|w0+w1_0|0_h0|w0_h0|w0+w1_h0"
      u"|0_h0+h1|w0_h0+h1|w0+w1_h0+h1")
COL = [u"red", u"green", u"blue", u"white"]

def nine(sizes=None, fill=u"black"):
    g = av.filter.Graph()
    sizes = sizes or [u"160x90"] * 9
    srcs = [g.add(u"color", u"c=%s:s=%s:r=25" % (COL[i % 4], sizes[i]))
            for i in range(9)]
    xs = g.add(u"xstack",
               u"inputs=9:layout=%s:fill=%s" % (L9, fill))
    sk = g.add(u"buffersink")
    for i, p in enumerate(srcs):
        p.link_to(xs, 0, i)
    xs.link_to(sk)
    g.configure()
    o = sk.pull()
    return o.to_ndarray(format=u"rgb24"), o.width, o.height

a, w, h = nine()
print(u"--- 九格 160x90 -> %dx%d" % (w, h))
for j in range(3):
    cells = []
    for i in range(3):
        k = j * 3 + i
        blk = a[j * 90 + 40:j * 90 + 50, i * 160 + 75:i * 160 + 85]
        cells.append(tuple(int(v) for v in blk.reshape(-1, 3).mean(axis=0)))
    print(u"  第 %d 行 %s" % (j + 1, cells))

print(u"--- 九格尺寸不一致")
mix = [u"160x90"] * 3 + [u"80x45"] * 3 + [u"40x22"] * 3
_, w2, h2 = nine(mix)
print(u"  三列依次 160x90 / 80x45 / 40x22 -> %dx%d" % (w2, h2))

实跑输出:

--------------------------------------------------------------------
7.10.4 使用 FFmpeg 实现【九宫格】
--------------------------------------------------------------------

**① 九个坐标**

**九宫格的 layout 就是四个变量拼出来的九个点:**

  行\列       第 1 列     第 2 列      第 3 列
  ----------------------------------------------------
  第 1 行     0_0         w0_0         w0+w1_0
  第 2 行     0_h0        w0_h0        w0+w1_h0
  第 3 行     0_h0+h1     w0_h0+h1     w0+w1_h0+h1

**第三行要写 `h0+h1`,因为第一行的高度是 `h0`、第二行是 `h1`,**
**两行叠起来才是第三行的起点。九格等大时就是 `90+90=180`。**

**② 九路 160×90 拼成 480×270**

  格     输入      颜色     格     输入      颜色     格     输入      颜色
  -----------------------------------------------------------------------------
  1      red       红       2      green     绿       3      blue      蓝
  4      white     白       5      red       红       6      green     绿
  7      blue      蓝       8      white     白       9      red       红

**九格按 红绿蓝白红绿蓝白红 循环,量出来的中心色一路对上。**
**480 是 `160×3`、270 是 `90×3`,尺寸也是三倍关系。**

**③ 九格尺寸必须一致**

**`w0`、`w1`、`h0`、`h1` 只认第 0 路与第 1 路。**
**九格等大时它们相等,怎么用都对;一旦不等,坐标就全歪。**

  九格尺寸                          输出尺寸
  ----------------------------------------------
  全是 160×90                       480×270
  三列依次 160×90、80×45、40×22     480×202

**第二行那个 480×202 就歪了:宽不是三列相加,高也对不上。**
**`w0+w1` 只加到第二路的宽,第三路的宽它根本不知道。**
**所以规矩很硬:先 `scale` 把九路统一成同一尺寸,再进 xstack。**

**④ 九宫格的实际用途**

  用途           为什么是九格
  -------------------------------------------
  短视频同屏     九宫格是最常见的竖屏拼法
  监控九路       3×3 正好铺满一块屏
  多机位预览     一眼看全九个角度

**九路一起解、一起拼,解码与内存的开销大致是单路的九倍。**
**真要长期跑,得先降分辨率:九格每格 320×180 就够了,不必按原片尺寸拼。**

三行输出按 红绿蓝 / 白红绿 / 蓝白红 排下来,跟本节那张表一致。 九格都没错位,说明第三行写 h0+h1 是对的。

最后一行是本节的重点:尺寸一不齐,输出变成 480×202。 480 还是对的(因为第三列起点按 w0+w1 算),但高从 270 掉到 202,画面被压扁了。

这个错误不会抛异常,只会出一张歪图。 所以生产环境里,进 xstack 之前统一 scale 是硬规矩,不能靠肉眼发现。

30 秒实验

  1. 改格数:把 inputs=9 改成 inputs=4 配四宫格 layout,看能不能复用同一张图。
  2. 试等大:把 mix 换成九路全 80x45,看输出是不是 240×135。
  3. 量混色:把格中心改成格边界取样,看混色有多宽。

常见误区

  1. 九格尺寸不统一。画布会歪成 480×202 这种怪尺寸,还不报错。
  2. 第三行写 0_h1。那是第二行的起点,第三行会跟第二行重叠。
  3. 九路按原片尺寸拼。输出面积是九倍,编码直接卡死。
自测三问(点开对答案) 1. 九宫格第三行的 y 起点怎么写? —— `h0+h1`,前两行高度之和。 2. 九格 160×90 拼完多大? —— 480×270。 3. 九格尺寸不统一会怎样? —— 画布歪掉(实测 480×202),而且不报错。

相关笔记