Video for Linux two(Video4Linux2)简称V4L2,是V4L的改进版。(Video4Linux2)是 Linux 内核中用于捕获视频数据的框架和 API。
它提供了一套标准化的接口,方便开发者与各种视频硬件设备进行交互,并支持丰富的视频格式和功能。通过使用 V4L2,开发者可以构建高效的视频捕获和处理应用程序。
V4L2支持三种方式来采集图像:内存映射方式(mmap)、直接读取方式(read)和用户指针。内存映射的方式采集速度较快,一般用于连续视频数据的采集,实际工作中的应用概率更高;直接读取的方式相对速度慢一些,所以常用于静态图片数据的采集;用户指针使用较少。
设备抽象:V4L2 提供了一种统一的接口来抽象不同的硬件设备,使得应用程序无需关心具体的硬件实现细节。
视频捕获:支持从视频捕获设备(如摄像头)中获取视频数据。V4L2 提供了多种视频格式和帧率的支持。
视频输出:支持将视频数据发送到视频输出设备(如显示器或其他显示设备)。
控制接口:允许应用程序控制视频设备的各种参数,如亮度、对比度、饱和度等。
内存映射和用户指针:提供了高效的数据传输机制,包括内存映射(mmap)和用户指针(user pointer)方式,使得视频数据的捕获和处理更加高效。
使用摄像头时,核心是"获得数据"。所以先讲如何获取数据,即如何得到buffer。
摄像头采集数据时,是一帧又一帧地连续采集。所以需要申请若干个buffer,驱动程序把数据放入buffer,APP从buffer得到数据。这些buffer可以使用链表来管理。
驱动程序周而复始地做如下事情:
从硬件采集到数据
把"空闲链表"取出buffer,把数据存入buffer
把含有数据的buffer放入"完成链表"
APP也会周而复始地做如下事情:
监测"完成链表",等待它含有buffer
从"完成链表"中取出buffer
处理数据
把buffer放入"空闲链表"
链表操作示意图如下:
参考mjpg-streamer和video2lcd,总结了摄像头的使用流程,如下:、
列出帧细节
open:打开设备节点/dev/videoX
ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力,比如
确认它是否是"捕获设备",因为有些节点是输出设备
确认它是否支持mmap操作,还是仅支持read/write操作
ioctl VIDIOC_ENUM_FMT:枚举它支持的格式
ioctl VIDIOC_S_FMT:在上面枚举出来的格式里,选择一个来设置格式
申请buffer用来放置摄像头数据
ioctl VIDIOC_REQBUFS:申请buffer,APP可以申请很多个buffer,但是驱动程序不一定能申请到
ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射
如果申请到了N个buffer,这个ioctl就应该执行N次
执行mmap后,APP就可以直接读写这些buffer
ioctl VIDIOC_QBUF:把buffer放入"空闲链表"
如果申请到了N个buffer,这个ioctl就应该执行N次
获取数据
ioctl VIDIOC_STREAMON:启动摄像头
存储数据
这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"
poll/select
ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer
处理:前面使用mmap映射了每个buffer的地址,处理时就可以直接使用地址来访问buffer
ioclt VIDIOC_QBUF:把buffer放入"空闲链表"
ioctl VIDIOC_STREAMOFF:停止摄像头
问题一、linux/videodev.h:没有那个文件或目录
解决:
sudo apt-get install libv4l-dev
cd /usr/include/linux
sudo ln -s ../libv4l1-videodev.h videodev.h
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <linux/types.h> /* for videodev2.h */
- #include <linux/videodev2.h>
-
- /* ./video_test </dev/video0> */
-
- int main(int argc, char **argv)
- {
- int fd;
- struct v4l2_fmtdesc fmtdesc;
- struct v4l2_frmsizeenum fsenum;
- int fmt_index = 0;
- int frame_index = 0;
-
- if (argc != 2)
- {
- printf("Usage: %s , print format detail for video device\n", argv[0]);
- return -1;
- }
-
- /* open */
- fd = open(argv[1], O_RDWR);
- if (fd < 0)
- {
- printf("can not open %s\n", argv[1]);
- return -1;
- }
-
- while (1)
- {
- /* 枚举格式 */
- fmtdesc.index = fmt_index; // 比如从0开始
- fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type为"捕获"
- if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
- break;
-
- frame_index = 0;
- while (1)
- {
- /* 枚举这种格式所支持的帧大小 */
- memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));
- fsenum.pixel_format = fmtdesc.pixelformat;
- fsenum.index = frame_index;
-
- if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0)
- {
- printf("format %s,%d, framesize %d: %d x %d\n", fmtdesc.description, fmtdesc.pixelformat, frame_index, fsenum.discrete.width, fsenum.discrete.height);
- }
- else
- {
- break;
- }
-
- frame_index++;
- }
-
- fmt_index++;
- }
-
- return 0;
- }
调用ioctl VIDIOC_ENUM_FMT可以枚举摄像头支持的格式,但是无法获得更多细节(比如支持哪些分辨率),
调用ioctl VIDIOC_G_FMT可以获得"当前的格式",包括分辨率等细节,但是无法获得其他格式的细节。
需要结合VIDIOC_ENUM_FMT、VIDIOC_ENUM_FRAMESIZES这2个ioctl来获得这些细节:
VIDIOC_ENUM_FMT:枚举格式
VIDIOC_ENUM_FRAMESIZES:枚举指定格式的帧大小(即分辨率)
用于描述视频设备支持的格式信息的结构体。
struct v4l2_fmtdesc {
__u32 index; /* Format number */
__u32 type; /* enum v4l2_buf_type */
__u32 flags;
__u8 description[32]; /* Description string */
__u32 pixelformat; /* Format fourcc */
__u32 reserved[4];
};
index (__u32 index)
type (__u32 type)
enum v4l2_buf_type。它表示查询的格式属于哪种类型的缓冲区,常见的值包括 V4L2_BUF_TYPE_VIDEO_CAPTURE(视频捕获)和 V4L2_BUF_TYPE_VIDEO_OUTPUT(视频输出)。flags (__u32 flags)
description (__u8 description[32])
pixelformat (__u32 pixelformat)
V4L2_PIX_FMT_YUYV 表示 YUYV 格式,V4L2_PIX_FMT_MJPEG 表示 MJPEG 格式。FourCC 是一种常用的编码方式,使用四个字节(通常是ASCII字符)来唯一标识视频数据格式。reserved (__u32 reserved[4])
用途:struct v4l2_fmtdesc 通常与 VIDIOC_ENUM_FMT ioctl 操作一起使用。应用程序使用这个结构体来枚举视频设备支持的所有格式。
struct v4l2_frmsizeenum {
__u32 index; /* Frame size number */
__u32 pixel_format; /* Pixel format */
__u32 type; /* Frame size type the device supports. */
union { /* Frame size */
struct v4l2_frmsize_discrete discrete;
struct v4l2_frmsize_stepwise stepwise;
};
__u32 reserved[2]; /* Reserved space for future use */
};
__u32 index
index = 0 开始,并递增 index 来查询设备支持的所有帧尺寸。__u32 pixel_format
V4L2_PIX_FMT_YUYV。__u32 type
V4L2_FRMSIZE_TYPE_DISCRETE:设备支持的帧尺寸是离散的(单个特定的值)。V4L2_FRMSIZE_TYPE_CONTINUOUS:设备支持连续的帧尺寸范围。V4L2_FRMSIZE_TYPE_STEPWISE:设备支持的帧尺寸在一个范围内,并且可以按特定步长进行调整。union
type 字段的值,联合体包含具体的帧尺寸信息。
__u32 reserved[2]
ioctl():
ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现,也可以用来读写数据。

应用程序调用 ioctl 操作
ioctl 函数,向设备文件(如 /dev/video0)发送 VIDIOC_ENUM_FMT 请求,例如查询视频设备支持的格式,并传递一个 struct v4l2_fmtdesc 结构体来接收格式信息。设备驱动程序处理请求
/dev/video0 关联的设备驱动程序。设备驱动程序中会有相应的 ioctl 处理函数来接收和处理这个请求。设备驱动程序接收到 ioctl 请求后,会执行相应的处理逻辑。以 V4L2 驱动程序为例,它会有一个 vidioc_enum_fmt 函数来处理 VIDIOC_ENUM_FMT 请求。硬件设备返回信息
v4l2_fmtdesc 结构体。驱动程序返回结果
struct v4l2_fmtdesc 结构体返回给应用程序,应用程序就可以读取其中的信息。

RGB:
YUV:
MJPEG(Motion JPEG):
H.264 / H.265: