当前位置: 首页 > news >正文

Linux的V4L2视频框架学习笔记

目录

一、V4L2:Linux 视频的 “神经中枢”

二、源码目录讲解(linux-4.9/media目录)

三、V4L2 架构整体 overview

1. 用户空间组件

2. 内核空间组件

四、V4L2 内核空间核心架构细节

一、核心数据结构:设备与功能的抽象

1. struct v4l2_device:设备的 “根容器”

2. struct video_device:用户空间的 “直接入口”

3. struct v4l2_subdev:子设备的 “功能单元”

4. struct v4l2_buffer:缓冲区的 “载体”

5. struct v4l2_format:格式的 “协商器”

二、核心层:设备注册与 IOCTL 分发

1. 设备注册流程:从硬件到 /dev/videoX

(1)注册 v4l2_device:v4l2_device_register

(2)注册 v4l2_subdev:v4l2_device_register_subdev

(3)注册 video_device:video_register_device

2. IOCTL 命令分发:用户请求的 “路由器”

三、中间层:缓冲、格式、控制的 “标准化接口”

1. 缓冲区管理:数据流的 “管道”

2. 格式协商:硬件与应用的 “翻译官”

3. 控制接口:参数调整的 “旋钮”

四、硬件驱动层:硬件的 “直接操作者”

1. 典型驱动:USB 摄像头(uvcvideo)

(1)设备探测与初始化:uvc_probe

(2)格式设置回调:uvc_vidioc_s_fmt_vid_cap

(3)数据流传输:uvc_video_start_streaming/uvc_video_decode

2. 子设备驱动:I2C 图像传感器(ov5640)

(1)I2C 设备探测:ov5640_probe

(2)格式设置回调:ov5640_set_fmt

(3)电源管理:ov5640_power_on/ov5640_power_off

五、媒体控制器:复杂流水线的 “协调者”(扩展)


一、V4L2:Linux 视频的 “神经中枢”

        V4L2(Video for Linux 2,第二代 Linux 视频架构)是 Linux 系统中视频设备管理与数据处理的核心框架,负责统筹视频硬件的调度、提供标准化的视频操作接口,支持从简单的摄像头采集到复杂的视频编解码等全链路功能。其架构以 “分层抽象、统一适配” 为核心,覆盖内核空间的驱动框架与用户空间的操作接口,是 Linux 视频生态的 “底层骨架”。其重要性主要体现在:

硬件适配的 “翻译官”

视频硬件的形态千差万别:从 USB 摄像头、笔记本内置摄像头,到嵌入式 SoC 内置的图像信号处理器(ISP)、PCIe 视频采集卡,再到专业的工业相机(如 GigE 相机),不同硬件的通信协议(USB UVC、MIPI CSI-2、PCIe)、数据格式(原始 Bayer 数据、YUV 数据)、控制逻辑(曝光、对焦调节)差异极大。

但 V4L2 通过 “标准化驱动框架 + 统一设备抽象” 屏蔽了这些硬件差异:

  • 对驱动开发者:只需按照 V4L2 规范,实现硬件对应的 “回调函数集”(如视频流采集的 v4l2_file_operations、参数控制的 v4l2_ioctl_ops),将硬件能力映射为 V4L2 定义的 “标准功能”(如 “视频捕获设备”“视频输出设备”“编解码器设备”)。
  • 对应用开发者:无需关心硬件型号,只需通过 V4L2 统一接口(如 ioctl 系统调用、read/write 数据读写)操作设备 —— 无论是 USB 摄像头还是 MIPI 接口的工业相机,应用程序都能通过相同的逻辑完成 “打开设备→设置分辨率→启动采集→读取数据” 的流程。

例如:常用的 v4l2-ctl 工具(V4L2 命令行调试工具),只需执行 v4l2-ctl --list-formats 就能查看任何 V4L2 兼容设备支持的视频格式,执行 v4l2-ctl -c exposure_auto=1 就能调节不同摄像头的曝光模式 —— 这背后正是 V4L2 统一适配能力的体现。

视频功能的 “基础工具箱”

