认识 Linux Video 子系统

文摘   2024-11-22 18:26   广东  

关注+星号公众号,不容错过精彩

作者:HywelStar


 

在Linux 系统中有一个关于视频相关的子系统叫做V4L2子系统,全程叫做(video4linux2),由于在市面上会遇到各种各样的摄像头,接入Linux系统中,新增了Video子系统这个模块,大大方便了在应用层的使用,对摄像头的控制,统一了接口和使用流程的便利性。相关摄像头,视频基础知识可以查看文章末尾推荐列表或者点击原文。

1. 概述

  • • 介绍 Linux 视频子系统的基本概念:Linux 视频子系统是一个管理和控制视频设备的框架,主要用于支持各种视频设备(如摄像头、视频采集卡等)的数据采集和流式传输。它为多媒体应用提供统一的接口,极大简化了视频设备的开发。

  • • Video4Linux2(V4L2)是 Linux 视频子系统的核心,它定义了一系列接口和驱动框架,用于支持视频采集、编码、解码等操作。V4L2 是在Linux 2.6 版本开始推行,这套框架更重要的是,标准化了视频的接口,统一了应用所控制的接口,对于Video设备进行了统一的管理。

2. V4L2 框架结构

V4L2 驱动程序的基本架构:包括硬件层,内核驱动,用户空间的交互三大部分。

下图以 RK3588 为例,其他平台的架构大致相似。

  • • 应用层

在V4L2中提供给用户空间层是应用程序访问视频设备的接口层,主要通过调用 V4L2 API 与驱动交互,实现对视频设备的控制和数据获取。

设备文件接口:

/dev/mediaX、/dev/videoX、/dev/v4l-subdevX 等,这些设备文件为应用程序提供了访问视频和媒体子设备的接口,用于配置和获取视频流。通常采用ioctl的接口进行调用。

Media 控制器 :用户空间的控制工具,用于操作和管理 Media Controller(媒体控制器)接口。它通过 /dev/mediaX 设备文件与内核的 Media Framework 交互,控制设备的路由和设置。

典型操作流程

通过 ioctl 操作来设置视频格式、分辨率、帧率等。

请求和配置缓冲区,将视频流数据映射到用户空间。

启动视频流,通过内存映射或直接读取的方式获取帧数据。

  • • 内核层

功能:V4L2 核心层负责管理驱动程序和硬件抽象层之间的交互,为 V4L2 驱动提供标准化接口,使得用户空间可以与不同视频设备进行一致性操作。

内核层主要存在两个比较重要的部分:V4L2 FrameworkMedia Framework

V4L2 Framework

视频设备驱动的核心模块,负责管理视频设备的标准接口,将应用程序的 ioctl 请求分发到对应的驱动程序模块中。对于设备的注册卸载功能,处理资源分配,支持复杂的多媒体设备,帮助用户空间程序控制不同子设备的连接、路由和操作。

它同时支持主设备(videoX)和子设备(v4l-subdevX), 实现对不同视频设备的统一管理。

Media Framework

提供了一个多媒体设备的抽象层,通过 Media Controller API 管理和控制多种媒体设备(如摄像头传感器、ISP、编码器等)的拓扑结构和数据流路由。

Media Framework 在图中与多个驱动模块相连,包括 ISP Driver、CSI2 Host Driver 等,统一协调这些模块的工作,使视频流可以顺畅传递。

  • • 底层硬件(Hardware)

这是实际的视频采集和输出硬件设备,包括摄像头、视频捕捉卡、编解码器等设备。它负责视频数据的生成、采样和输出。

硬件接口方式,图中采用 MIPI CSI 接口进行数据传输,图中还包含一个ISP部分,负责对图形进行信号处理,包括降噪、白平衡、色彩校正等操作,最终输出可用的图像帧。

3. V4L2 API 核心接口

V4L2 API 的核心接口主要用于应用程序与设备之间的常用操作接口。

3.1 设备查询接口

设备查询接口用于获取视频设备的基本信息,帮助应用程序了解设备的功能和支持的操作。这类接口一般在应用程序初始化阶段使用,以确保设备支持期望的操作和格式。

常用接口:VIDIOC_QUERYCAP

使用方法:常用ioctl(fd, VIDIOC_QUERYCAP, &cap) 其中capstruct v4l2_capability

struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
    perror("Failed to query device capabilities");
}

到底可以获取哪些数据,查看接口体:

3.2 格式配置接口

格式配置接口用于设置或查询视频设备的工作格式,包括分辨率、像素格式、颜色空间等。这些接口确保视频数据在传输和处理过程中具有正确的格式。

常用接口:VIDIOC_S_FMT:设置视频数据的格式。VIDIOC_G_FMT:获取当前的视频数据格式。

使用方法:通过 VIDIOC_G_FMT 获取当前格式,然后根据需要修改并通过 VIDIOC_S_FMT 设置新格式。

struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1280;
fmt.fmt.pix.height = 720;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
    perror("Failed to set video format");
}

3.3 缓冲区管理

缓冲区管理接口用于请求、管理和交换视频缓冲区,是视频流处理中最重要的接口之一。V4L2 支持多种缓冲区管理模式,常见的是基于内存映射(mmap)的缓冲区管理。

