1. V4L2设备枚举与能力查询
任务目标
编写一个C程序,枚举Linux系统中所有可用的V4L2视频设备(/dev/video*),对每个设备执行VIDIOC_QUERYCAP查询,输出驱动名称、总线信息、设备能力标志及对应的能力描述。
环境依赖
- Linux系统(内核已启用V4L2框架)
- V4L2开发头文件:
libv4l-dev(Debian/Ubuntu)或v4l-utils(部分发行版提供头文件) - 编译器:gcc
关键完整代码
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <dirent.h>
#include <errno.h>
static void print_caps(__u32 caps) {
printf(" 设备能力标志: 0x%08x\n", caps);
printf(" 支持: ");
if (caps & V4L2_CAP_VIDEO_CAPTURE) printf("视频采集 ");
if (caps & V4L2_CAP_VIDEO_OUTPUT) printf("视频输出 ");
if (caps & V4L2_CAP_VIDEO_OVERLAY) printf("视频叠加 ");
if (caps & V4L2_CAP_VIDEO_M2M) printf("内存到内存 ");
if (caps & V4L2_CAP_STREAMING) printf("流式I/O ");
if (caps & V4L2_CAP_READWRITE) printf("读写I/O ");
if (caps & V4L2_CAP_DEVICE_CAPS) printf("独立设备能力 ");
if (caps & V4L2_CAP_META_CAPTURE) printf("元数据采集 ");
if (caps & V4L2_CAP_TOUCH) printf("触摸设备 ");
if (caps & V4L2_CAP_IO_MC) printf("媒体控制器I/O ");
printf("\n");
}
static void query_device(const char *path) {
int fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0) {
printf("%s: 打开失败 (%s)\n", path, strerror(errno));
return;
}
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
printf("%s: QUERYCAP失败 (%s)\n", path, strerror(errno));
close(fd);
return;
}
printf("设备节点: %s\n", path);
printf(" 驱动名称: %s\n", cap.driver);
printf(" 设备名称: %s\n", cap.card);
printf(" 总线信息: %s\n", cap.bus_info);
printf(" 驱动版本: %u.%u.%u\n",
(cap.version >> 16) & 0xff,
(cap.version >> 8) & 0xff,
cap.version & 0xff);
if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) {
printf(" 设备特定能力:\n");
print_caps(cap.device_caps);
} else {
printf(" 全局能力:\n");
print_caps(cap.capabilities);
}
printf("\n");
close(fd);
}
int main(void) {
DIR *dir = opendir("/dev");
if (!dir) {
perror("opendir /dev");
return 1;
}
struct dirent *entry;
int found = 0;
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, "video", 5) == 0) {
char path[64];
snprintf(path, sizeof(path), "/dev/%s", entry->d_name);
query_device(path);
found = 1;
}
}
closedir(dir);
if (!found) {
printf("未找到任何 /dev/video* 设备\n");
return 1;
}
return 0;
}
编译与运行
gcc -o v4l2_enum v4l2_enum.c
sudo ./v4l2_enum
需要root权限或加入video组才能打开设备节点。
验证方法
- 正常输出:每个设备节点显示驱动名、设备名、总线信息、版本号及能力标志,能力描述中至少包含一项(如“视频采集”或“流式I/O”)。
- 对比系统工具:运行
v4l2-ctl --list-devices(若已安装v4l-utils),对比设备名称与总线信息是否一致。 - 无设备情况:若系统无摄像头或视频设备,程序输出“未找到任何 /dev/video* 设备”并返回退出码1。
常见问题
- 打开设备失败:确认设备节点存在且当前用户有权限(
ls -l /dev/video*查看属组)。 - ioctl失败:某些设备(如部分USB摄像头)在打开时可能需要特定访问模式,可尝试将
O_RDWR改为O_RDONLY重试。 - 能力标志为空:部分驱动未正确填充
capabilities字段,可检查内核日志(dmesg)确认驱动加载状态。