V4L2 不仅是 “硬件与应用的桥梁”,更提供了视频处理的 核心基础功能,覆盖从采集到输出的全流程,是所有视频应用的 “功能底座”:

  • 全场景设备类型支持:除了最基础的 “视频捕获”(如摄像头采集)和 “视频输出”(如屏幕显示),还支持细分场景设备:

    • “视频编解码器”(如 SoC 内置的 H.264 编码器,通过 V4L2 接口提交原始 YUV 数据并获取压缩后的 H.264 码流);
    • “视频叠加设备”(如在视频画面上叠加字幕、水印);
    • “元数据设备”(如获取摄像头的传感器参数、ISP 处理后的统计数据)。
  • 精细化参数控制:通过 “控制接口”(Control Interface)统一管理硬件参数,涵盖:

    • 图像基础参数:分辨率(VIDIOC_S_FMT)、帧率(VIDIOC_S_PARM)、像素格式(如 YUYV422、MJPEG、RAW10);
    • 传感器调节参数:曝光时间、焦距(自动对焦 / 手动对焦)、白平衡、ISO 感光度;
    • 图像处理参数:对比度、饱和度、 gamma 校正(部分硬件支持)。
  • 高效数据传输:提供多种数据传输模式适配不同场景需求:

    • “用户指针模式”(User Pointer):应用程序分配缓冲区,通过指针传递给驱动,适合低延迟场景;
    • “内存映射模式”(MMAP):驱动分配内核缓冲区,应用程序通过 mmap 映射到用户空间读写,是最常用的高效模式(避免数据拷贝);
    • “直接渲染模式”(DMABUF):支持缓冲区在不同硬件组件间共享(如从摄像头采集的缓冲区直接传递给 GPU 渲染),减少数据搬运开销。

        这些功能是视频应用的 “刚需”:无论是视频通话软件(需采集摄像头数据)、监控系统(需控制摄像头曝光并编码视频),还是嵌入式设备的人脸识别(需读取原始图像数据做算法处理),最终都需通过 V4L2 调用硬件能力。

上层框架的 “依赖基石”

与 ALSA 类似,实际开发中很少直接编写底层 V4L2 代码(需处理缓冲区管理、异步事件、格式协商等细节),但 几乎所有 Linux 视频上层框架都以 V4L2 为底层依赖,构建更易用的高层能力:

  • GStreamer(多媒体处理框架):通过 v4l2src(V4L2 采集源)、v4l2sink(V4L2 输出端)、v4l2h264enc(V4L2 H.264 编码器)等插件,将 V4L2 能力封装为 “管道元素”,开发者只需拼接管道即可实现复杂功能(如 “摄像头采集→H.264 编码→RTSP 推流”)。

  • FFmpeg(音视频处理工具集):通过 libavdevice 模块对接 V4L2,支持以 v4l2 为输入源(如 ffmpeg -f v4l2 -i /dev/video0 output.mp4 直接采集摄像头数据并保存为视频文件),并能调用 V4L2 硬件编解码器加速视频处理。

  • 嵌入式厂商 SDK:全志、瑞芯微等 SoC 厂商的视频接口(如全志的 “VE 引擎” 接口、瑞芯微的 “MIPI 摄像头驱动封装”),底层均通过 V4L2 与硬件对接 —— 开发者调用 SDK 接口时,本质是通过上层封装间接使用 V4L2 的能力。

  • 桌面应用:Linux 下的摄像头应用(如 Cheese 拍照软件、Zoom 视频会议),底层均通过 V4L2 访问摄像头,无需单独适配不同硬件。

        

        可以说,V4L2 是 Linux 视频生态的 “隐形支柱”—— 从最基础的摄像头采集到复杂的视频编解码,从嵌入式设备到桌面系统,它以统一的架构串联起硬件与上层应用,让 Linux 视频功能的实现摆脱了 “硬件型号依赖” 的桎梏


二、源码目录讲解(linux-4.9/media目录)

在讲解V4L2之前我们先一块看一下源码目录中的文件都是干什么的,方便我们进行理解


1. v4l2-core/ — V4L2 核心层(Video4Linux2 Core)

职责

  • 实现 V4L2 框架的核心功能,相当于 ALSA 的 sound/core/,提供用户空间 /dev/video*/dev/vbi*/dev/radio* 等设备节点背后的抽象接口。
  • 负责 ioctl 分发、buffer 管理、视频流格式协商、设备注册/注销、poll/select、mmap、控制接口(VIDIOC_*)等。

代表文件

  • v4l2-dev.c:注册/管理 video_device,负责 /dev/videoX 节点。
  • v4l2-ioctl.c:统一的 ioctl 分发中心,实现 VIDIOC_* 命令调用。
  • v4l2-fh.c:file handle(fh)抽象,支持多进程/多实例访问。
  • v4l2-subdev.c:sub-device(sensor、decoder 等)框架实现。
  • videobuf2-*.[ch]:videobuf2(vb2)缓冲管理框架,支持 MMAP、USERPTR、DMABUF 等模式。
  • v4l2-ctrls.c:控制接口(亮度、对比度、饱和度等 kcontrol 类似功能)。
  • v4l2-async.c:异步子设备绑定机制(异步探测 sensor、bridge 芯片等)。
  • v4l2-event.c:事件通知机制(如信号丢失、帧同步事件)。