常用接口:

  • • VIDIOC_REQBUFS:请求视频缓冲区。

  • • VIDIOC_QUERYBUF:查询缓冲区的信息。

  • • VIDIOC_QBUF:将缓冲区放入输入队列,准备接收数据。

  • • VIDIOC_DQBUF:从输出队列中取出已填充数据的缓冲区。

使用方法:

struct v4l2_requestbuffers req;
req.count = 4;  // 请求4个缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
    perror("Failed to request buffers");
}

// 查询和映射缓冲区
struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;  // 查询第一个缓冲区
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
    perror("Failed to query buffer");
}
// 将缓冲区映射到用户空间
void *buffer_start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);

3.4 数据流控制

数据流控制接口用于启动或停止视频流。这些接口通常用于实时视频采集应用中,确保视频流在需要时可以被快速启动和关闭。

常用接口:VIDIOC_STREAMON:启动视频流。VIDIOC_STREAMOFF:停止视频流。

使用方法:应用程序通过 ioctl(fd, VIDIOC_STREAMON, &type) 启动数据流,type 通常为 V4L2_BUF_TYPE_VIDEO_CAPTURE,表示视频采集。

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
    perror("Failed to start video stream");
}

4. 视频缓冲区管理

视频缓冲区管理为什么这里单独一小节,因为是 V4L2 中处理视频数据的重要组成部分。通过高效管理视频缓冲区,可以提升视频采集、传输和处理的性能。V4L2 支持多种缓冲区类型和缓冲区队列管理机制,以适应不同应用的需求。

4.1 缓冲区类型

内存映射(MMAP)、用户指针(User Pointer)、DMA 缓冲区

4.1.1 MMAP

概述:内存映射(MMAP)模式是将视频设备的缓冲区直接映射到用户空间,使应用程序可以直接访问视频数据,而不需要额外的内存拷贝操作。

优点:效率高,适应性非常广;

使用方法:在3.3 有介绍;

4.1.2 User Pointer

概述:在 User Pointer 模式下,应用程序负责管理缓冲区的内存地址并将其指针传递给驱动。这种模式提供了更大的灵活性,应用程序可以在用户空间中分配和管理缓冲区。

优点

灵活性高:应用程序可以根据需求动态分配缓冲区,适用于需要频繁调整缓冲区大小或数量的场景。

使用方法

struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
buf.m.userptr = (unsigned long)user_buffer;
buf.length = buffer_length;
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
    perror("Failed to enqueue buffer");
}

4.1.3 DMA

概述:DMA 模式使用直接内存访问技术,将视频数据从视频设备直接传输到用户空间的缓冲区,通常用于需要高数据吞吐量的场景。

优点:

CPU 占用低:DMA 可以完全绕过 CPU,直接在内存之间传输数据,因此非常适合硬件解码、视频采集等对数据传输性能要求高的应用。

适用于硬件解码:在需要实时视频传输的应用中,DMA 可以有效减少延迟。

4.2 缓冲区队列管理

缓冲区队列管理是指通过队列的方式管理缓冲区的排队和出队,以确保视频流数据的有序传输。V4L2 提供了 VIDIOC_QBUF 和 VIDIOC_DQBUF 接口,用于将缓冲区放入队列和从队列中取出缓冲区。

对于缓冲区队列管理主要以下流程:

请求缓冲区:调用 VIDIOC_REQBUFS 申请缓冲区。
查询缓冲区:使用 VIDIOC_QUERYBUF 获取缓冲区的具体信息(如地址和大小)。
映射缓冲区(MMAP 模式下):使用 mmap 将缓冲区映射到用户空间。
启动视频流:调用 VIDIOC_STREAMON 开启视频流。
缓冲区入队:调用 VIDIOC_QBUF 将空闲缓冲区放入队列。
缓冲区出队:调用 VIDIOC_DQBUF 取出填充数据的缓冲区。
循环操作:重复步骤 5 和 6,直到需要停止视频流。
停止视频流:调用 VIDIOC_STREAMOFF 停止视频流。
解除映射并释放缓冲区:使用 munmap 解除映射,并调用 VIDIOC_REQBUFS 将 count 设置为 0 释放缓冲区

5. 常用工具

使用 V4L2 进行视频采集和处理时,很多适合采用一些工具去验证设备,控制设备,这样降低开发时间成本。目前可能会使用到以下几种:

  • • v4l2-ctl:常用的 V4L2 控制工具,帮助调试和配置视频设备

  • • ffmpeggstreamer:如何使用这些工具进行视频采集、转码和流式传输

针对上面每个工具的使用方法,这里不再一一展开,后续有机会单独列出。不过目前很多SoC采用gstreamer这种方式。

6. 总结

本章节主要对Linux Video子系统内容进行介绍,认识V4L2框架接口,V4L2 提供了统一的接口,使得应用层可以方便地与不同的视频设备交互,从而简化了开发复杂视频应用的流程。在实际应用中,可进一步结合编码、网络传输等模块,构建功能更丰富的视频采集系统。后续有机会再介绍相关数据获取等其他内容。


往期推荐



认识摄像头模组

揭密摄像头DVP接口

探索摄像头MIPI 接口

视频编码质量效果评估

常见的视频编码格式介绍

视频面试常见问题

嵌入式系统常用授时方案对比

Type-C 接口耳机分析

嵌入式C语言代码风格简述

USB PD 协议介绍与交互过程

Type-C 的工作原理


 

           
“阅读原文”一起来充电吧!


码思途远
一位码农的日常分享,专注嵌入式领域相关知识。
 最新文章