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 秒实验
- 把
vfwcap那一行去掉再跑:对比耗时,看打开摄像头本身要多久。 - 把
format=fmt删掉再跑一次:看哪些能靠探测活下来、哪些直接失败。 - 在 Linux 上跑
ffmpeg -devices:把自己机器上真有哪几个接口列出来。
常见误区
- 把
-f写在-i后面。它对不上那个输入,会跑去给输出当格式。 - 采集端照抄
-re。采集本来就是实时的,加了只会更慢。 - 以为多推一路就多编一次。编码在前面的链里,后面只是分发给不同封装器。
自测三问(点开对答案)
1. `ValueError` 和 `OSError` 分别代表什么? —— 前者是这套二进制里没编进这个接口,后者是编了但这台机器上没有设备。 2. 为什么设备输入必须写 `-f`? —— 设备节点没有文件头可探测,libav 猜不出格式,只能显式告诉它。 3. 同时推两个平台,CPU 会翻倍吗? —— 不会。编码只跑一遍,多出来的只是封装和网络发送。- ☐ 10.5 完——第十章收官,下一章 11.1 安装 FFmpeg 5.0