同一个功能,三个平台只差两个字符串:格式名与设备名。 虚拟机里采不到,多半不是 FFmpeg 的问题,是 USB 没直通。
12.2 在本机 Windows 上跑通了链,这一节换到 Linux 那套写法。 沙箱没有 v4l2 这条路,所以这一节的结论有一半是查规格得来的。
一、三个平台三种写法
先看同一件事在三处怎么写。 平台 格式名 设备名
Windows dshow video=摄像头名 Windows gdigrab desktop Linux v4l2 /dev/video0 macOS avfoundation 0
只差两个字符串,其余代码完全一样。
所以跨平台采集的标准写法是「一个函数按平台返回这两个字符串」。
为什么不写成一份配置:设备名里常常带机器相关的名字(摄像头的产品名、
/dev/video 后面那个序号),写死了换台机器就失效,按平台现算才稳。
二、本机没有 v4l2 这条路
这是本篇最需要说清的一处「测不了」。 项 值
v4l2 在格式表里 不在 能不能验 不能,这条路没编进来
「不在格式表」与「报 OSError」是两件完全不同的事。 现象 含义 能做什么
不在格式表 这个平台的支持没编进来 换库或换平台 在表里但 OSError 路在、设备没拿到 查设备与占用
沙箱是 Windows,这一套库编译时就没带 v4l2。 所以连「路通不通」都测不了——它压根没有这条路。 这一篇因此只能给「怎么写」,不能给「跑出来多少」, 下面那张排查表也是照 v4l2 的公开规格写的,不是实测。
三、虚拟机里采摄像头三条排查
在 VMWare 里装 Ubuntu 采摄像头,卡住的地方基本就这三处。 现象 原因与处理
虚拟机里看不到摄像头 USB 默认不直通,要手动挂载
挂上了还是打不开 宿主机占着,先退出占用程序
/dev/video0 不存在 没挂进去,ls /dev/video* 确认
排查顺序要反过来:先看 /dev 下有没有那个设备节点,再看 FFmpeg 报什么。
节点都没有,后面的报错全是废话。
第二条最坑:Windows 上开着会议软件,摄像头被独占,
虚拟机里挂载会「成功」但打不开——表现与没插摄像头一样,很容易查错方向。
做完这三步,Ubuntu 里的写法其实与 Windows 一模一样:
av.open("/dev/video0", format="v4l2", options={...}),后面还是 demux 加 decode。
四、为什么设备层最不统一
全课讲了十一层,这一层是唯一「统一不下去」的。 层 统一程度
编解码、封装、滤镜 完全统一,一套 API 协议 基本统一,靠 URL 前缀 设备 不统一,名字是操作系统的
因为前几层的对象都是「数据」:包、帧、像素,格式怎么排布由 FFmpeg 自己定。
而设备的对象是「硬件」:谁插在哪、叫什么、能不能同时被两个程序打开,全归操作系统管。
FFmpeg 只能在外面包一层,包得住「怎么读」,包不住「它叫什么」。
这条也解释了为什么设备相关的代码总有一堆 #ifdef:
不是写得不好,是这一层的本质就带平台差异。
五、这一节为什么讲不深
照实说,这一篇的内容比前面几章薄。 限制 后果
沙箱是 Windows v4l2 这条路没有 没有装 VMWare 虚拟机那三条只能照规格写 没有摄像头 参数协商验不到
能给的只有「调用形状」与「排查顺序」。 这两样恰好最实用——真实项目里卡住的地方九成在这两样上,不在 API 细节里。 后面的 12.4 到 12.6 会换一种策略:凡是设备拿不到,就用合成数据把「采到之后的处理」跑通, 这样至少能证明链的后半段是对的。
六、动手 Demo:换成 Linux,设备这条路长什么样
写一个按平台返回两个字符串的小函数,再查三条路在不在本机这套库里。
import platform
import av
def dev_of():
"""按平台返回 (设备名, 格式名),其余代码不用改。"""
p = platform.system()
if p == "Windows":
return "video=摄像头名", "dshow"
if p == "Linux":
return "/dev/video0", "v4l2"
return "0", "avfoundation"
print(platform.system(), dev_of())
# 三条路在不在本机这套库里
for fmt in ("dshow", "gdigrab", "v4l2", "avfoundation"):
print(fmt, fmt in av.formats_available)
# 路在,才有资格谈设备名对不对
try:
av.open("video=0", format="dshow").close()
print("开成功")
except Exception as e:
print(type(e).__name__)
实跑输出:
--------------------------------------------------------------------
换成 Linux,设备这条路长什么样
--------------------------------------------------------------------
**① 同一个功能,三个平台的三种写法**
平台 格式名 设备名
-----------------------------------------------
Windows dshow video=摄像头名
Windows gdigrab desktop
Linux v4l2 /dev/video0
macOS avfoundation 0
**只差两个字符串,其余代码完全一样。**
**所以跨平台采集的标准写法是「一个函数按平台返回这两个字符串」。**
**② 本机没有 v4l2 这条路**
项 值
--------------------------------------------
v4l2 在格式表里 **不在**
能不能验 不能,这条路没编进来
**「不在格式表」的意思是:这个平台的支持根本没编进这套库。**
**所以连「路通不通」都测不了——它压根没有这条路。**
**这跟 dshow 报 OSError 是两件不同的事**:**dshow 是「路在、没设备」,v4l2 是「连路都没有」。**
**③ VMWare 里采摄像头要多做一步**
现象 原因与处理
------------------------------------------------------------
虚拟机里看不到摄像头 USB 设备默认不直通,要手动挂
挂上了还是打不开 宿主机占着,要先退出占用程序
/dev/video0 不存在 没挂进去,`ls /dev/video*` 确认
**这三条都不是 FFmpeg 的问题,是虚拟机的问题。**
**排查顺序要先看 /dev 下有没有那个节点,再看 FFmpeg 报什么。**
**④ 为什么设备层这么「不统一」**
层 统一程度
---------------------------------------------------------
编解码、封装、滤镜 完全统一,一套 API
协议 基本统一,靠 URL 前缀
设备 **不统一,设备名是操作系统的**
**因为设备归操作系统管,FFmpeg 只能在外面包一层。**
**包得住「怎么读」,包不住「叫什么」。**
**这一条也解释了本节为什么讲不出更多东西**:
**沙箱是 Windows,v4l2 这条路没编进来,VMWare 也没有。**
**能给的只有「调用形状」与「排查顺序」,不能假装跑过。**
只差两个字符串,所以跨平台采集写成「按平台返回两个字符串」最稳。 v4l2 不在格式表里——这不是报错,是这条路根本没编进来。
30 秒实验
- 把
dev_of()的返回值打印成三元组,加上platform.release(),看信息够不够定位机器。 - 在 Linux 上(若有环境)先跑
ls /dev/video*,再跑同一段代码,看报错怎么变。 - 查
av.formats_available里含alsa与pulse的名字,它们也在设备这一层。
常见误区
- 以为 v4l2 打不开是设备问题。 先确认它在不在格式表里。
- 把设备名写死在配置里。 换台机器就失效,要按平台现算。
- 虚拟机里一报错就去改 FFmpeg 代码。 先看
/dev有没有那个节点。
自测三问(点开对答案)
1. 三个平台的采集写法差在哪? —— 只差格式名与设备名两个字符串,其余完全一样。 2. 「不在格式表」与「报 OSError」差在哪? —— 前者是没这条路,后者是路在、设备没拿到。 3. 虚拟机里采摄像头第一步查什么? —— `ls /dev/video*`,先确认设备节点在不在。- ☐ (本节完,下一节 12.4 AVDevice 的 API 方式采集话筒获取的数据)