看这里

  • ioctl 调用链:应用 → v4l2-ioctl.c → 驱动回调(vidioc_ops)。
  • buffer 管理:videobuf2-* 中队列/入队/出队/中断回调。
  • subdev 机制:v4l2-subdev.c 定义 v4l2_subdev_ops,用于 sensor/ISP 解耦。
  • controls:v4l2-ctrls.c 统一实现标准控制和扩展控制。

2. platform/ — 平台相关视频驱动

职责

  • 存放 SoC 平台自带的视频接口驱动,例如 CSI(Camera Serial Interface)、VIP、ISP、JPEG 编解码、VPU 视频加速器等。
  • 典型的 ARM SoC 都会在这里放各自的 camera/vpu 驱动。

代表文件/子目录

  • platform/sunxi/:全志 SoC 的 VFE(Video Front End)、CSI、ISP 驱动。
  • platform/omap/:TI OMAP 视频接口驱动。
  • platform/sti/:STMicroelectronics 的视频驱动。

看这里

  • 平台特定 video controller probe/init 代码。
  • struct video_device 注册点。
  • DMA buffer 配置、ISP pipeline 构建。

3. usb/ — USB 视频驱动

职责

  • 提供 UVC(USB Video Class)摄像头驱动和厂商特定 USB 摄像头驱动。
  • 负责与 USB core 交互、配置端点、启动/停止视频流、处理 isochronous 或 bulk 传输。

代表文件

  • usb/uvc/uvc_driver.c:UVC 核心逻辑,最重要的 USB 摄像头驱动。
  • usb/gspca/:老式 gspca USB 摄像头驱动集合。
  • 其他单个厂商驱动(如 usb/stk1160.c)。

看这里

  • UVC 协议实现,特别是 uvc_queue.c(缓冲管理)和 uvc_video.c(流控制)。
  • gspca 框架,包含多个 legacy 摄像头芯片支持。

4. pci/ — PCI/PCIe 视频采集卡驱动

职责

  • 驱动桌面或服务器用的视频采集卡,例如 BT878、CX23885、HDMI capture 卡等。
  • 负责寄存器配置、DMA buffer 提交、PCI 资源管理。

代表文件/子目录

  • pci/bt8xx/:BT8xx 系列采集卡驱动。
  • pci/cx23885/:CX23885/7/8 采集卡驱动。
  • pci/tw68/:TW68 视频解码采集卡。

看这里

  • PCI probe/init 和 BAR 映射。
  • DMA 通道配置。
  • V4L2 控制接口对接。

5. i2c/ — I²C 总线视频子设备驱动

职责

  • 存放各种 I2C 总线上的视频子设备(V4L2 subdev),例如摄像头 sensor、TV decoder、HDMI RX 芯片。
  • 这些不会直接生成 /dev/video* 节点,而是以 subdev 形式挂接在主控驱动下。

代表文件

  • 各类 sensor:ov5640.cov2640.cadv7180.c 等。
  • HDMI 接收芯片:tda1997x.c 等。

看这里

  • v4l2_subdev 的注册和 ops 回调(如 s_streams_power)。
  • controls 定义(曝光、增益、白平衡等)。
  • I2C probe → regmap → v4l2_subdev。

6. firewire/ — IEEE1394 (FireWire) 视频驱动

  • 职责
    支持基于 FireWire 的摄像头、视频采集设备。
    现在较少使用,但在专业视频领域(工业相机、DV 摄像机)仍有应用。


7. common/ — 公共库与通用组件

职责

  • 存放可复用的 V4L2 辅助模块,提供常见算法、格式转换、辅助库。

代表文件/子目录

  • common/videobuf2/:videobuf2 框架通用实现(缓冲队列核心)。
  • common/tuners/:模拟电视调谐器驱动。
  • common/ir/:红外遥控接收器驱动。

看这里

  • vb2 queue 初始化、qbuf/dqbuf 逻辑。
  • 电视/调谐器驱动,作为历史遗留部分。

8. radio/ — FM/AM 收音机驱动

  • 职责
    支持基于 PCI、USB 或 I2C 的广播收音机设备,暴露 /dev/radio* 节点。


9. 架构相关子目录(arm/、sh/、mips/ 等)

  • 职责
    各架构特定的视频驱动适配代码(类似 ALSA 的 sound/soc/arm/)。
    主要是 SoC 上的 VPU、ISP、CSI 控制器。


10. 顶层文件(drivers/media/ 根目录)

内容

  • KconfigMakefile:V4L2 子系统功能开关、依赖管理。
  • media-device.c:media controller 框架实现。
  • media-entity.c:media entity/links/pads 机制(搭建 pipeline 拓扑)。
  • mc-entity.c:media controller 内部 API。

