🎬 第九章 FFmpeg命令行实现音视频设备采集
📄 一句话概括

Linux 录屏用 x11grab,输入写 :0.0,但 -video_size 必须自己给。 这跟 gdigrab 正好相反——那边能自己问系统桌面多大。

📝 本节和前面几节的关系

9.6.1 是 Windows 录屏,这一节是 Linux。 顺带把三平台的录屏写法摆在一起,也把「录屏加小窗」接回第七章。

一、一条命令录全屏

同样是录屏,Linux 这边的输入串不一样。

ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 \ -i :0.0 -c:v libx264 -preset veryfast -crf 23 out.mp4

参数 作用


-f x11grab X11 桌面抓取接口 -i :0.0 屏幕号点显示号,第一块屏 -video_size WxH 必写,不给就报错 -framerate 25 抓多少帧 -draw_mouse 0 不要光标就写 0

:0.0 的意思是「第 0 号 X 服务器上的第 0 号屏幕」。 单屏机器上写 :0.0 或 :0 都行,多屏要写 :0.1。

-video_size 在 x11grab 上是必写项,这一点跟 gdigrab 不一样。 gdigrab 能自己问系统「桌面多大」,x11grab 不会—— 不给就是抓 0×0,等于什么都没抓到。

二、实测:本机三条 Linux 通路一条都没有

x11grab、kmsgrab、v4l2 三个全在这里试一遍。

接口 参数 结果


x11grab :0.0 ValueError kmsgrab (空) ValueError v4l2 /dev/video0 ValueError

三条全是同一个错:no container format。 同一个错、同一个原因——这些接口在 Windows 版 FFmpeg 里没编进去。

所以这一节跟 9.5.1 一样,演示只到「证明没有」为止。 真正的录屏命令在 Linux 机器上照抄就能跑。

三、三平台录屏对照

录屏没有跨平台的统一写法,只有「接口名 + 输入串」不同。

平台 接口 输入怎么写


Windows gdigrab -i desktop 或 title=名字 Linux x11grab -i :0.0,必须给 -video_size Linux 新路 kmsgrab -i -,要 root 与 DRM macOS avfoundation -i "1:none"

这张表最有用的是最后一列:三种输入的写法差别很大, 一个写「desktop」、一个写「:0.0」、一个写「1:none」。 而后面那串编码参数,三个平台完全通用。

kmsgrab 是新路,绕过 X11 直接读显卡帧缓冲, 所以它能抓「没有桌面环境」的机器(比如纯控制台)。 代价是要 root 权限,而且要自己处理 DMA-BUF 到普通帧的转换, 用起来比 x11grab 麻烦不少。

四、录屏加摄像头小窗

这一条把两章接起来:采集是第九章,合成是第七章。

ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 -i :0.0 \ -f v4l2 -video_size 640x480 -i /dev/video0 \ -filter_complex "[1:v]scale=320:180[p];[0:v][p]overlay=W-w-16:H-h-16" \ -c:v libx264 -preset veryfast out.mp4

[1:v] 是第二路输入(摄像头),先 scale 缩到 320×180, 再 overlay 贴到第一路(桌面)的右下角,W-w-16 就是「右边留 16 像素」。

注意这个坐标会算成奇数,而第七章量过: overlay 会把奇数向下抹成偶数——所以实际落点会再往左或往上偏一像素。 写的时候不用管,看效果就行。

想做「屏幕加摄像头」的网课录制,这一条命令就够了, 不用装任何第三方录屏软件。

五、动手 Demo:确认三条 Linux 通路都没有,再看三平台对照

下面这段对 x11grab、kmsgrab、v4l2 各试一次。 再看一眼三平台录屏的输入串差在哪。

import av

def probe(fmt, arg):
    c = None
    try:
        c = av.open(arg, format=fmt)
        st = c.streams[0]
        return u"打开成功 %dx%d" % (st.codec_context.width,
                                       st.codec_context.height)
    except Exception as e:
        return u"%s: %s" % (type(e).__name__, str(e)[:36])
    finally:
        if c is not None:
            c.close()

for fmt, arg in (("x11grab", ":0.0"),
                 ("kmsgrab", ""),
                 ("v4l2", "/dev/video0")):
    print(u"%-9s %-14s -> %s" % (fmt, arg, probe(fmt, arg)))

print(u"本机 formats 里有 x11grab 吗:%s"
      % ("x11grab" in av.formats_available))

实跑输出:

--------------------------------------------------------------------
9.6.2 Linux 系统中 FFmpeg 录屏
--------------------------------------------------------------------

