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

虚拟机上用摄像头,第一步不是装驱动,是「把设备从宿主手里抢过来」。 抢不到,Ubuntu 里怎么找都是空的。

📝 本节和前面几节的关系

前面四节都在讲命令,这一节讲操作——把设备接进虚拟机。 9.5.2 再回到命令,看接进去之后怎么采。

一、为什么虚拟机要「抢」设备

摄像头是独占设备:同一时刻只能被一个操作系统打开。 插上 U 盘时宿主会自动挂载,摄像头却不会自动交出去—— 因为宿主自己也可能在用(比如微信视频)。

所以这一步必须手动做,而且必须在虚拟机运行、摄像头已插好的状态下做。

角色 手里有什么 能不能用


宿主 Windows 物理插口 默认在用 虚拟机 Ubuntu 虚拟 USB 控制器 要手动连接

连接的动作本质上是「把 USB 设备从宿主的驱动栈上摘下来, 挂到虚拟机的虚拟 USB 控制器上」—— 在操作系统看来,就像插到了另一台电脑上。

二、四步连接法

按顺序做,每一步都有明确的成功标志。

步骤 点哪里 看到什么算成功


1 插上摄像头,等宿主认出 宿主能打开预览 2 虚拟机菜单 可移动设备 列表里出现摄像头名 3 点「连接(断开与主机连接)」 宿主那一侧自动释放 4 Ubuntu 里看 /dev/video* 多出 video0

第 3 步括号里那几个字是关键:「断开与主机连接」。 只选「连接」不选断开,经常出现「两边都拿着、两边都用不了」的状态。

如果第 2 步的列表里根本没有摄像头名字, 先确认 USB 控制器在虚拟机设置里是打开的(USB 3.0/2.0 要选对)。

三、Ubuntu 里怎么确认接到了

四条命令,按顺序走,一条比一条深。

命令 看什么


ls /dev/video* 有没有 video0 v4l2-ctl --list-devices 设备名与节点对应 v4l2-ctl -d /dev/video0 --all 支持的分辨率与格式 ffplay -f v4l2 /dev/video0 能不能出画面

顺序不能颠倒:先看节点、再看能力、最后试画面。 反过来做,一旦没画面就分不清是「没认到」还是「格式不对」。

ls /dev/video* 常见输出是 video0 video1 两个节点—— 很多摄像头会同时提供「采集」与「元数据」两个接口, 真正能出画面的通常是 video0,但也要试一下 video1 才知道。

四、三个最常见的失败

这三条基本覆盖了虚拟机接摄像头时的绝大多数问题。

现象 多半是什么


列表里没有摄像头 USB 没透传,或插在 USB 3.0 口上 连上了但没 video0 宿主还占着,没断开 有 video0 但采不到 权限不够,要加 video 组

第一条的「USB 3.0 口」是个老问题: 虚拟机的 USB 控制器版本与物理口不匹配时,透传会失败。 换到 USB 2.0 口往往立刻就好——这跟 FFmpeg 一点关系都没有, 但排查时最省时间。

第三条的权限问题,标准做法是把用户加进 video 组: sudo usermod -aG video $USER,然后注销重登。 不重登不生效,这一点很容易漏。

五、动手 Demo:先证明本机没有 Linux 那套接口

这一节是纯操作,沙箱跑的是 Windows,/dev/video0 在这里不存在。 所以演示只能做到「确认这些接口一个都没有」, 把「没有」跟「有但没设备」这两种情况分清楚。

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)[:40])
    finally:
        if c is not None:
            c.close()

for fmt, arg in (("v4l2", "/dev/video0"),
                 ("x11grab", ":0.0"),
                 ("alsa", "default"),
                 ("avfoundation", "0")):
    print(u"%-12s %-14s -> %s" % (fmt, arg, probe(fmt, arg)))

实跑输出:

--------------------------------------------------------------------
9.5.1 VMware 中的 Ubuntu 连接 USB 摄像头
--------------------------------------------------------------------

