V4L2(Video for Linux 2)是 Linux 内核中用于视频设备的子系统,提供了统一的接口,使用户空间程序能够与各种视频设备进行通信和控制。V4L2 主要用于处理视频输入设备(例如摄像头)和视频输出设备(例如显示屏等)。
V4L2 提供了一个统一的接口,允许用户空间程序与各种视频设备进行通信,而无需了解底层硬件的具体细节。这使得开发视频应用程序更加方便。 同时V4L2 支持各种不同类型的视频设备,包括摄像头、视频采集卡、Webcam 等。这些设备可以通过 V4L2 接口进行访问和控制。V4L2 支持多种视频和图像格式,包括 YUV 格式、RGB 格式等。V4L2 允许用户从视频设备中采集视频流,并提供了配置和控制相关参数的功能,除了采集视频流,V4L2 还支持将视频流输出到显示设备或其他目标等。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#define VIDEO_DEVICE "/dev/video0"
#define WIDTH 640
#define HEIGHT 480
#define NUM_BUFFERS 4
//打开摄像头设备
int fd = open(VIDEO_DEVICE, O_RDWR);
if (fd == -1) {
perror("Error opening device");
exit(EXIT_FAILURE);
}
//设置视频格式
struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = WIDTH;
fmt.fmt.pix.height = HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; // 使用 MJPEG 格式,可以根据需求选择其他格式
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("Error setting format");
exit(EXIT_FAILURE);
}
//请求缓冲区
struct v4l2_requestbuffers req = {0};
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
req.count = NUM_BUFFERS;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
perror("Error requesting buffers");
exit(EXIT_FAILURE);
}
struct buffer {
void *start;
size_t length;
} *buffers = calloc(req.count, sizeof(*buffers));
// 映射缓冲区到用户空间
for (unsigned int i = 0; i < req.count; ++i) {
struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
perror("Error querying buffer");
exit(EXIT_FAILURE);
}
buffers[i].length = buf.length;
buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[i].start == MAP_FAILED) {
perror("Error mapping buffer");
exit(EXIT_FAILURE);
}
}
// 启动视频流
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
perror("Error starting stream");
exit(EXIT_FAILURE);
}
//捕获视频帧
while (1) {
struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
perror("Error dequeueing buffer");
exit(EXIT_FAILURE);
}
// 处理图像数据,可以在这里添加对图像的处理代码
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
perror("Error queueing buffer");
exit(EXIT_FAILURE);
}
}
// 停止视频流和清理资源
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
perror("Error stopping stream");
exit(EXIT_FAILURE);
}
for (unsigned int i = 0; i < req.count; ++i) {
munmap(buffers[i].start, buffers[i].length);
}
free(buffers);
close(fd);
步骤总结
- 打开设备: 使用 open 函数打开视频设备文件。
- 配置参数: 使用 VIDIOC_S_FMT 设置视频设备的格式和参数。
- 请求缓冲区: 使用 VIDIOC_REQBUFS 请求视频缓冲区。
- 映射缓冲区: 使用 mmap 将内核缓冲区映射到用户空间。
- 启动视频流: 使用 VIDIOC_STREAMON 启动视频流。
- 捕获和处理帧:使用 VIDIOC_QBUF 将缓冲区排队,然后使用 VIDIOC_DQBUF 从队列中取出缓冲区并处理捕获的帧。
- 停止视频流使用 VIDIOC_STREAMOFF 停止视频流。
- 释放资源:释放映射的缓冲区和关闭设备文件。
编程的注意事项
1.确保硬件设备(例如摄像头)支持 V4L2 接口。确保V4L2 内核模块已加载。可以使用 lsmod | grep videodev 命令来检查是否加载了 videodev 模块。
2.对于每个 V4L2 API 调用都要进行错误处理。V4L2 操作可能会失败,需要适当地处理错误情况
3.使用 mmap 函数将内核缓冲区映射到用户空间。注意处理内存映射的错误和释放映射的资源。