4. 枚举帧尺寸
任务目标
针对指定像素格式(如 V4L2_PIX_FMT_YUYV),通过 VIDIOC_ENUM_FRAMESIZES ioctl 枚举摄像头支持的帧尺寸。需要区分两种类型:离散尺寸(固定分辨率列表)和连续范围(最小/最大宽高及步进),并完整打印所有可能的分辨率。
核心实现
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
static void print_discrete(const struct v4l2_frmsizeenum *fsize) {
printf(" 离散尺寸: %ux%u\n",
fsize->discrete.width, fsize->discrete.height);
}
static void print_stepwise(const struct v4l2_frmsizeenum *fsize) {
const struct v4l2_frmsize_stepwise *sw = &fsize->stepwise;
printf(" 连续范围: 宽 %u~%u (步进 %u), 高 %u~%u (步进 %u)\n",
sw->min_width, sw->max_width, sw->step_width,
sw->min_height, sw->max_height, sw->step_height);
printf(" 可枚举分辨率:\n");
unsigned int w, h;
for (w = sw->min_width; w <= sw->max_width; w += sw->step_width)
for (h = sw->min_height; h <= sw->max_height; h += sw->step_height)
printf(" %ux%u\n", w, h);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "用法: %s <设备节点> <像素格式四字符码>\n", argv[0]);
fprintf(stderr, "示例: %s /dev/video0 YUYV\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
// 将四字符码(如 "YUYV")转为 V4L2 像素格式值
char fourcc[5] = {0};
strncpy(fourcc, argv[2], 4);
__u32 pixfmt = v4l2_fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
struct v4l2_frmsizeenum fsize;
memset(&fsize, 0, sizeof(fsize));
fsize.pixel_format = pixfmt;
printf("像素格式 %s 支持的帧尺寸:\n", argv[2]);
int count = 0;
for (fsize.index = 0; ; fsize.index++) {
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize) < 0) {
if (errno == EINVAL)
break; // 枚举结束
perror("VIDIOC_ENUM_FRAMESIZES");
close(fd);
return 1;
}
count++;
printf("索引 %u, 类型 %s:\n", fsize.index,
fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE ? "离散" :
fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE ? "连续" :
fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS ? "连续(步进1)" : "未知");
if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
print_discrete(&fsize);
else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)
print_stepwise(&fsize);
}
if (count == 0)
printf(" (无任何帧尺寸信息)\n");
close(fd);
return 0;
}
编译与运行
# 编译(需安装 libv4l-dev 或仅需内核头文件)
gcc -o enum_framesize enum_framesize.c
# 运行:枚举 /dev/video0 在 YUYV 格式下的所有帧尺寸
./enum_framesize /dev/video0 YUYV
验证方法
- 正常输出:程序打印至少一个索引条目,且每个条目包含类型(离散/连续)及对应的分辨率信息。
- 离散类型:应看到类似
离散尺寸: 640x480的输出,每个索引对应一个固定分辨率。 - 连续类型:应看到最小/最大宽高及步进值,随后列出按步进展开的所有分辨率组合。若类型为
CONTINUOUS,步进值应为 1。 - 结束条件:当 ioctl 返回
EINVAL时枚举结束,程序正常退出,退出码为 0。 - 交叉验证:可使用
v4l2-ctl --list-formats-ext -d /dev/video0对比输出,两者应一致(若设备支持该工具)。
常见问题
- 枚举为空:确认设备节点存在且当前像素格式确实被支持。可先用
VIDIOC_ENUM_FMT确认格式有效性。 - 步进值过大导致打印过多:连续范围若步进为 1 且范围很大(如 16~1920),打印行数可能较多,属正常现象;如需限制可自行修改循环条件。
- 类型为 CONTINUOUS:内核将其视为步进为 1 的 STEPWISE,代码中已统一处理。