
简介v4l2loopback虚拟摄像头驱动资源包面向Linux内核开发者、嵌入式工程师及视频应用测试人员用于在无物理摄像头环境下快速注册/dev/video虚拟节点并配合.rc权限配置实现多路视频流处理、录制、预览与推流等场景。资源共10个文件以v4l2loopback.c和v4l2loopback.h等源文件与头文件为核心附带Makefile、o目标文件、cmd编译命令及modules.order等构建与辅助文件压缩包整体仅151KB结构紧凑便于直接定位驱动主干代码与模块构建方式。目前已有235人学习下载。通过阅读源码可掌握v4l2loopback模块的加载流程、虚拟视频设备注册机制以及权限控制细节同时可参考Makefile和头文件进行二次编译与调试进而将虚拟摄像头集成到视频会议、直播推流、面部识别或安全监控等实际开发中为各类视频输入依赖型应用提供低成本、高效率的测试与验证方案。 最近不少朋友在折腾“虚拟摄像头”时踩了一堆坑尤其是想在 Linux 下把自定义画面、视频流或者测试信号变成一个标准摄像头设备让微信、Zoom、OBS、Chrome 都能直接调用。这个需求背后绝大多数时候都是在跟 v4l2loopback 这个内核模块打交道。v4l2loopback 不是一个应用软件而是一个 Linux 内核驱动框架它会在系统里凭空“创建”出一个虚拟的 V4L2 视频设备让任何支持 V4L2 接口的程序都能把它当作真实摄像头来用。今天这篇我结合这些年摸爬滚打的经验把 v4l2loopback 的原理、安装、加载、使用和排查整理成一篇可直接照做的完整记录适合做直播推流、视频开发、AI 视觉测试、远程会议备用方案的朋友参考。1. v4l2loopback 是什么先搞清楚底层逻辑再动手1.1 从 V4L2 说起为什么虚拟摄像头能“骗过”系统在 Linux 世界里摄像头、电视卡、HDMI 采集卡这类设备几乎都遵循 V4L2Video for Linux 2这一套内核视频接口规范。只要硬件驱动写得好应用程序就可以通过/dev/video0这类设备节点打开摄像头用标准的ioctl指令去设置分辨率、像素格式、帧率然后一帧一帧地读取画面数据。反过来理解如果我在内核里实现一个“假的”视频设备也暴露成/dev/videoX并且完整实现 V4L2 的 open、close、ioctl、mmap、read 等接口那么上层的应用根本分辨不出它是真硬件还是虚拟出来的。v4l2loopback 就是这个思路的终极实践。它本质上是一个内核模块加载之后会注册一批新的 V4L2 设备节点。这些节点没有真实硬件但只要有所写的程序往里面写入视频帧数据就会进到一个“内存缓冲区”里当其他程序打开这个虚拟设备去读取视频时v4l2loopback 就把缓冲区里的帧数据提供给读取方看起来就跟一个真实的 USB 摄像头一模一样。这个“一写多读”的模型正好解决了“一个视频源只能被一个程序独占”这个 Linux 下的老大难问题。我在项目里最常用到它的一类场景是这样的FPV 无人机图传画面解码后想同时推流到 OBS 和某个自研的图像识别程序但解码器只有一个视频输出通道。这时我就把解码器的画面写到 v4l2loopback 虚拟设备上然后 OBS 和识别程序同时打开这个虚拟摄像头互不干扰。这种解耦方式比在业务层复制视频帧再分发要稳妥得多因为应用层完全不用考虑多个消费者各自的分辨率、帧率需求它们都向同一个虚拟设备要数据就行。1.2 谁最适合用 v4l2loopback场景判断不是所有“虚拟摄像头”需求都适合直接用 v4l2loopback我这里先把典型场景理清楚免得你绕远路。视频会议备用把脚本生成的字幕、PPT 转播画面、监控画面变成一个标准摄像头在 Zoom/腾讯会议里直接选它作为视频源。这个场景最常见。推流工具中转OBS 的虚拟摄像头功能在某些 Linux 版本上配置麻烦直接用 v4l2loopback 把 OBS 预览画面输出出去效果更稳定。算法测试与调试在做人脸识别、姿态估计、目标检测的时候需要把算法处理后的图像回灌给视频管道作二次测试虚拟摄像头就是标准的“测试信号源生成器”。无人值守多路视频分发同一路视频源同时发给多个上层应用避免因为其中一个读取速度慢而阻塞真实采集线程。反过来说如果你只是想录制屏幕不需要别人“看到”这个摄像头或者你只想在浏览器里临时测试摄像头是否正常那 v4l2loopback 可能就杀鸡用牛刀了。另外Windows 上的虚拟摄像头生态如 OBS Virtual Camera、e2eSoft 等实现方式跟内核驱动模型不同别把 v4l2 的思路强行套过去。2. 安装与加载从零到第一个虚拟设备2.1 各发行版安装方式与内核版本匹配v4l2loopback 的安装在大多数主流发行版里都极其简单。我这里按常见的三类系统给出命令。# Debian / Ubuntu / Linux Mint 等 sudo apt install v4l2loopback-dkms v4l2loopback-utils # Fedora / RHEL / CentOS Stream sudo dnf install v4l2loopback # Arch Linux / Manjaro sudo pacman -S v4l2loopback-dkms这里有一个特别重要的坑v4l2loopback 是内核模块必须跟你当前运行的内核版本严格匹配。Debian/Ubuntu 下用dkms方式安装的话升级内核后模块会自动重新编译一般不需要再手动管。但如果你是从源码编译安装的每次内核升级后都必须重新执行编译流程否则会出现modprobe: ERROR could not insert v4l2loopback: Exec format error这一看就是内核版本不匹配导致的。还有一个小细节有些 Ubuntu 版本上v4l2loopback-utils里的辅助工具可能没装全导致你找不到v4l2-ctl命令。这个工具是 V4L2 调试和测试的利器建议顺手单独装一下v4l-utils。我在多台 Ubuntu 20.04 / 22.04 机器上测试过apt install v4l2loopback-dkms v4l2loopback-utils v4l-utils这个组合是长期稳定可复现的。如果你想体验最新特性比如支持更高位深格式、新的 buffer 模型可以到 GitHub 上拉源码自己编译。过程大致是git clone https://github.com/umlaeute/v4l2loopback.git cd v4l2loopback make sudo make install源码编译的好处是你能在make的时候看到明确的模块编译选项也能在modinfo里读到自定义参数细节适合做内核开发或者需要定制格式支持的朋友。但日常使用我不推荐自己编译发行版仓库里的版本已经足够稳定。2.2 加载模块参数与设备节点规划安装完之后真正“创建”虚拟摄像头的动作是加载内核模块而且加载时可以指定创建几个设备、每个设备叫什么名字。我平时最常用的一种方式sudo modprobe v4l2loopback video_nr10,11 card_labelVirtualCam0,VirtualCam1 exclusive_caps1这个命令的含义是video_nr10,11指定虚拟设备的主设备号即创建/dev/video10和/dev/video11。这样做的最大好处是跟真实的摄像头设备通常占用/dev/video0到/dev/video7隔离开避免程序默认打开第一个摄像头时撞车。我在实际项目中习惯把虚拟摄像头固定在高编号上这是一个被证明能减少很多麻烦的好习惯。card_label给每个虚拟设备起一个易识别的名字。在会议软件里选择摄像头时就会显示成“VirtualCam0”而不是一串看不懂的设备名。exclusive_caps1开启“独占能力”模式。这个参数被很多老玩家忽视但非常重要。开了之后只有正在写入数据的一方会看到“输出”能力其他打开该设备的程序只看到“采集”能力极大地避免一些应用在枚举设备时莫名卡死或报错。加载成功后可以用以下命令确认设备是否创建成功v4l2-ctl --list-devices输出里会看到类似VirtualCam0 (platform:v4l2loopback-000) /dev/video10到了这一步你的 Linux 系统里就凭空多出了一个“标准摄像头”。接下来要解决的问题是怎么把画面数据“喂”给它。3. 实操环节让画面真正流进虚拟摄像头3.1 用 FFmpeg 往虚拟设备推流最硬核的通用方案FFmpeg 是往 v4l2loopback 里喂数据最通用的工具。无论你的视频源是屏幕录制、USB 摄像头、网络流RTSP/RTMP、视频文件还是程序生成的颜色测试图FFmpeg 都可以将它们统一转换格式并输出到 v4l2loopback 设备。比如把自己的 USB 摄像头画面“转发”到虚拟摄像头同时让 OBS 和会议软件都读取虚拟摄像头ffmpeg -f v4l2 -i /dev/video0 -f v4l2 -vcodec rawvideo -pix_fmt yuyv422 /dev/video10这里有两个容易踩坑的点。第一-pix_fmt必须尽量匹配虚拟设备的原生格式v4l2loopback 默认通常使用yuyv422或rgb24。如果你给的是nv12而虚拟设备不支持FFmpeg 自己会尝试转换但会白白增加 CPU 消耗。第二-f v4l2指定输出为 V4L2 设备后面的路径一定要对应上你modprobe时指定的video_nr。我曾经因为把输出写成了/dev/video0结果直接把真实摄像头搞成了一团花屏。另一个我很常用的场景是循环播放一个视频文件作为“虚拟摄像头信号源”适合测试会议软件或者视频聊天界面的表现ffmpeg -re -stream_loop -1 -i test.mp4 -f v4l2 -pix_fmt yuyv422 /dev/video10-re表示按原始帧率读取避免视频文件帧率过高导致虚拟摄像头输出的时间戳乱套。-stream_loop -1是无限循环播放。这个组合我反复用稳定可靠。如果是游戏直播录屏的场景可以录制 X11 屏幕区域输出到虚拟摄像头ffmpeg -f x11grab -video_size 1920x1080 -framerate 30 -i :0.00,0 -f v4l2 -pix_fmt yuyv422 /dev/video10注意屏幕录制到虚拟摄像头的性能开销主要消耗在像素格式转换上。如果你的虚拟设备支持rgb24而屏幕抓取也是 RGB 格式那么 CPU 占用会明显低很多。在我的 i5-12400 机器上1080p30 的 x11grab yuyv422 转换大约占用 15%-20% CPU如果直接用 rgb24 则降到 8% 左右。在推流和生产环境里这个性能差距不能忽视。3.2 用 Python/OpenCV 向虚拟设备写入帧FFmpeg 适合直接“转发”现成视频流但如果你想在推送之前做图像处理、叠加文字、跑算法模型再放到虚拟摄像头里那就得写代码了。这里给一个我自己项目里一直在用的 Python 方案核心是 OpenCV 的VideoWriter。import cv2 import numpy as np import time # 打开 v4l2loopback 虚拟设备 out cv2.VideoWriter( /dev/video10, cv2.CAP_V4L2, 0, 30.0, (1280, 720), ) if not out.isOpened(): raise RuntimeError(无法打开虚拟摄像头设备请检查是否已加载 v4l2loopback) frame np.zeros((720, 1280, 3), dtypenp.uint8) start time.time() while True: # 这里画一个随时间变化的测试画面 t time.time() - start cv2.putText(frame, fVirtual Camera Test {t:.1f}s, (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0, 255, 0), 3) # 写入虚拟摄像头 out.write(frame) # 注意VideoWriter 在 V4L2 下写入速度并不自动按帧率控制 # 因此需要手动 sleep 来控制帧率 time.sleep(1 / 30.0) out.release()这里必须说一个我自己踩过的大坑OpenCV 的VideoWriter在 V4L2 后端下写入时帧率参数fps并不真正控制帧率它只是写在设备能力描述里实际写入速度完全依赖你调write的频率。如果不手动sleep写入会快得离谱造成接收端视频“快进”或者出现大量无效帧。上面代码里我在循环末尾手动控制了帧率这在生产环境中是必须的。另外OpenCV 默认的CAP_V4L2后端只支持有限的像素格式常见的坑是它默认请求YUYV但虚拟设备实际配置的是MJPG或其他格式。遇到这种情况推荐先用v4l2-ctl -d /dev/video10 --set-fmt-videowidth1280,height720,pixelformatYUYV手动把虚拟设备格式固定下来再运行 Python 脚本能省掉很多“打开失败”“无法读帧”的报错。如果你不想用 OpenCV也可以直接用v4l2loopback-utils自带的v4l2loopback-ctl或者用 Python 的v4l2ioctl 封装自己写。但就我经验而言OpenCV 方案最省事代码量最少社区资料也最丰富适合绝大多数应用开发者。3.3 OBS 输出到虚拟摄像头直播方案的一招稳定组合OBS Studio 在 Linux 下通过插件或者原生方式都能向 v4l2loopback 输出。最常见的操作是安装obs-v4l2sink插件。安装方式如下# Ubuntu/Debian sudo apt install obs-studio # obs-v4l2sink 插件在部分发行版仓库里有 sudo apt install obs-v4l2sink如果没有则去 GitHub 项目页面下载对应版本的.deb或源码包编译。装好之后在 OBS 的“工具”菜单里会多出一个“V4L2 Video Output”选项点进去设置输出设备为/dev/video10然后点“开始”。之后无论你在 OBS 里做什么布局、滤镜、切场景最终画面都会实时输出到虚拟摄像头。这套方案的稳定之处在于OBS 内部有一套完整的渲染和线性 RGB 处理管线输出到 v4l2sink 时会把色彩空间转换到 YUYV不会出现像 FFmpeg 直接转发时可能产生的色彩偏绿、偏紫问题。我做直播项目的经验是凡是需要多场景切换、叠加文字、转场动画的复杂导播需求都优先用 OBS v4l2sink而不是手写 FFmpeg 滤镜链。不过要提醒一件事OBS 版本升级之后插件可能失效因为插件是针对特定 OBS API 版本编译的。升级 OBS 后如果发现工具菜单里没有 V4L2 输出选项先检查插件是否被自动卸载了一般重装一下插件就好。4. 常见问题与排查技巧实录4.1 权限问题为什么打开设备一直报 Permission denied这个问题我在 Linux 下碰到过无数次。v4l2loopback 创建出来的设备节点默认属于root用户普通用户直接访问会提示权限不足。解决办法有两种。第一种把当前用户加入video组sudo usermod -aG video $USER然后重新登录一次生效。这个办法一劳永逸因为真实摄像头、采集卡等视频设备的设备节点通常也归video组管。第二种临时修改设备节点权限重启后失效sudo chmod 666 /dev/video10这个方法适合快速验证但不适合生产环境因为权限会覆盖所有能访问系统的用户存在一定的安全隐患。我个人的习惯是在开发机上加video组在服务器上通过 udev 规则把虚拟设备固定成指定用户/组来管理这样多用户环境也不会乱。4.2 画面格式不对颜色发绿、花屏或完全黑屏颜色发绿这个现象几乎都是像素格式不匹配造成的。虚拟设备输出端读取时默认按设备当前的格式解释数据如果你写入的是YUYV但读取端按MJPG解码那结果必然花屏或发绿。排查方法是v4l2-ctl -d /dev/video10 --get-fmt-video查看当前格式注意输出端应用比如 Zoom、OBS打开摄像头时有时会自己请求改成它支持的格式导致你写入的格式和读取端实际格式不一致。这时候有两个保险做法在modprobe时加上exclusive_caps1让读写双方各看到各的能力减少应用强改格式的冲动。在写入端明确指定格式不要图省事让驱动“自动协商”。FFmpeg 里用-pix_fmt强制指定Python 里在VideoWriter构造函数里传入fourcc cv2.VideoWriter_fourcc(*YUYV)或者按设备支持的格式写。完全黑屏的问题除了格式不对之外更大的可能性是写入端虽然在跑但根本没有真正写入帧。比如你在循环里做了一个很耗时的图像处理导致实际写帧率远低于接收端期望的帧率接收方等待超时后就显示黑屏。这种黑屏还可以用v4l2-ctl --stream-mmap测试是否读到帧快速定位问题出在写入端还是读取端。4.3 重启后设备消失如何实现开机自动创建虚拟机摄像头默认情况下modprobe加载的模块和参数重启后都不会保留。想让 v4l2loopback 设备在开机时自动真实存在需要借助内核模块配置。在/etc/modprobe.d/v4l2loopback.conf里写入选项options v4l2loopback video_nr10,11 card_labelVirtualCam0,VirtualCam1 exclusive_caps1然后在/etc/modules-load.d/v4l2loopback.conf里写入一行模块名v4l2loopback这样每次开机系统就会自动加载 v4l2loopback 并创建好两个虚拟摄像头设备。我在无人工厂、无人值守直播服务器上都是这么配置的从不担心重启后设备丢失。这里还有一个隐藏坑如果系统里已经加载了 v4l2loopback你再改/etc/modprobe.d配置不会立刻生效需要先sudo modprobe -r v4l2loopback卸载再重新modprobe v4l2loopback或者直接重启。卸载时如果正有进程占用设备比如 OBS 还开着输出会提示Module is in use这时候先把占用程序退干净再卸载。5. 经验收尾我在实际项目里的心得体会用 v4l2loopback 这些年我最大的体会是绝大部分问题不是出在驱动本身而是出在“格式协商”和“设备命名”这两个环节。很多教程上来就教人modprobe v4l2loopback完事但从不提exclusive_caps和多设备前缀规划结果用户一接会议软件就抓到假摄像头或者写进去的画面对不上号。如果你要做的项目是长期使用的我强烈建议把虚拟设备固定在/dev/video10以上的编号并且统一命名为vCam0、vCam1这类前缀然后在业务代码里通过设备名去查找而不是硬编码/dev/videoX路径。这样可以避免你日后插拔 USB 摄像头导致设备号漂移应用找不到路径直接崩溃。最后再分享一个小技巧如果某个程序打开虚拟摄像头后无法正常读取但你又不想装各种调试工具可以用ffplay直接预览虚拟设备的内容ffplay -f v4l2 -input_format yuyv422 -video_size 1280x720 -framerate 30 /dev/video10如果ffplay能看到画面就说明写入端工作正常问题出在最终读取的 App 上如果ffplay也黑屏那就回去查写入端别在应用配置上浪费时间。这个二分定位法能帮你省下一堆瞎折腾的时间。本文还有配套的精品资源点击获取