1. V4L2设备枚举与能力查询
任务目标
编写一个Linux用户空间程序,枚举系统中所有可用的V4L2视频设备(/dev/video*),对每个设备打开文件句柄并查询其基本能力,包括驱动名称、设备名称、总线信息、设备类型(capture/output等)以及支持的像素格式列表,最终将结果格式化打印到终端。
环境与依赖
- 操作系统:Linux(内核已启用V4L2框架)
- 开发库:需安装
libv4l-dev(提供头文件与链接库) - 编译器:gcc
- 设备节点:
/dev/video*(无物理摄像头时可用v4l2loopback虚拟设备验证)
关键完整代码
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <dirent.h>
#include <libgen.h>
static const char *buf_type_str(__u32 type) {
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: return "Video Capture";
case V4L2_BUF_TYPE_VIDEO_OUTPUT: return "Video Output";
case V4L2_BUF_TYPE_VIDEO_OVERLAY: return "Video Overlay";
default: return "Other";
}
}
static void print_formats(int fd) {
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf(" Supported formats (capture):\n");
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
printf(" [%u] %-10s %s\n", fmtdesc.index,
(char *)fmtdesc.pixelformat, fmtdesc.description);
fmtdesc.index++;
}
}
static void query_device(const char *path) {
int fd = open(path, O_RDWR);
if (fd < 0) {
perror(path);
return;
}
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
perror("VIDIOC_QUERYCAP");
close(fd);
return;
}
printf("=== %s ===\n", path);
printf(" Driver : %s\n", cap.driver);
printf(" Card : %s\n", cap.card);
printf(" Bus info : %s\n", cap.bus_info);
printf(" Version : %u.%u.%u\n",
(cap.version >> 16) & 0xFF,
(cap.version >> 8) & 0xFF,
cap.version & 0xFF);
printf(" Device caps : 0x%08x\n", cap.device_caps);
if (cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)
printf(" Type : %s\n", buf_type_str(V4L2_BUF_TYPE_VIDEO_CAPTURE));
if (cap.device_caps & V4L2_CAP_VIDEO_OUTPUT)
printf(" Type : %s\n", buf_type_str(V4L2_BUF_TYPE_VIDEO_OUTPUT));
print_formats(fd);
printf("\n");
close(fd);
}
int main() {
struct dirent **namelist;
int n = scandir("/dev", &namelist, NULL, alphasort);
if (n < 0) {
perror("scandir");
return 1;
}
for (int i = 0; i < n; i++) {
if (strncmp(namelist[i]->d_name, "video", 5) == 0) {
char path[64];
snprintf(path, sizeof(path), "/dev/%s", namelist[i]->d_name);
query_device(path);
}
free(namelist[i]);
}
free(namelist);
return 0;
}
编译与运行
gcc -o v4l2_enum v4l2_enum.c
sudo ./v4l2_enum # 部分设备节点需要root权限才能打开
验证方法
程序执行后,终端会按设备节点顺序输出每个V4L2设备的信息块。判定通过的观察点:
- 每个
/dev/video*节点均有一块以=== /dev/videoX ===开头的输出; - Driver/Card/Bus info 字段非空且与
v4l2-ctl --all -d /dev/videoX命令输出一致; - 若设备支持视频采集,格式列表至少包含一项(如YUYV或MJPG);
- 无设备时程序正常退出,不产生段错误。
常见问题
- 无设备节点:加载
v4l2loopback内核模块创建虚拟设备:sudo modprobe v4l2loopback。 - 权限不足:将用户加入
video组或使用sudo运行。 - 格式枚举为空:部分输出型设备不支持CAPTURE格式枚举,属正常现象,可查看
device_caps确认设备类型。