4. 枚举视频格式

任务目标

实现一个V4L2视频设备驱动中的格式枚举功能,当用户空间程序调用VIDIOC_ENUM_FMT ioctl时,驱动能够返回所支持的像素格式列表(YUYV、RGB24等),并正确填充索引(index)、描述符(description)和像素格式标识(pixelformat)。

核心实现

在V4L2驱动中,格式枚举通过vidioc_enum_fmt回调实现。该回调接收用户传入的struct v4l2_fmtdesc指针,其中index字段由用户设置,用于指定要查询的格式序号。驱动需要根据该索引返回对应的格式信息,若索引超出范围则返回-EINVAL。

#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>

/* 驱动支持的像素格式表 */
static const struct v4l2_format_info {
    u32 pixelformat;
    const char *description;
} supported_formats[] = {
    { V4L2_PIX_FMT_YUYV,  "YUYV 4:2:2" },
    { V4L2_PIX_FMT_RGB24, "24-bit RGB 8-8-8" },
    { V4L2_PIX_FMT_MJPEG, "Motion-JPEG" },
};

#define NUM_FORMATS ARRAY_SIZE(supported_formats)

static int vidioc_enum_fmt(struct file *file, void *priv,
                           struct v4l2_fmtdesc *f)
{
    if (f->index >= NUM_FORMATS)
        return -EINVAL;

    /* 清除保留位,确保返回结构干净 */
    memset(f->reserved, 0, sizeof(f->reserved));

    /* 设置格式类型(视频捕获) */
    f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    /* 填充像素格式和描述 */
    f->pixelformat = supported_formats[f->index].pixelformat;
    strscpy(f->description, supported_formats[f->index].description,
            sizeof(f->description));

    return 0;
}

/* 在v4l2_ioctl_ops中注册 */
static const struct v4l2_ioctl_ops my_ioctl_ops = {
    .vidioc_enum_fmt = vidioc_enum_fmt,
    /* ... 其他回调 ... */
};

关键说明

  • 索引语义:V4L2规范要求驱动按index从0开始连续返回格式,直到返回-EINVAL为止。用户程序通常循环调用此ioctl来枚举所有格式。
  • 描述符长度:description字段最大32字节(含结尾NUL),使用strscpy确保不越界。
  • 类型匹配:若驱动同时支持视频捕获和输出,需检查f->type是否与当前设备节点匹配,不匹配时返回-EINVAL。本例为简化仅支持捕获。

用户空间验证

编译并运行以下测试程序,观察输出是否符合预期:

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

int main(void)
{
    int fd = open("/dev/video0", O_RDWR);
    if (fd < 0) { perror("open"); return 1; }

    struct v4l2_fmtdesc fmt;
    int index = 0;

    while (1) {
        memset(&fmt, 0, sizeof(fmt));
        fmt.index = index;
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        if (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) < 0)
            break;  /* EINVAL 表示枚举结束 */

        printf("index=%d  pixelformat=0x%08x  desc=%s\n",
               index, fmt.pixelformat, fmt.description);
        index++;
    }

    close(fd);
    return 0;
}

预期输出(与驱动中格式表严格一致):

index=0  pixelformat=0x56595559  desc=YUYV 4:2:2
index=1  pixelformat=0x47524742  desc=24-bit RGB 8-8-8
index=2  pixelformat=0x47504a4d  desc=Motion-JPEG

通过判定标准

  • 测试程序能依次打印出驱动中定义的全部3种格式,且pixelformat值与V4L2定义一致(YUYV=0x56595559,RGB24=0x47524742,MJPEG=0x47504a4d)。
  • 当index超过2时,ioctl返回-1且errno为EINVAL,循环正常终止。
  • 使用v4l2-ctl --list-formats工具验证时,输出格式列表与上述一致。