3. 枚举并选择视频格式
任务目标
编写一个V4L2用户空间程序,枚举摄像头设备支持的像素格式(如YUYV、MJPEG)及每种格式对应的帧尺寸,并从中选择一种常用格式(YUYV)作为后续采集的配置格式。通过本案例理解struct v4l2_format、struct v4l2_fmtdesc和struct v4l2_frmsizeenum三个核心描述符的结构与使用方式。
环境与依赖
- Linux系统,内核已启用V4L2框架,摄像头设备节点为
/dev/video0(如不同请修改) - 无需额外库,仅使用Linux系统头文件,编译命令:
gcc -o enum_fmt enum_fmt.c
关键完整代码
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
static void print_fourcc(__u32 fourcc) {
printf("%c%c%c%c", (fourcc >> 0) & 0xFF, (fourcc >> 8) & 0xFF,
(fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
}
int main(void) {
int fd = open("/dev/video0", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { perror("QUERYCAP"); return 1; }
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "设备不支持视频采集\n"); return 1;
}
printf("设备: %s\n", cap.card);
/* 1. 枚举像素格式 */
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("\n支持的像素格式:\n");
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
printf(" 索引%u: ", fmtdesc.index);
print_fourcc(fmtdesc.pixelformat);
printf(" (%s)\n", fmtdesc.description);
fmtdesc.index++;
}
/* 2. 枚举YUYV格式的帧尺寸 */
struct v4l2_frmsizeenum frmsize;
memset(&frmsize, 0, sizeof(frmsize));
frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
printf("\nYUYV支持的帧尺寸:\n");
while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) {
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf(" %ux%u\n", frmsize.discrete.width, frmsize.discrete.height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS ||
frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
printf(" 范围: %u-%u x %u-%u\n",
frmsize.stepwise.min_width, frmsize.stepwise.max_width,
frmsize.stepwise.min_height, frmsize.stepwise.max_height);
break; /* 范围型只需打印一次 */
}
frmsize.index++;
}
/* 3. 选择YUYV 640x480并设置 */
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
perror("S_FMT"); return 1;
}
/* 4. 回读确认实际生效的格式 */
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_G_FMT, &fmt) == 0) {
printf("\n已设置格式: ");
print_fourcc(fmt.fmt.pix.pixelformat);
printf(" %ux%u\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
}
close(fd);
return 0;
}
运行与验证
- 编译:
gcc -o enum_fmt enum_fmt.c - 运行:
./enum_fmt(确保当前用户有/dev/video0访问权限,必要时sudo) - 通过条件:程序依次输出设备名称、所有支持的像素格式列表、YUYV格式的帧尺寸列表,最后显示
已设置格式: YUYV 640x480。若摄像头不支持640x480的YUYV,VIDIOC_S_FMT会失败或回读尺寸与请求不同,此时应改为枚举结果中实际存在的尺寸。
核心结构说明
struct v4l2_fmtdesc:用于VIDIOC_ENUM_FMT,其中pixelformat为四字符码(FourCC),description为可读描述。struct v4l2_frmsizeenum:用于VIDIOC_ENUM_FRAMESIZES,需先指定pixel_format。类型为离散时用discrete,连续或步进时用stepwise。struct v4l2_format:用于VIDIOC_S_FMT/VIDIOC_G_FMT,采集场景使用fmt.pix子结构,包含宽、高、像素格式和场设置。
注意:VIDIOC_S_FMT可能因设备限制调整参数(如宽度对齐),因此设置后必须用VIDIOC_G_FMT回读实际值,后续采集需以回读值为准。