关注+星号公众号,不容错过精彩
作者: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 Framework
, Media 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)
其中cap
是struct 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 控制工具,帮助调试和配置视频设备•
ffmpeg
、gstreamer
:如何使用这些工具进行视频采集、转码和流式传输
针对上面每个工具的使用方法,这里不再一一展开,后续有机会单独列出。不过目前很多SoC采用gstreamer
这种方式。
6. 总结
本章节主要对Linux Video子系统内容进行介绍,认识V4L2框架接口,V4L2 提供了统一的接口,使得应用层可以方便地与不同的视频设备交互,从而简化了开发复杂视频应用的流程。在实际应用中,可进一步结合编码、网络传输等模块,构建功能更丰富的视频采集系统。后续有机会再介绍相关数据获取等其他内容。
往期推荐