🎬 第十章 FFmpeg命令行在Linux系统中的应用
📄 一句话概括

Linux 上的采集命令全靠 -f 说清楚「这是哪种设备」,一个输入配一个。 设备节点没有文件头可探测,所以 -f 在设备上省不掉,在普通文件上倒能省。

📝 本节和前面几节的关系

第八章讲的是「网络上的一条流怎么进出」,第九章讲的是「设备怎么采」, 这一章把两件事拼成一条命令:从设备采、编码、推到服务器。

一、一条命令走完全链路

Linux 上最典型的直播推流命令是这样:

ffmpeg -f v4l2 -i /dev/video0 -f alsa -i default \
  -c:v libx264 -preset ultrafast -tune zerolatency \
  -c:a aac -f flv rtmp://127.0.0.1:1935/live/stream

三段各管一件事:前两行是输入(摄像头、话筒),中间是编码,最后是输出(推流)。 -f 出现了三次:两次在输入侧指定设备接口,一次在输出侧指定封装格式。

「一个输入配一个 -f」是 Linux 采集命令里最容易写漏的一处。 因为参数是位置敏感的:-f 只对紧跟其后的那个 -i 生效, 写成 -i /dev/video0 -f v4l2 就变成了「给输出指定格式」,报错还看不懂。

二、Linux 上的输入源全是 -f 名字

第八章那些 -f flv、-f mpegts 说的是容器; 这里这些 -f 说的是设备接口,完全另一类东西:

接口 Linux 上写什么 本机能开吗 它抓的是什么


v4l2 -f v4l2 -i /dev/video0 ValueError 摄像头 alsa -f alsa -i default ValueError 话筒 x11grab -f x11grab -i :0.0 ValueError X11 桌面 fbdev -f fbdev -i /dev/fb0 ValueError 帧缓冲设备 lavfi -f lavfi -i testsrc 能开 合成出来的假源 vfwcap -f vfwcap -i 0 能开 Windows 老式采集

这一列 ValueError 的意思不是「没设备」,是「这套二进制里没这个接口」, 跟第九章那条判据完全一致:ValueError 是没编进去,OSError 是有但没设备。 所以想判断「Linux 上能不能采」,查 ffmpeg -devices 比试开快得多。

三、-f 在设备上省不掉,在文件上倒能省

这里有个反直觉的实测:把一个 mp4 改名成没后缀的文件, 不写 -f 也照样能打开——因为 libav 会先读一段数据去判断格式。

这个机制叫探测(probe):打开时先看文件头, 认出是 ftyp 就当 mp4,认出 FLV 头就当 flv,跟名字没关系。 所以「不写 -f 一定失败」这句话是错的。

输入是什么 -f 能不能省


普通文件(有后缀) 能省,后缀加探测双保险 普通文件(没后缀) 能省,靠探测(实测开成了) 设备节点(摄像头等) 必须写,拍出来的是寄存器不是文件头 管道、标准输入 必须写,同上,没有文件头可探

所以规矩要说得更准:-f 省不省,看「这个输入有没有可探测的文件头」。 文件有,设备没有。

四、四条只在采集端才成立的规矩

从设备推流和从文件推流,参数差得不多,但有四条必须改:

规矩 为什么 写反了会怎样


采集端不加 -re 采集本来就是实时的 更慢,还会积压 -preset ultrafast 直播要低延迟 画质多一点、延迟大很多 -tune zerolatency 关掉编码器内部缓冲 首帧能慢出半秒以上 -g 与关键帧间隔对齐 连不上要等下一个关键帧 断流恢复变慢

第一条尤其容易搞反:-re 是给「读文件」踩刹车的, 让文件按实时速度吐帧;而采集本来就是实时的,再加一次只会更慢。 这是第八章和第九章拼在一起时,最容易从旧例子里抄错的一个参数。

这四条都不改「能不能出流」,只改「出得快不快、断得快不快」—— 所以它们出了问题不会报错,只会让人觉得「卡」和「慢」。 排查时最容易被漏掉的一层就在这儿。

五、一边落盘一边推流

直播时常常既想推出去、又想留个底。有两种写法:

ffmpeg -f v4l2 -i /dev/video0 -c:v libx264 -preset ultrafast \
  -f flv rtmp://127.0.0.1:1935/live/stream \
  -c copy out.flv

第二种是 tee:把同一路输出复制成多份。

ffmpeg -i in.mp4 -c:v libx264 \
  -f tee "[f=flv]rtmp://127.0.0.1:1935/live/s|[f=mp4]out.mp4"

两种都只编一次——这一点很重要:多写一个输出不会让编码多跑一遍, 因为编码发生在前面的滤镜链里,后面只是把同一份码流分给几个封装器。 所以「同时推三个平台」并不会让 CPU 翻三倍,翻倍的是网络带宽。

六、动手 Demo:逐个接口试开,看本机到底有几个

下面这段先查格式总数,再对六个接口各试一次打开。 ValueError 和 OSError 在这张表里会分得很清楚。

import av

print("格式总数 %d" % len(av.formats_available))
for fmt, arg in (("v4l2", "/dev/video0"),
                 ("alsa", "default"),
                 ("x11grab", ":0.0"),
                 ("fbdev", "/dev/fb0"),
                 ("lavfi", "testsrc=size=160x90:rate=25"),
                 ("vfwcap", "0")):
    try:
        c = av.open(arg, format=fmt)
        print("%-9s 能开  流 %s" % (fmt, [s.type for s in c.streams]))
        c.close()
    except Exception as e:
        print("%-9s %s" % (fmt, type(e).__name__))