**① 先说清楚:这一节是纯操作,沙箱验不了**

  环节                在哪做          能不能实测
  --------------------------------------------------
  虚拟机连摄像头      VMware 菜单     不能
  Linux 认到设备      Ubuntu 里       不能
  FFmpeg 采到画面     Ubuntu 里       不能

**沙箱跑的是 Windows,`/dev/video0` 这种东西在这里根本不存在。**
**所以这一节的实测只能到「证明这些接口本机没有」为止,**
**真正的操作步骤按下面的清单走。**

**② 证明本机确实没有 Linux 那套接口**

  接口             参数            结果
  -----------------------------------------------
  v4l2             /dev/video0     ValueError
  x11grab          :0.0            ValueError
  alsa             default         ValueError
  avfoundation     0               ValueError

**四个全是 `ValueError`,而且错在同一个地方:**
**`no container format`——说明这些「格式」在编译时就没进去,**
**不是「设备没找到」,是「根本没有这条通路」。**

**这个区别很重要:设备没找到还能插一个,通路没有只能换机器或重编 FFmpeg。**

**③ VMware 连接 USB 摄像头的四步**

  步骤     点哪里                           看到什么算成功
  ----------------------------------------------------------------------------
  1        插上摄像头,等宿主认出           宿主能打开摄像头预览
  2        虚拟机菜单 可移动设备            列表里出现摄像头名字
  3        点「连接(断开与主机连接)」     宿主那一侧自动释放
  4        Ubuntu 里看 /dev/video*          多出 video0(可能还有 video1)

**第 3 步是最容易漏的:不点「断开与主机连接」,Ubuntu 就抢不到这台设备。**
**因为摄像头是独占设备,同一时刻只能被一个系统用。**

**④ 在 Ubuntu 里怎么确认接到了**

  命令                                看什么
  ----------------------------------------------------------
  `ls /dev/video*`                    有没有 video0
  `v4l2-ctl --list-devices`           设备名与对应节点
  `v4l2-ctl -d /dev/video0 --all`     支持的分辨率与格式
  `ffplay -f v4l2 /dev/video0`        能不能直接出画面

**顺序不能颠倒:先有节点,再看能力,最后才试画面。**
**反过来做,一旦没画面就分不清是「没认到」还是「格式不对」。**

**⑤ 三个最常见的失败**

  现象                   多半是什么
  ----------------------------------------------------------
  列表里没有摄像头       USB 没透传,或插在 USB 3.0 口上
  连上了但没 video0      宿主还占着,没断开
  有 video0 但采不到     权限不够,要加 video 组

**USB 3.0 口兼容性是虚拟机里的老问题,插到 USB 2.0 口往往就好了——**
**这一条跟 FFmpeg 一点关系都没有,但排查时最省时间。**

四条全是 ValueError: no container format,错在同一个地方。

这个错的意思不是「设备没找到」,而是「这条通路根本没编进去」。 区别很重要:设备没找到还能插一个,通路没有只能换机器或重编 FFmpeg。

30 秒实验

  1. 在 Windows 上跑 dshow 那一条:对比一下 OSError 与 ValueError 的差别。
  2. 把 v4l2 改成 v4l2:///dev/video0:看错误会不会变成别的(URL 写法)。
  3. 查一下 av.formats_available 里有没有 v4l2:与试开结果互相印证。

常见误区

  1. 以为连上摄像头 Ubuntu 就自动有了。必须手动在菜单里连接。
  2. 只点「连接」不点「断开与主机连接」。两边都拿着,两边都用不了。
  3. 加进 video 组但不重登。组权限要重新登录才生效。
自测三问(点开对答案) 1. 为什么摄像头要手动连接进虚拟机? —— 它是独占设备,宿主默认占着,必须手动把 USB 设备从宿主驱动栈上摘下来。 2. Ubuntu 里确认接到摄像头的第一条命令是什么? —— `ls /dev/video*`,看有没有 video0。 3. `ValueError: no container format` 说明什么? —— 这个接口在编译时就没包含进去,不是设备没找到。

相关笔记