2. 视频设备打开与关闭
任务目标
实现V4L2视频设备的打开与关闭操作,包含打开失败时的错误处理,并确保设备文件描述符在关闭时正确释放。本案例以Linux字符设备(如/dev/video0)为操作对象,通过系统调用完成设备生命周期管理。
核心实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define DEVICE_PATH "/dev/video0"
/**
* 打开V4L2视频设备
* @param path 设备文件路径
* @return 成功返回文件描述符(>=0),失败返回-1
*/
int v4l2_open_device(const char *path) {
if (path == NULL) {
fprintf(stderr, "错误:设备路径为空\n");
return -1;
}
int fd = open(path, O_RDWR);
if (fd < 0) {
perror("打开设备失败");
return -1;
}
// 验证设备是否为V4L2视频设备
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
perror("查询设备能力失败");
close(fd);
return -1;
}
// 检查是否为视频采集设备(可选校验)
if (!(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "错误:%s 不是视频采集设备\n", path);
close(fd);
return -1;
}
printf("成功打开设备: %s\n", path);
printf("驱动名称: %s\n", cap.driver);
printf("设备名称: %s\n", cap.card);
return fd;
}
/**
* 关闭V4L2视频设备
* @param fd 设备文件描述符
* @return 0表示成功,-1表示失败
*/
int v4l2_close_device(int fd) {
if (fd < 0) {
fprintf(stderr, "错误:无效的文件描述符\n");
return -1;
}
if (close(fd) < 0) {
perror("关闭设备失败");
return -1;
}
printf("设备已关闭 (fd=%d)\n", fd);
return 0;
}
int main(void) {
int fd = v4l2_open_device(DEVICE_PATH);
if (fd < 0) {
return EXIT_FAILURE;
}
// 此处可插入后续操作(如格式设置、缓冲申请等)
if (v4l2_close_device(fd) < 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
编译与运行
# 编译(需要Linux内核头文件)
gcc -o v4l2_open_close v4l2_open_close.c
# 运行(需要root权限或video组权限)
./v4l2_open_close
验证方法
- 正常路径:运行程序后,终端应依次输出设备打开成功信息(含驱动名称和设备名称)以及设备关闭信息,程序退出码为0。
- 错误路径测试:将
DEVICE_PATH改为不存在的路径(如/dev/video99),重新编译运行,程序应输出打开设备失败错误并返回非零退出码。 - 资源释放验证:在程序运行期间,通过另一终端执行
ls -l /proc/$(pgrep v4l2_open_close)/fd,可观察到设备文件描述符在关闭后不再出现在列表中。
关键说明
- 打开设备使用
O_RDWR模式,确保后续可进行读写操作;若仅需查询能力,可使用O_RDONLY。 - 通过
VIDIOC_QUERYCAPioctl验证设备身份,避免误操作非V4L2设备;若设备不支持该ioctl,open虽成功但后续操作会失败,因此必须检查此步骤。 - 关闭设备时只需调用
close(),内核会自动释放相关资源;本案例中的v4l2_close_device封装主要用于统一错误处理和日志输出。 - 若系统存在多个视频设备,可通过修改
DEVICE_PATH宏或将其改为命令行参数来适配不同设备。