实跑输出:

--------------------------------------------------------------------
10.5 使用 FFmpeg 实现流媒体及直播功能
--------------------------------------------------------------------

**① Linux 上的输入源,跟 Windows 是两套名字**

**这一节在 Linux 上最典型的全链路命令是:**

    ffmpeg -f v4l2 -i /dev/video0 -f alsa -i default \
      -c:v libx264 -preset ultrafast -tune zerolatency \
      -c:a aac -f flv rtmp://127.0.0.1:1935/live/stream

**`-f` 出现两次,一次给摄像头、一次给话筒,各管各的 `-i`。**
**这个「一个输入配一个 `-f`」是 Linux 采集命令里最容易写漏的一处。**

  接口          Linux 上写什么            本机能开吗    它抓的是什么
  --------------------------------------------------------------------------------
  v4l2          -f v4l2 -i /dev/video0    ValueError    摄像头
  alsa          -f alsa -i default        ValueError    话筒
  x11grab       -f x11grab -i :0.0        ValueError    X11 桌面
  fbdev         -f fbdev -i /dev/fb0      ValueError    帧缓冲设备
  lavfi         -f lavfi -i testsrc       能开          合成出来的假源
  vfwcap        -f vfwcap -i 0            能开          Windows 老式采集

**本机只有 `lavfi` 能开,`vfwcap` 也能开——前者是造的,后者是 Windows 的。**
**四个 Linux 接口清一色 `ValueError`,意思是这套二进制里根本没编进去。**
**这就是第九章反复强调的那条判据:`ValueError` 是「没有」,`OSError` 是「有但没设备」。**

**② 预期会失败,结果没失败——关于 `-f` 的一个真实边界**

**本来想证明「不写 `-f` 就打不开」,于是把一个 mp4 改名成没后缀的文件来试:**

  怎么打开                结果            说明
  --------------------------------------------------------------------
  bc_noname(不带 -f)    打开成功        靠文件头自己认出来了
  bc_noname(带 -f mp4)  打开成功        明写,省掉猜的过程

**两次都开成了。所以「不写 `-f` 一定失败」是错的。**
**libav 打开时会先读一段数据判断格式,名字里没后缀它也能猜对——**
**这叫探测(probe),跟「按后缀认」是两回事。**

**但设备节点和管道没有文件头可探测,`/dev/video0` 读出来的是摄像头寄存器,**
**不是任何已知容器的开头,探测必失败。**
**所以规矩要说得更准:**

  输入是什么            `-f` 能不能省
  ----------------------------------------------------
  普通文件(有后缀)    能省,后缀加探测双保险
  普通文件(没后缀)    能省,靠探测(本节实测)
  设备节点(摄像头等)  必须写,没有文件头可探
  管道、标准输入        必须写,同上

**这也解释了 9.1 那 414 个格式名到底是什么:**
**它们就是 `-f` 后面能填的全部答案,也是探测失败时的全部退路。**

**③ 四条只在采集端才成立的规矩**

  规矩                  为什么                    写反了会怎样
  --------------------------------------------------------------------------
  采集端不加 -re        采集本来就是实时的        更慢,还会积压
  -preset ultrafast     直播要低延迟              画质多一点、延迟大很多
  -tune zerolatency     关掉编码器内部缓冲        首帧能慢出半秒以上
  -g 与关键帧间隔对齐   连不上要等下一个关键帧    断流恢复变慢

**这四条都不改「能不能出流」,只改「出得快不快、断得快不快」——**
**所以它们出了问题不会报错,只会让人觉得「卡」和「慢」,**
**排查时最容易被漏掉的一层就在这儿。**

前三行(v4l2、alsa、x11grab)报的都是 ValueError: no container format, 说明这套二进制里压根没有这几个接口——不是「设备没插」。

最后两行能开:lavfi 是造出来的假源,vfwcap 是 Windows 的老接口。 vfwcap 打开要一两秒,因为它真的去初始化摄像头了; 同一个进程里第二次打开它会卡住,所以实验里只开一次。

30 秒实验

  1. 把 vfwcap 那一行去掉再跑:对比耗时,看打开摄像头本身要多久。
  2. 把 format=fmt 删掉再跑一次:看哪些能靠探测活下来、哪些直接失败。
  3. 在 Linux 上跑 ffmpeg -devices:把自己机器上真有哪几个接口列出来。

常见误区

  1. 把 -f 写在 -i 后面。它对不上那个输入,会跑去给输出当格式。
  2. 采集端照抄 -re。采集本来就是实时的,加了只会更慢。
  3. 以为多推一路就多编一次。编码在前面的链里,后面只是分发给不同封装器。
自测三问(点开对答案) 1. `ValueError` 和 `OSError` 分别代表什么? —— 前者是这套二进制里没编进这个接口,后者是编了但这台机器上没有设备。 2. 为什么设备输入必须写 `-f`? —— 设备节点没有文件头可探测,libav 猜不出格式,只能显式告诉它。 3. 同时推两个平台,CPU 会翻倍吗? —— 不会。编码只跑一遍,多出来的只是封装和网络发送。

相关笔记