ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

v4l2_device,v4l2_subdev,media_devcie,video_device的关系与区别

v4l2_device,v4l2_subdev,media_devcie,video_device的关系与区别 一、区分四种device在早期的 Linux 驱动开发中一个普通的 USB 摄像头结构简单整个硬件被看作一个整体完全通过/dev/video0一个节点就能搞定。但在现代嵌入式 SoC如 RK3588/RK3568中相机系统是一个极其复杂的硬件链路通常包含CMOS 传感器 - MIPI CSI 接收器 - ISP图像信号处理器- DMA 引擎负责把数据存入内存。为了解耦并灵活控制这些模块Linux 引入了V4L2 (Video for Linux 2)框架并配合Media Controller (媒体控制器)框架进行管理。这四个核心组件分工明确互不相同1.v4l2_device全局大管家它是什么它是整个 V4L2 驱动实例的内核顶层管理者。核心职责它不直接处理视频数据也没有与之直接挂钩的设备节点。它更像是一个“主板”或容器负责把属于这套硬件的所有子设备v4l2_subdev和视频节点video_device进行统一注册、绑定。它为内部驱动提供全局性的引用计数、自旋锁和全局上下文管理。用户空间表现纯内核内部结构体不向用户空间暴露任何/dev节点。2.v4l2_subdev子设备/处理单元它是什么代表视频流拓扑链路中的某一个具体的硬件处理块如IMX415 传感器、MIPI D-PHY、ISP 处理核心、硬件缩放器等。核心职责它们负责对视频流进行特定的硬件处理如暴光控制、色彩转换、裁剪、对焦但通常不直接负责与系统内存交互即不负责 DMA 搬运数据。子设备之间通过“Pads引脚”连成一个完整的处理链条。用户空间表现往往会暴露为/dev/v4l-subdevX节点。应用端可以通过这些节点利用ioctl直接微调底层硬件的细节参数例如直接控制镜头的白平衡、传感器的曝光时间而不会干扰到链条中的其他节点。3.video_device数据流终端/DMA 节点它是什么视频流物理数据的输入/输出终点。核心职责它主要负责和系统内存进行 DMA 数据传输。当视频流经多个v4l2_subdev处理完毕后最终会汇聚到video_device。它负责申请、队列Queue、出队Dequeue内存缓冲区Buffers把数据最终递交给用户空间或者从内存送给硬件编码器。用户空间表现这就是我们最熟悉的/dev/videoX节点。你的视频捕获程序如 OpenCV、FFmpeg 或你自己写的 V4L2 抓图代码主要就是对它进行VIDIOC_REQBUFS请求缓存和读取操作。4.media_device拓扑结构管理器它是什么Media Controller 框架的顶层结构体。核心职责它负责发现、注册和管理整个板子上的硬件拓扑图Topology。它把每一个v4l2_subdev和video_device视为一个实体Entity管理它们之间是如何连线的Links。用户空间表现暴露为/dev/mediaX。我们可以使用它在运行时动态地开启或关闭某些物理通道比如将 MIPI 接口的数据路由给 ISP0 还是 ISP1。直观理解它们如何协同工作结合下面的经典管线拓扑图可以非常清晰地看出它们的位置左侧的Sensor、中间的Host Frontend和Host Scaler全都是v4l2_subdev子设备它们像流水线工人一样各司其职一步步对图像信号进行加工。最右侧的V4L I/O节点是video_device它是数据流的终点负责将加工完的数据打包送入系统内存。v4l2_device在幕后把上述所有成员都圈在同一个 V4L2 的管理作用域内。media_device则是掌控图中那些蓝色箭头Links的总阀决定数据该往哪条支路流。核心区别对比组件管理级别用户空间节点核心大白话职责media_device媒体框架层/dev/mediaX“接线员”控制各硬件模块之间怎么连线。v4l2_device驱动核心层(无)“后台大管家”纯内核结构管理设备生命周期。v4l2_subdev硬件流水线/dev/v4l-subdevX“流水线工人”只做硬件处理调焦、曝光、缩放不管内存。video_device数据出入口/dev/videoX“搬运工”利用 DMA 将最终的图像帧搬入/搬出 DDR 内存。二、四个结构体分别在驱动的哪个阶段创建在现代嵌入式 Linux如 Rockchip 平台中这些设备的创建并不是在一个驱动里一气呵成的而是遵循“设备树DTS解析 - 独立 Probe - 异步通知Async Notifier组合”的流程。这四个结构体分别在驱动的Probe探针阶段和Async Complete异步匹配完成阶段被先后创建和注册。整体执行顺序如下驱动初始化的核心阶段1.宿主驱动 Probe创建控制核心Host/ISP 驱动启动阶段。当系统解析设备树并加载 ISP 或采集控制器主驱动如rkisp时进入主驱动的probe函数。media_device在此阶段最早被初始化。驱动调用media_device_init()搭建好拓扑结构的骨架。v4l2_device紧接着被创建。驱动调用v4l2_device_register()将其绑定到平台的struct device上作为后续所有子设备的“母板”。2.外设驱动 Probe创建加工单元Sensor/解码芯片驱动启动阶段。摄像头传感器如imx415.c作为 I2C 设备独立进行probe。v4l2_subdev在 Sensor 驱动的probe函数中驱动调用v4l2_subdev_init(sensor-sd, sensor_ops)来初始化这个子设备并通过修改内核结构体填充它的 Pads引脚信息。异步注册初始化后Sensor 驱动调用v4l2_async_register_subdev()把自己挂到内核的等待队列中高喊“我准备好了谁来绑定我”3.异步匹配与绑定V4L2 Async Notifier 阶段。主驱动Host在第一步结束时会注册一个异步通知器Notifier。当它发现系统里刚跑起来的 Sensor 子设备刚好和自己设备树里配置的port/endpoint节点匹配时就会触发主驱动的bound回调函数通过v4l2_device_register_subdev()正式把这个子设备拉进自己的大家庭。4.图谱构建完成创建数据终端Async Notifier Complete 阶段。当设备树里指定的全部外设子设备Sensor、MIPI PHY 等都找齐并绑定后主驱动会触发complete回调。video_device在这个阶段被最终创建。因为只有把前面的流水线Subdev都接好了主驱动才知道当前支持什么格式。驱动分配video_device_alloc()设置fops和ioctl_ops最后调用video_register_device()。此时/dev/videoX节点正式在用户空间可见。激活拓扑最后调用media_device_register()激活整个媒体图谱/dev/mediaX节点正式向用户空间开放。为什么video_device/dev/videoX要在最后创建在复杂的嵌入式硬件中这是一个非常经典的设计拓扑未定节点不现。如果video_device在主驱动一开始probe时就创建此时外接的摄像头 Sensor 可能还没初始化甚至可能根本没插摄像头。如果用户空间此时去open(/dev/video0)并查询能力VIDIOC_QUERYCAP驱动根本无法回答这个数据流终点到底能支持多大分辨率、什么图像格式。因此Linux 驱动框架强制要求必须等所有v4l2_subdev齐备、整条物理链路在内核打通后才能在最后关头注册video_device开放数据接口。三、示例补充我在用户空间打开的是video_device对吧我不能对video_device进行格式配置是吧?这个理解切中了 V4L2 框架演进中最核心的痛点简短的回答是你可以对video_device/dev/videoX配置格式但它能管辖的范围极其有限。在嵌入式 SoC如瑞芯微平台上你绝对无法单靠它来改变摄像头的物理分辨率。为了让你彻底搞懂我们需要把驱动分为传统 monolithic一体化模式和现代 MCMedia Controller媒体控制器中心模式来看。情况一传统的 USB 摄像头UVC 驱动—— 可以在video_device配置一切如果你接的是一个免驱的 USB 摄像头它的底层硬件被打包成了一个整体。怎么做应用层直接打开/dev/video0然后调用ioctl(fd, VIDIOC_S_FMT, fmt)。驱动内部行为此时的video_device权力极大。你给它设置1920x1080 MJPEG它的驱动内部会自动把这个请求“透传”给底层的 Sensor 芯片让 Sensor 改变输出分辨率。结论这种情况下你完全可以也必须在video_device上配置格式。情况二现代嵌入式 SoC如 RK3588 / 树莓派—— 实行“双层配置”在复杂的嵌入式硬件中如果只在/dev/videoX上调用VIDIOC_S_FMT你会发现根本无法改变摄像头的物理输出大小。因为这里实行了严格的职责分离1.v4l2_subdev负责配置硬件/物理层格式Media Bus Format你想让 Sensor 物理输出3840x2160还是1920x1080输出的是RAW10还是RAW12操作对象必须去操作子设备节点如/dev/v4l-subdev0或者用media-ctl工具。本质这是在调节硬件源头的进水流量和裁剪窗口。2.video_device负责配置内存/终点层格式Memory Format当你把源头的硬件格式配好后数据流经过 ISP 处理最后来到了负责 DMA 搬运的video_device/dev/videoX。此时你依然需要对它调用一次VIDIOC_S_FMT但意义完全变了你能配什么你只能配置最终存入系统 DDR 内存的像素格式如NV12、YUYV、RGB888。限制条件极度严格你在这里设置的分辨率Width/Height必须与前面子设备Subdev吐出来的分辨率保持一致或等比例缩放。如果瞎配会怎样如果你把 Subdev 配成了1080p RAW10却跑去把video_device配成4K NV12在调用ioctl(video_fd, VIDIOC_STREAMON)开启流时内核会直接抛出EINVAL无效参数或EPIPE链路管道断开错误拒绝执行。为什么现代 Linux 搞得这么麻烦因为在嵌入式系统里一对多的关系太常见了。假设你的 RK3588 接入了一路摄像头数据流出来后同时送给了硬件缩放器 A输出 1080p 用于 AI 检测和硬件缩放器 B输出 4K 用于录像。这时候系统会生成两个数据终点/dev/video0和/dev/video1。如果允许用户直接在/dev/video0上强行把 Sensor 改成 720p那正在使用/dev/video1录制 4K 视频的线程直接就崩溃了。所以现代 V4L2 规定源头Sensor/Subdev的格式由拓扑管理者统一协调而终点video_device只负责管好自己那一块内存 buffer 怎么排布。实际开发的正确姿势你在写代码或者调试脚本时标准流程一定是先用media-ctl把/dev/v4l-subdevX统统配成1920x1080, 格式: SRGGB10。应用层open(/dev/videoX)。应用层对video_device发起VIDIOC_S_FMT高喊“我要1920x1080的V4L2_PIX_FMT_NV12格式的内存切片”开始抓流。
返回列表