🎬 第十二章 AVDevice设备读写理论及案例实战
📄 一句话概括

同一个功能,三个平台只差两个字符串:格式名与设备名。 虚拟机里采不到,多半不是 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 秒实验

  1. 把 dev_of() 的返回值打印成三元组,加上 platform.release(),看信息够不够定位机器。
  2. 在 Linux 上(若有环境)先跑 ls /dev/video*,再跑同一段代码,看报错怎么变。
  3. 查 av.formats_available 里含 alsa 与 pulse 的名字,它们也在设备这一层。

常见误区

  1. 以为 v4l2 打不开是设备问题。 先确认它在不在格式表里。
  2. 把设备名写死在配置里。 换台机器就失效,要按平台现算。
  3. 虚拟机里一报错就去改 FFmpeg 代码。 先看 /dev 有没有那个节点。
自测三问(点开对答案) 1. 三个平台的采集写法差在哪? —— 只差格式名与设备名两个字符串,其余完全一样。 2. 「不在格式表」与「报 OSError」差在哪? —— 前者是没这条路,后者是路在、设备没拿到。 3. 虚拟机里采摄像头第一步查什么? —— `ls /dev/video*`,先确认设备节点在不在。

相关笔记