看这里

  • 如果涉及 ISP pipeline(sensor → CSI → ISP → scaler → encoder),就需要用 Media Controller API,在这里找到 entity、pad、link 管理。

三、V4L2 架构整体 overview

        V4L2 的架构采用分层设计,核心目标是视频设备抽象与功能标准化:通过内核驱动屏蔽不同视频硬件(摄像头、编码器、采集卡等)的差异,通过用户空间库与工具提供统一交互接口,让应用程序无需关心底层硬件细节(如传感器型号、总线类型、编解码芯片差异)。整体结构如下:

应用程序
↓↑
用户空间:libv4l2(V4L2 库) + 工具集(v4l2-ctl/v4l2-compliance 等) + 插件系统
↓↑(系统调用 /ioctl)
内核空间:V4L2 核心层 → 中间层(捕获 / 输出 / 编解码 / 控制等接口) → 硬件驱动层
↓↑
视频硬件(图像传感器、USB 摄像头、ISP、编解码器、HDMI 采集卡等)


1. 用户空间组件

        用户空间是应用程序与 V4L2 交互的入口,主要包含 libv4l2 库工具集 和 插件系统,核心作用是简化应用开发、解决兼容性问题并提供调试能力:

(1)工具集

        V4L2 工具集主要来自 v4l-utils 包,是调试、配置视频设备的核心工具,常用工具及功能如下:

  • v4l2-ctl:全能型视频设备控制工具,支持设备查询、参数配置、流传输测试
    示例:v4l2-ctl --list-formats(查询设备支持的像素格式)、v4l2-ctl --set-fmt-video=width=1280,height=720,pixelformat=YUYV(设置视频分辨率与格式)、v4l2-ctl --stream-mmap --stream-to=frame.yuv(捕获一帧 YUV 数据到文件)。
  • v4l2-compliance:V4L2 设备兼容性测试工具,验证驱动是否符合 V4L2 标准
    示例:v4l2-compliance -d /dev/video0(测试 /dev/video0 设备的合规性,检测 IOCTL 支持、格式协商、缓冲管理等是否正常)。
  • v4l2-sysfs-path:查询视频设备在 sysfs 中的路径,用于查看硬件底层信息
    示例:v4l2-sysfs-path -d /dev/video0(输出设备对应的 sysfs 路径,如 /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/video4linux/video0)。
  • qv4l2:图形化 V4L2 设备调试工具(需安装 Qt 依赖),支持实时预览视频、可视化配置参数(亮度、对比度等)。

(2)插件系统

        V4L2 插件通过 libv4l2 库实现,用于补充硬件能力、解决兼容性问题,核心插件如下:

  • 格式转换插件:自动将硬件不支持的像素格式(如 RGB)转换为硬件支持的格式(如 YUYV/NV12),避免应用手动处理格式适配。
  • 驱动适配插件:为老旧或非标准驱动提供兼容性封装,让不符合 V4L2 标准的驱动可被标准应用调用。
  • v4l2loopback:虚拟视频设备插件,模拟 /dev/videoX 节点,支持将应用生成的视频流(如桌面录制、视频文件)作为 “虚拟摄像头” 输出,供其他应用(如视频会议软件)读取。
    示例:modprobe v4l2loopback devices=1(加载插件并创建 1 个虚拟设备 /dev/video1)、ffmpeg -re -i test.mp4 -f v4l2 /dev/video1(将视频文件推流到虚拟设备)。
  • crop/scale 插件:提供软件层面的图像裁剪(Crop)与缩放(Scale)功能,适配应用与硬件的分辨率差异(如硬件仅支持 1280x720,应用需 640x360 时自动缩放)。

2. 内核空间组件

        内核空间是 V4L2 的核心实现,负责视频设备驱动、数据流管理及硬件控制,按功能分为 核心层中间层 和 硬件驱动层,各层级职责与关键组件如下:

