3. 实现 V4L2 的 querycap 能力查询
任务目标
编写一个Linux用户空间程序,通过V4L2接口枚举指定摄像头设备支持的所有像素格式及对应的分辨率,并将结果打印到终端。该程序用于验证摄像头驱动是否正确注册了格式描述符,是后续配置采集参数前的必要检查步骤。
环境与依赖
- 操作系统:Linux(带V4L2内核驱动)
- 摄像头设备:
/dev/video0(可按需修改) - 编译依赖:仅需Linux内核头文件(通常已随libc-dev安装)
- 编译工具:gcc
关键完整代码
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
static const char *pix_fmt_desc(unsigned int pf) {
switch (pf) {
case V4L2_PIX_FMT_YUYV: return "YUYV 4:2:2";
case V4L2_PIX_FMT_MJPEG: return "MJPEG";
case V4L2_PIX_FMT_H264: return "H.264";
case V4L2_PIX_FMT_NV12: return "NV12";
case V4L2_PIX_FMT_RGB24: return "RGB24";
default: return "Other/Unknown";
}
}
static void enum_framesizes(int fd, __u32 pixfmt) {
struct v4l2_frmsizeenum fsize;
memset(&fsize, 0, sizeof(fsize));
fsize.pixel_format = pixfmt;
fsize.index = 0;
while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize) == 0) {
if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf(" %ux%u\n", fsize.discrete.width, fsize.discrete.height);
} else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
printf(" %u-%u x %u-%u (step %ux%u)\n",
fsize.stepwise.min_width, fsize.stepwise.max_width,
fsize.stepwise.min_height, fsize.stepwise.max_height,
fsize.stepwise.step_width, fsize.stepwise.step_height);
break; // 步进/连续类型只需一条记录
}
fsize.index++;
}
}
int main(int argc, char *argv[]) {
const char *dev = (argc > 1) ? argv[1] : "/dev/video0";
int fd = open(dev, O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
struct v4l2_fmtdesc fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.index = 0;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Device: %s\n", dev);
printf("Supported formats:\n");
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
printf(" [%u] 0x%04x %-12s %s\n",
fmt.index, fmt.pixelformat,
(char *)fmt.description, pix_fmt_desc(fmt.pixelformat));
enum_framesizes(fd, fmt.pixelformat);
fmt.index++;
}
close(fd);
return 0;
}
编译与运行
gcc -o enum_fmt enum_fmt.c
./enum_fmt /dev/video0
若摄像头不是/dev/video0,将设备路径作为命令行参数传入即可。
验证方法与判定标准
- 正常输出:程序打印设备路径后,逐行列出每个格式索引、四字符码(如
YUYV)、驱动描述字符串及该格式支持的分辨率列表。 - 判定通过:输出中至少包含一个格式条目,且每个格式条目下能看到至少一个分辨率(离散值或范围)。若输出为空或ioctl返回
EINVAL,说明驱动未正确实现格式枚举,或设备节点无权限(尝试sudo)。 - 交叉验证:可运行
v4l2-ctl --list-formats-ext -d /dev/video0(若已安装v4l-utils)对比输出,两者格式与分辨率集合应一致。
常见问题
- 打开设备失败:确认设备节点存在且当前用户有读写权限(可加入
video组或使用sudo)。 - 无任何格式输出:检查驱动是否加载(
dmesg | grep video),或设备是否被其他进程独占。 - 分辨率枚举不完整:部分驱动对步进类型只返回一条范围记录,本程序已做处理;若驱动实现有误,可能只返回离散值,属正常现象。