4. 设置采集分辨率与像素格式
任务说明
本案例目标是通过V4L2的VIDIOC_S_FMT ioctl,将摄像头采集分辨率设置为640x480、像素格式设置为YUYV,然后使用VIDIOC_G_FMT读取驱动实际生效的格式,验证驱动是否正确接受或调整了参数。该操作是任何V4L2采集应用的第一步——只有格式协商成功,后续的缓冲区申请和帧采集才有意义。
核心实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
static int xioctl(int fd, unsigned long req, void *arg)
{
int ret;
do {
ret = ioctl(fd, req, arg);
} while (ret == -1 && errno == EINTR);
return ret;
}
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_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_ANY;
if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("VIDIOC_S_FMT");
close(fd);
return 1;
}
printf("S_FMT 返回: width=%u height=%u format=%c%c%c%c\n",
fmt.fmt.pix.width, fmt.fmt.pix.height,
(fmt.fmt.pix.pixelformat >> 0) & 0xff,
(fmt.fmt.pix.pixelformat >> 8) & 0xff,
(fmt.fmt.pix.pixelformat >> 16) & 0xff,
(fmt.fmt.pix.pixelformat >> 24) & 0xff);
/* 重新读取当前实际生效的格式 */
struct v4l2_format cur;
memset(&cur, 0, sizeof(cur));
cur.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(fd, VIDIOC_G_FMT, &cur) == -1) {
perror("VIDIOC_G_FMT");
close(fd);
return 1;
}
printf("G_FMT 读取: width=%u height=%u format=%c%c%c%c\n",
cur.fmt.pix.width, cur.fmt.pix.height,
(cur.fmt.pix.pixelformat >> 0) & 0xff,
(cur.fmt.pix.pixelformat >> 8) & 0xff,
(cur.fmt.pix.pixelformat >> 16) & 0xff,
(cur.fmt.pix.pixelformat >> 24) & 0xff);
if (cur.fmt.pix.width == 640 && cur.fmt.pix.height == 480 &&
cur.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
printf("验证通过: 驱动接受640x480 YUYV\n");
} else {
printf("验证结果: 驱动调整了参数(实际为%ux%u)\n",
cur.fmt.pix.width, cur.fmt.pix.height);
}
close(fd);
return 0;
}
编译与运行
# 编译(需要linux/videodev2.h头文件,通常由libv4l-dev或内核头文件提供)
gcc -o set_fmt set_fmt.c
# 运行(默认打开/dev/video0,也可指定其他设备节点)
./set_fmt /dev/video0
验证方法
程序执行后观察输出:
- S_FMT返回:显示驱动对请求参数的初步响应,通常与请求一致,但部分驱动会静默调整(如对齐到step、改变格式)。
- G_FMT读取:这是驱动最终生效的格式,是验证的唯一依据。
- 判定标准:若G_FMT读回640x480且格式为YUYV,说明驱动完全接受请求;若读回其他值,说明驱动不支持请求参数并做了调整,此时应检查驱动支持的格式列表(可通过
VIDIOC_ENUM_FMT和VIDIOC_ENUM_FRAMESIZES查询)。
常见问题
- 打开设备失败:确认设备节点存在且有读写权限(
sudo或加入video组)。 - ioctl返回EINVAL:设备不支持该类型缓冲区(如某些设备只支持
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),需改用multi-planar API。 - 驱动调整分辨率:部分USB摄像头仅支持固定分辨率列表,S_FMT会就近选择。此时应使用
VIDIOC_ENUM_FRAMESIZES查询可用尺寸,选择驱动支持的值。