层级作用关键组件 / 数据结构
核心层管理视频设备的注册、枚举、资源分配,提供统一的设备访问接口(如 /dev/videoX),处理用户空间与内核的基础交互。v4l2_device:视频设备抽象基类,管理同一硬件下的多个子设备(如传感器 + ISP);
video_device/dev/videoX 设备节点抽象,包含设备类型(捕获 / 编码)、IOCTL 回调、文件操作接口;
v4l2_subdev:子设备抽象(如传感器、编码器),无独立节点,需通过主设备或媒体控制器访问。
中间层实现标准化的视频功能接口,屏蔽硬件差异,提供缓冲管理、格式协商、流控制等核心能力,是应用与硬件驱动的 “桥梁”。缓冲管理v4l2_buffer(缓冲区抽象,含状态 / 地址 / 大小)、v4l2_requestbuffers(缓冲申请参数);
格式协商v4l2_pix_format(像素格式 / 分辨率)、v4l2_format(格式配置结构体);
功能接口
- 捕获 / 输出:v4l2_fh(文件句柄抽象,关联缓冲队列);
- 控制:v4l2_ctrl(亮度 / 对比度等参数抽象)、v4l2_ctrl_handler(控制参数管理器);
- 编解码:v4l2_m2m_ctx(内存到内存设备上下文,管理编码 / 解码缓冲队列)。
硬件驱动层针对具体视频硬件的驱动实现,直接操作硬件寄存器(如传感器初始化、ISP 参数配置、编码器启动),将硬件能力映射为中间层的标准化接口。USB 摄像头驱动uvcvideodrivers/media/usb/uvc/),支持 UVC 协议摄像头;
传感器驱动ov5640.c/imx219.cdrivers/media/i2c/),控制 I2C 接口图像传感器;
SoC 平台驱动rkisp1drivers/media/platform/rockchip/rkisp1/)、sunxi-csi(全志 CSI 控制器),适配 SoC 内置的 ISP/CSI 模块;
编解码驱动rk_h264_enc.c(Rockchip H.264 编码器)、omap_vcodec_dec.c(TI OMAP 解码器),实现硬件编解码功能。

四、V4L2 内核空间核心架构细节

        V4L2通过 “核心层抽象设备 + 中间层标准化功能 + 驱动层操作硬件”,实现 “屏蔽硬件差异,统一交互逻辑” 的目标。以下从数据结构核心流程组件交互三个维度详解内核空间架构:


一、核心数据结构:设备与功能的抽象

V4L2 通过一系列关键结构体,实现对视频设备、子设备、缓冲区、格式的抽象,是各组件交互的 “语言”。

1. struct v4l2_device:设备的 “根容器”

  • 作用:代表一组视频设备(如 “传感器 + ISP + 编码器” 组成的相机模组),是硬件设备的顶层抽象,管理子设备链表、驱动生命周期。
  • 关键成员
    struct v4l2_device {struct device *dev;          // 内核 device 对象(电源管理、sysfs 关联)char name[V4L2_DEVICE_NAME_SIZE]; // 设备名称(如 "ov5640_camera")struct list_head subdevs;    // 子设备(v4l2_subdev)链表struct list_head video_devices; // 视频设备(video_device)链表void (*release)(struct v4l2_device *); // 释放回调
    };
    
  • 核心地位:驱动初始化时先创建 v4l2_device(通过 v4l2_device_register),后续子设备(传感器、编码器等)都依附于它,实现 “多组件设备” 的统一管理。

2. struct video_device:用户空间的 “直接入口”

  • 作用:代表 /dev/videoX 设备节点,是用户空间(通过 /dev/video0 等)直接操作的对象,封装设备类型、IOCTL 回调、文件操作接口。
  • 关键成员
    struct video_device {int minor;                   // 次设备号(对应 /dev/videoX 的 X)const char *name;            // 设备名称(如 "UVC Camera")const struct v4l2_file_operations *fops; // 文件操作集(open/read/ioctl)struct v4l2_ioctl_ops *ioctl_ops; // IOCTL 命令回调集合enum v4l2_pix_format_type type; // 设备类型(捕获/输出/编解码等)struct v4l2_device *v4l2_dev; // 所属的 v4l2_device
    };
    
  • 核心地位:通过 video_register_device 注册后,内核在 /dev 下创建字符设备节点,用户空间的 openioctl 等操作最终转发到该结构体的回调函数。

3. struct v4l2_subdev:子设备的 “功能单元”

  • 作用:代表视频流水线中的子功能单元(如图像传感器、ISP、编码器),无独立设备节点,需通过 video_device 或媒体控制器访问,负责具体硬件功能(如传感器配置、编码启动)。
  • 关键成员
    struct v4l2_subdev {struct v4l2_device *v4l2_dev; // 所属的 v4l2_devicechar name[32];               // 子设备名称(如 "ov5640_sensor")const struct v4l2_subdev_ops *ops; // 子设备操作回调(初始化、格式设置等)struct list_head list;       // 加入 v4l2_device.subdevs 的链表节点
    };
    
  • 核心地位:子设备是 “模块化硬件” 的抽象。例如,相机模组中,一个 v4l2_subdev 负责传感器(如 OV5640),另一个负责 ISP,主 video_device 整合它们的功能对外提供接口。