**① 一条命令录全屏**

`ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 \`
`  -i :0.0 -c:v libx264 -preset veryfast -crf 23 out.mp4`

  参数                作用
  ----------------------------------------------------------
  -f x11grab          X11 桌面抓取接口
  -i :0.0             屏幕号 . 显示号,`:0.0` 是第一块屏
  -video_size WxH     **必写**,不给就抓不到
  -framerate 25       抓多少帧
  -draw_mouse 0       不要光标就写 0

**`-video_size` 在 x11grab 上是必写项,这跟 gdigrab 不一样——**
**gdigrab 能自己问系统桌面多大,x11grab 不会,不给就是报错。**

**② 实测:本机三条 Linux 通路,一条都没有**

  接口        参数            结果           宽高
  ---------------------------------------------------
  x11grab     :0.0            ValueError     —
  kmsgrab                     ValueError     —
  v4l2        /dev/video0     ValueError     —

**三条全是 `ValueError: no container format`——**
**同一个错,同一个原因:这些接口在 Windows 版 FFmpeg 里就没编进去。**

**③ 三平台录屏命令对照**

  平台           接口             输入怎么写
  ---------------------------------------------------------------------
  Windows        gdigrab          `-i desktop` 或 `-i title=窗口名`
  Linux          x11grab          `-i :0.0`,且必须给 `-video_size`
  Linux 新路     kmsgrab          `-i -`,需要 root 与 DRM
  macOS          avfoundation     `-i "1:none"`,1 是屏幕序号

**录屏这件事没有跨平台的统一写法,只有「接口名 + 输入串」不同。**
**编码参数那一半完全通用,所以换平台时只改前三个参数就够。**

**④ 录屏 + 摄像头画中画:把第七章拼回来**

`ffmpeg -f x11grab -video_size 1920x1080 -framerate 25 -i :0.0 \`
`  -f v4l2 -video_size 640x480 -i /dev/video0 \`
`  -filter_complex "[1:v]scale=320:180[p];[0:v][p]overlay=W-w-16:H-h-16" \`
`  -c:v libx264 -preset veryfast out.mp4`

**这一条把两章接起来了:`x11grab` 是采集(第九章),**
**`scale` 加 `overlay` 是合成(第七章)。**
**所以「录屏带小窗」不用装第三方软件,一条命令就够。**

**注意 `overlay` 的坐标 `W-w-16` 与 `H-h-16` 会算成奇数,**
**而第七章量过:crop 与 overlay 都会把奇数向下抹成偶数,**
**所以实际落点是 `W-w-16` 再减一——写的时候不用管,看效果就行。**

**⑤ 录屏到底该不该用 FFmpeg**

  场景               合适吗     为什么
  --------------------------------------------------------
  简单录一段存档     合适       一条命令,不用装软件
  要剪辑、要标注     不合适     它只管录,不管编辑
  要录带小窗         合适       overlay 一条命令搞定
  要推直播           合适       录完直接换输出地址就行

**它的长处是「可编程」:所有参数都在命令行里,**
**能写进脚本、能被别的程序调用,这是图形界面录屏软件做不到的。**

三条全是 ValueError: no container format,连宽高都没读到。

"x11grab" in av.formats_available 也是 False—— 这是最省事的一次判断:不用试开,查一下清单就知道有没有。

30 秒实验

  1. 把 x11grab 换成 gdigrab 再跑一次:它会打开成功,差别一眼看出。
  2. 查一下 formats_available 里有哪些 grab:体会「接口按平台编」这件事。
  3. 在 Linux 上把 -video_size 去掉:看它报的错跟沙箱里的错哪个更早出现。

常见误区

  1. x11grab 不写 -video_size。它不会自己问桌面多大,抓出来是 0×0。
  2. 多屏机器上还写 :0.0。第二块屏要写 :0.1。
  3. 以为 kmsgrab 跟 x11grab 一样简单。它要 root,还要自己做格式转换。
自测三问(点开对答案) 1. `x11grab` 跟 `gdigrab` 最大的差别是什么? —— x11grab 必须自己给 `-video_size`,gdigrab 能自己问系统桌面多大。 2. `:0.0` 这两个数字分别是什么? —— 第 0 号 X 服务器、第 0 号屏幕;多屏时第二个数字会变。 3. 录屏加小窗用了哪两章的技能? —— 采集用第九章的 x11grab 与 v4l2,合成用第七章的 scale 与 overlay。

相关笔记