1. V4L2框架初探

V4L2框架初探:枚举摄像头设备 学习卡片

任务目标

编写一个Linux用户空间程序,通过V4L2框架枚举系统中所有摄像头设备节点(/dev/video*),并打印每个设备对应的驱动名称、设备名称、总线信息及设备能力,从而理解V4L2设备节点与内核驱动的对应关系。

环境与依赖

  • Linux系统(内核已启用V4L2框架,存在摄像头设备节点)
  • 内核头文件:linux/videodev2.h(通常随libc-dev或linux-libc-dev包提供)
  • 编译器:gcc
  • 无需额外第三方库,仅使用系统调用open/ioctl/close

核心实现原理

V4L2设备节点位于/dev/video*。程序遍历/dev目录下所有以video开头的设备文件,依次打开并调用以下ioctl查询设备信息:

  • VIDIOC_QUERYCAP:获取struct v4l2_capability,包含驱动名(driver)、设备名(card)、总线信息(bus_info)及能力标志(device_caps)。
  • 通过device_caps & V4L2_CAP_VIDEO_CAPTURE判断是否为视频采集设备(摄像头)。

关键完整代码

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

static void print_device_info(const char *path) {
    int fd = open(path, O_RDWR);
    if (fd < 0) {
        perror("open");
        return;
    }

    struct v4l2_capability cap;
    memset(&cap, 0, sizeof(cap));
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP");
        close(fd);
        return;
    }

    printf("设备节点: %s\n", path);
    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);

    if (cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)
        printf("  能力: 视频采集\n");
    if (cap.device_caps & V4L2_CAP_STREAMING)
        printf("  能力: 流式I/O\n");
    printf("\n");

    close(fd);
}

int main(void) {
    DIR *dir = opendir("/dev");
    if (!dir) {
        perror("opendir /dev");
        return 1;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strncmp(entry->d_name, "video", 5) == 0) {
            char path[256];
            snprintf(path, sizeof(path), "/dev/%s", entry->d_name);
            print_device_info(path);
        }
    }

    closedir(dir);
    return 0;
}

编译与运行

gcc -o v4l2_enum v4l2_enum.c
sudo ./v4l2_enum   # 部分设备节点需要root权限才能打开

验证方法

运行程序后,观察输出:

  • 若系统存在摄像头(如USB摄像头或笔记本内置摄像头),应能看到对应的/dev/video*节点及其驱动、设备、总线信息。
  • 使用ls /dev/video*对比程序输出的设备节点是否与系统实际节点一致。
  • 使用v4l2-ctl --list-devices(若已安装v4l-utils)交叉验证驱动名称和设备名称是否匹配。

通过条件:程序能列出所有/dev/video*节点,且每个节点的驱动名称、设备名称、总线信息与内核实际绑定信息一致(可通过cat /sys/class/video4linux/video*/name核对设备名称)。

常见问题

  • 打开设备失败:确认当前用户是否有权限访问/dev/video*,必要时使用sudo运行,或将用户加入video组。
  • ioctl返回错误:某些/dev/video*节点可能不是摄像头(如电视调谐器、视频输出设备),此时VIDIOC_QUERYCAP仍可成功,但device_caps中不会包含V4L2_CAP_VIDEO_CAPTURE标志。本程序会打印所有video节点信息,如需仅显示摄像头,可在打印前增加该标志判断。