4. struct v4l2_buffer:缓冲区的 “载体”

  • 作用:抽象视频缓冲区(用户空间通过 mmap/DMABUF 访问),管理缓冲区的状态(空闲 / 队列中 / 已填充)、内存地址、时间戳等。
  • 关键成员
    struct v4l2_buffer {__u32 index;                 // 缓冲区索引(0、1、2...)__u32 type;                  // 缓冲区类型(捕获/输出等)__u32 bytesused;             // 已用字节数__u32 flags;                 // 标志(如 V4L2_BUF_FLAG_DONE 表示已填充)struct timespec timestamp;   // 时间戳void *priv;                  // 驱动私有数据(如物理地址)
    };
    
  • 核心地位:是 “数据流” 的核心载体。用户空间通过 VIDIOC_REQBUFS 申请缓冲区,内核用 v4l2_buffer 管理,硬件填充数据后标记状态,用户空间通过 VIDIOC_DQBUF 取出。

5. struct v4l2_format:格式的 “协商器”

  • 作用:描述视频格式(分辨率、像素格式、帧率等),是用户空间与驱动协商格式的载体(如 VIDIOC_S_FMT 设置格式,VIDIOC_G_FMT 获取格式)。
  • 关键成员
    struct v4l2_format {__u32 type;                  // 格式类型(图像/输出等)union {struct v4l2_pix_format pix; // 图像格式(分辨率、像素格式 YUYV/NV12 等)// 省略其他格式类型(如压缩、音频格式)} fmt;
    };
    
  • 核心地位:实现 “格式兼容性”。驱动通过它上报支持的格式,用户空间选择合适的格式,确保硬件与应用的格式匹配。

二、核心层:设备注册与 IOCTL 分发

        核心层位于 drivers/media/v4l2-core/,是 V4L2 内核空间的 “中枢”,负责设备注册IOCTL 命令分发缓冲区管理等基础能力,为驱动提供 “骨架”。

1. 设备注册流程:从硬件到 /dev/videoX

V4L2 设备注册需经过 v4l2_device 注册 → v4l2_subdev 注册 → video_device 注册 三步

(1)注册 v4l2_devicev4l2_device_register
  • 函数:int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
  • 动作:
    • 初始化 v4l2_dev->dev(关联内核 device,用于电源管理、sysfs);
    • 将 v4l2_dev 加入全局链表(v4l2_device_list),方便系统枚举;
    • 初始化 subdevs 和 video_devices 链表,为子设备、视频设备注册做准备。
(2)注册 v4l2_subdevv4l2_device_register_subdev
  • 函数:int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd);
  • 动作:
    • 将子设备 sd 加入 v4l2_dev->subdevs 链表;
    • 调用子设备的 ops->init 回调(如传感器初始化寄存器);
    • 若支持媒体控制器,注册为 “媒体实体(media entity)”。
(3)注册 video_devicevideo_register_device
  • 函数:int video_register_device(struct video_device *vdev, int type, int nr);
  • 动作:
    • 分配次设备号 nr(对应 /dev/videoX 的 X);
    • 注册字符设备(V4L2 主设备号为 81,次设备号由 type 和 nr 计算);
    • 在 /dev/videoX 创建设备节点;
    • 初始化 fops(文件操作集,如 openioctl)和 ioctl_ops(IOCTL 回调集合)。

形象的理解这个过程:

1. 核心容器先建起来:v4l2_device

  • v4l2_device_register(),相当于先准备好一个“大框架”或者“核心上下文”。

  • 这个 v4l2_device 负责承载整个驱动下面的 subdevsvideo_devices

  • 没有它,后面的 subdev、video_device 都没地方挂。

2. 往容器里挂子设备:v4l2_subdev

  • v4l2_device_register_subdev(),把 sensor / ISP / bridge 这种子模块挂到 v4l2_dev->subdevs 链表。

  • 每个 subdev 代表一个“功能单元”(例如摄像头 sensor,CSI bridge,解码器)。

  • 如果驱动涉及 media controller,subdev 还会变成 media entity,和其他 entity 建图。

3. 注册视频设备节点:video_device

  • 最后 video_register_device(),把最终能暴露给用户空间的 /dev/videoX 节点挂到 v4l2_dev->video_devices

  • 这个节点本质是一个 字符设备接口,它的 fops 和 ioctl_ops 由驱动提供。

  • 用户空间 ioctl(fd, VIDIOC_XXX) 的时候,最后是通过这个 video_device 走到驱动的回调。

