2. 查询Camera能力集

任务说明

本案例实现一个Linux用户空间程序,通过V4L2接口的ioctl系统调用查询摄像头设备的能力集,包括支持的像素格式、分辨率范围及当前格式信息,并将结果打印输出。程序直接操作/dev/video0设备节点,适用于嵌入式Linux或标准Linux系统中接入的UVC/CSI摄像头。

核心实现

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

static void print_fourcc(__u32 fourcc) {
    char code[5] = {
        (char)(fourcc & 0xFF),
        (char)((fourcc >> 8) & 0xFF),
        (char)((fourcc >> 16) & 0xFF),
        (char)((fourcc >> 24) & 0xFF),
        '\0'
    };
    printf("%s", code);
}

static void query_capability(int fd) {
    struct v4l2_capability cap;
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
        perror("VIDIOC_QUERYCAP");
        return;
    }
    printf("驱动: %s\n", cap.driver);
    printf("设备: %s\n", cap.card);
    printf("总线: %s\n", cap.bus_info);
    printf("版本: %u.%u.%u\n",
           (cap.version >> 16) & 0xFF,
           (cap.version >> 8) & 0xFF,
           cap.version & 0xFF);
    printf("能力标志: 0x%08X\n", cap.capabilities);
}

static void query_formats(int fd) {
    struct v4l2_fmtdesc fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    printf("\n支持的像素格式:\n");
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
        printf("  索引 %d: ", fmt.index);
        print_fourcc(fmt.pixelformat);
        printf(" - %s\n", fmt.description);
        fmt.index++;
    }
}

static void query_frame_sizes(int fd, __u32 pixfmt) {
    struct v4l2_frmsizeenum frmsize;
    memset(&frmsize, 0, sizeof(frmsize));
    frmsize.pixel_format = pixfmt;

    printf("  分辨率:\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_STEPWISE ||
                   frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
            printf("    范围: %u-%u x %u-%u (步进 %u x %u)\n",
                   frmsize.stepwise.min_width,
                   frmsize.stepwise.max_width,
                   frmsize.stepwise.min_height,
                   frmsize.stepwise.max_height,
                   frmsize.stepwise.step_width,
                   frmsize.stepwise.step_height);
        }
        frmsize.index++;
    }
}

static void query_current_format(int fd) {
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
        perror("VIDIOC_G_FMT");
        return;
    }
    printf("\n当前格式:\n");
    printf("  像素格式: ");
    print_fourcc(fmt.fmt.pix.pixelformat);
    printf("\n");
    printf("  分辨率: %ux%u\n",
           fmt.fmt.pix.width,
           fmt.fmt.pix.height);
    printf("  行字节数: %u\n", fmt.fmt.pix.bytesperline);
    printf("  图像大小: %u\n", fmt.fmt.pix.sizeimage);
}

int main(int argc, char *argv[]) {
    const char *dev_path = "/dev/video0";
    if (argc > 1) {
        dev_path = argv[1];
    }

    int fd = open(dev_path, O_RDWR);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    query_capability(fd);
    query_formats(fd);

    struct v4l2_fmtdesc fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
        printf("\n格式: ");
        print_fourcc(fmt.pixelformat);
        printf("\n");
        query_frame_sizes(fd, fmt.pixelformat);
        fmt.index++;
    }

    query_current_format(fd);
    close(fd);
    return 0;
}

编译与运行

# 编译
gcc -o query_camera query_camera.c

# 运行(默认查询 /dev/video0)
./query_camera

# 指定其他设备节点
./query_camera /dev/video1

验证方法

在目标板上连接摄像头并确认设备节点存在后,执行编译好的程序。预期输出应包含以下内容:

  • 设备信息段:以驱动:、设备:、总线:开头的三行,分别显示摄像头驱动名称、设备名称和总线信息;
  • 格式枚举段:列出所有支持的像素格式,每行包含格式索引、四字符编码(如YUYV、MJPG、H264)和描述文字;
  • 分辨率枚举段:对每种格式列出支持的分辨率,离散型显示为宽x高,步进型显示为范围及步进值;
  • 当前格式段:以当前格式:开头,显示当前生效的像素格式、分辨率、行字节数和图像大小。

判定标准:程序能完整打印上述四类信息且无VIDIOC_*错误输出,即表示ioctl查询能力集功能正常。若某个ioctl调用失败,程序会通过perror打印具体错误原因,可据此排查设备权限或驱动兼容性问题。