4. 设置视频格式

任务目标

本案例演示通过V4L2 ioctl调用VIDIOC_S_FMT,将摄像头采集格式设置为YUYV、分辨率640x480,并验证设置是否生效。核心是理解struct v4l2_format的填充方式与驱动返回值的校验逻辑。

环境与依赖

  • Linux系统(含/dev/video0设备节点)
  • 内核已启用V4L2驱动,设备支持YUYV格式
  • 编译依赖:linux/videodev2.h(内核头文件)、libc

关键完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

int main(void) {
    const char *dev = "/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_NONE;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
        perror("VIDIOC_S_FMT");
        close(fd);
        return 1;
    }

    // 验证驱动实际采用的参数
    if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
        fprintf(stderr, "格式不支持: 驱动返回 0x%08x\n", fmt.fmt.pix.pixelformat);
        close(fd);
        return 1;
    }
    if (fmt.fmt.pix.width != 640 || fmt.fmt.pix.height != 480) {
        fprintf(stderr, "分辨率被调整: %ux%u\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
        close(fd);
        return 1;
    }

    printf("设置成功: YUYV 640x480, sizeimage=%u\n", fmt.fmt.pix.sizeimage);
    close(fd);
    return 0;
}

编译与运行

gcc -o set_fmt set_fmt.c
./set_fmt

验证方法与判定标准

  • 成功标志:程序输出设置成功: YUYV 640x480, sizeimage=614400(614400 = 640×480×2字节),退出码为0。
  • 失败排查:若ioctl返回错误,检查设备节点权限(sudo运行)或确认摄像头驱动是否支持YUYV格式(可用v4l2-ctl --list-formats查看)。
  • 驱动调整验证:部分驱动会静默修改分辨率或格式,代码中已加入二次校验,若驱动返回不同值会打印错误并退出非0。

常见问题

  • field字段:设置为V4L2_FIELD_NONE表示逐行扫描,若设备要求隔行需改用V4L2_FIELD_INTERLACED。
  • sizeimage含义:驱动填充的单帧图像字节数,YUYV为每像素2字节,可用于后续mmap缓冲区分配。