2. IOCTL 命令分发:用户请求的 “路由器”

        用户空间的 ioctl(如 VIDIOC_REQBUFSVIDIOC_S_FMT)是与 V4L2 交互的核心,核心层的 v4l2_ioctl.c 负责命令分发,流程如下:

  1. 用户发起 ioctl:通过 ioctl(fd, cmd, arg),其中 fd 是 /dev/videoX 的文件描述符,cmd 是 V4L2 命令(如 VIDIOC_QUERYCAP)。

  2. 核心层接收请求video_device 的 fops->unlocked_ioctl(实际为 v4l2_ioctl)被调用,进入核心层。

  3. 命令解析与分发v4l2_ioctl 通过 switch-case 匹配 cmd

    • 若为通用命令(如 VIDIOC_QUERYCAP 查询设备能力),核心层直接处理(填充设备支持的功能);
    • 若为驱动专属命令(如格式设置、缓冲管理),转发到 video_device->ioctl_ops 中对应的驱动回调。
  4. 驱动处理并返回:驱动的回调函数(如 v4l2_pix_format_ops->vidioc_s_fmt)处理硬件逻辑(如设置传感器分辨率),并将结果返回给用户空间。

三、中间层:缓冲、格式、控制的 “标准化接口”

        中间功能层基于核心层,提供缓冲区管理格式协商控制接口等标准化能力,屏蔽硬件差异,是 “驱动与核心层” 的交互桥梁。

1. 缓冲区管理:数据流的 “管道”

        V4L2 支持 MMAP(最常用)、USERPTRDMABUF 三种缓冲模式,核心层的 v4l2-bufs.c 统一管理。以 MMAP 模式为例:

  1. 申请缓冲:用户空间调用 ioctl(VIDIOC_REQBUFS, &req),核心层分配 req.count 个缓冲区,用 v4l2_buffer 描述,内核分配物理内存。

  2. 映射到用户空间:用户空间调用 mmap,核心层将内核缓冲区的物理地址映射到用户虚拟地址,返回映射指针。

  3. 入队缓冲:用户空间调用 ioctl(VIDIOC_QBUF, &buf),将缓冲区放入 “空闲队列”,核心层标记 buf 为 V4L2_BUF_STATE_QUEUED

  4. 硬件填充数据:驱动启动流(VIDIOC_STREAMON)后,硬件(如摄像头)将数据写入队列中的缓冲区,完成后标记 buf 为 V4L2_BUF_FLAG_DONE,并触发中断。

  5. 出队缓冲:用户空间调用 ioctl(VIDIOC_DQBUF, &buf),核心层取出 “已完成” 的缓冲区,返回给用户处理(如显示、编码)。

  6. 循环使用:用户处理完缓冲区后,再次 QBUF,重复 “入队→填充→出队” 流程。

2. 格式协商:硬件与应用的 “翻译官”

        格式协商通过 v4l2-format.c 实现,确保用户空间请求的格式与硬件能力匹配:

  1. 用户设置格式:调用 ioctl(VIDIOC_S_FMT, &fmt),指定分辨率(如 1280x720)和像素格式(如 YUYV)。

  2. 驱动验证与调整:核心层调用驱动的 vidioc_s_fmt 回调,驱动检查硬件是否支持该格式:

    • 若支持,直接应用;
    • 若不支持,调整为硬件最接近的格式(或返回错误)。
  3. 返回实际格式:驱动将最终生效的格式填充到 fmt 中,返回给用户空间,确保格式匹配。

3. 控制接口:参数调整的 “旋钮”

        V4L2 控制接口(如亮度、对比度)由 v4l2-ctrls.c 实现,核心是 struct v4l2_ctrl

  1. 驱动注册控制项:驱动初始化时,通过 v4l2_ctrl_new_std 注册控制项(如亮度范围 0-255,默认 128)。

  2. 用户获取 / 设置参数

    • 获取:ioctl(VIDIOC_G_CTRL, &ctrl),核心层调用驱动的 vidioc_g_ctrl 回调,读取硬件寄存器值;
    • 设置:ioctl(VIDIOC_S_CTRL, &ctrl),核心层调用驱动的 vidioc_s_ctrl 回调,将新值写入硬件寄存器。
  3. 自动控制与缓存:核心层支持控制项缓存(避免频繁读写硬件),以及自动控制(如自动曝光,由硬件算法处理)。


四、硬件驱动层:硬件的 “直接操作者”

        硬件驱动层位于 drivers/media/ 下的子目录(如 video/i2c/platform/),直接操作硬件寄存器,实现具体功能(如传感器初始化、编码启动),需实现核心层定义的回调接口。

1. 典型驱动:USB 摄像头(uvcvideo

uvcvideo(位于 drivers/media/usb/uvc/)是最常见的 USB 摄像头驱动,体现 V4L2 驱动的典型模式:

(1)设备探测与初始化:uvc_probe
  • USB 摄像头插入时,uvc_probe 被调用:
    • 解析 USB 设备描述符,确认是 UVC 设备;
    • 创建 v4l2_device 和 video_device
    • 注册 v4l2_subdev(处理 UVC 控制单元、处理单元);
    • 初始化 video_device 的 ioctl_ops(实现格式设置、缓冲管理等回调)。
(2)格式设置回调:uvc_vidioc_s_fmt_vid_cap
  • 用户空间设置捕获格式时,该回调被调用:
    • 验证分辨率、像素格式是否符合 UVC 设备能力;
    • 向 USB 设备发送 UVC 命令,配置传感器输出格式;
    • 填充 v4l2_format 并返回。
(3)数据流传输:uvc_video_start_streaming/uvc_video_decode
  • 启动流(VIDIOC_STREAMON)时,uvc_video_start_streaming 被调用:
    • 向 USB 设备发送 “开始流” 命令,触发摄像头输出数据;
    • 初始化 USB urb(异步数据传输结构);
  • USB 数据到达后,uvc_video_decode 处理数据:
    • 解析 UVC 数据包,提取视频帧;
    • 将帧数据填充到 v4l2_buffer 的缓冲区中;
    • 标记缓冲区为 DONE,触发 poll 通知用户空间。

2. 子设备驱动:I2C 图像传感器(ov5640

图像传感器(如 OV5640)通过 I2C 控制,位于 drivers/media/i2c/,作为 v4l2_subdev 存在:

(1)I2C 设备探测:ov5640_probe
  • I2C 总线枚举到 OV5640 时,ov5640_probe 被调用:
    • 创建 v4l2_subdev,初始化 ops(子设备操作集,如 initset_fmt);
    • 通过 v4l2_device_register_subdev 注册到主 v4l2_device(如 SoC 的 CSI 控制器)。
(2)格式设置回调:ov5640_set_fmt
  • 主设备(如 CSI 控制器)设置传感器格式时,调用该回调:
    • 通过 I2C 写入传感器寄存器,配置分辨率(如 1080P)、像素格式(如 YUV422);
    • 上报传感器支持的格式给主设备。
(3)电源管理:ov5640_power_on/ov5640_power_off
  • 控制传感器电源状态,在设备休眠 / 唤醒时被调用,减少无效功耗。

五、媒体控制器:复杂流水线的 “协调者”(扩展)

对于 “传感器 → ISP → 编码器” 等复杂视频流水线,V4L2 通过 ** 媒体控制器(Media Controller)** 管理拓扑结构,核心结构为 struct media_devicestruct media_entitystruct media_link

  • 实体(Entity):将每个子设备(传感器、ISP)抽象为 “实体”;
  • 链接(Link):描述实体间的数据流路径(如 “传感器输出 → ISP 输入”);
  • 拓扑配置:用户空间通过 media-ctl 工具配置实体间的链接,驱动根据链接动态调整数据流。

媒体控制器让复杂设备的流水线配置更灵活(如切换传感器输入、调整 ISP 处理路径)。

http://www.dtcms.com/a/376411.html

相关文章:

  • Android studio安装教程——超详细(含安装包安装教程)
  • 如何将大型音频文件从 iPhone 发送到不同的设备
  • 使用阿里云容器镜像服务 ACR
  • ffmpeg内存模型
  • Android面试指南(八)
  • 不止是进度条:深入PiXSingleGUI的TpSlideProgressBar组件架构设计​
  • Flutter 视频播放器——flick_video_player 介绍与使用
  • 【Java】Hibernate管理Session
  • 【ARMv7】系统复位上电后的程序执行过程
  • Ubuntu引导修复
  • PetaLinux_User_udev
  • 《链表的优雅封装:C++ list 模拟实现与迭代器之美》
  • 基于Redis设计一个高可用的缓存
  • 看涨看跌期权平价公式原理及其拓展
  • Django 基础入门:命令、结构与核心配置全解析
  • 中断系统介绍
  • 算法题 Day5---String类(2)
  • 关于Linux系统调试和性能优化技巧有哪些?
  • 大数据电商流量分析项目实战:Hadoop初认识+ HA环境搭建(二)
  • 软考中级习题与解答——第四章_软件工程(2)
  • AutoTrack-IR-DR200底盘仿真详解:为教育领域打造的高效机器人学习实验平台
  • 介绍 Python Elasticsearch Client 的 ES|QL 查询构建器
  • LeetCode 234. 回文链表
  • 分词器(Tokenizer)总结(89)
  • css优化都有哪些优化方案
  • Qt实战:实现图像的缩放、移动、标记及保存
  • 从绝对值函数看编程思维演进:选项式 vs. 组合式
  • 内网环境下ubuntu 20.04搭建深度学习环境总结
  • 【SQL注入】延时盲注
  • 解决React中通过外部引入的css/scss/less文件更改antDesign中Modal组件内部的样式不生效问题