Linux&UVC驱动
+ -

LINUX打开UVC相机代码

2024-03-28 19 0
  • 打开 /dev/video0 设备。
  • 查询设备的基本能力,并打印驱动程序、设备名称等信息。
  • 设置视频格式为 640x480 分辨率,YUYV 格式,交错扫描。
  • 请求 4 个内存缓冲区用于视频数据。
  • 将这些缓冲区映射到用户空间。
  • 将缓冲区放入视频队列。
  • 开始数据流。
  • 在需要时,可以添加视频处理或保存的代码。
  • 停止数据流。
  • 解除缓冲区的映射。
  • 关闭设备。

在实际应用中,你可能需要根据相机的具体规格和要求调整这些设置,比如分辨率、像素格式、缓冲区数量等。此外,你还可以添加更多的 V4L2 ioctl 请求来配置其他功能,如设置帧率、曝光、白平衡等。

确保在使用 mmap() 映射内存时小心处理错误和释放资源,以避免内存泄漏或其他问题。

另外,代码中大量使用的是ioctl函数,其不同的控制码对应不同的功能。这些功能码在UVC驱动中对应的回调函数详见:UVC驱动向V4L2提供的回调接口v4l2_ioctl_ops https://www.usbzh.com/article/detail-1343.html

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

#define CAMERA_DEVICE "/dev/video0"
#define OUTPUT_FILE "output.raw"

int main() {
    int fd;
    struct v4l2_capability cap;
    struct v4l2_fmtdesc fmt_desc;
    struct v4l2_frmsizeenum frmsize;
    struct v4l2_format fmt;
    struct v4l2_requestbuffers reqbuf;
    enum v4l2_buf_type type;

    // 打开设备
    fd = open(CAMERA_DEVICE, O_RDWR);
    if (fd == -1) {
        perror("Failed to open device");
        return EXIT_FAILURE;
    }

    // 查询设备能力
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
        perror("Failed to query device capabilities");
        close(fd);
        return EXIT_FAILURE;
    }

    printf("Driver: %s\n", cap.driver);
    printf("Card: %s\n", cap.card);
    printf("Bus: %s\n", cap.bus_info);

    // 打印支持的格式和分辨率
    printf("Supported formats:\n");
    fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    for (fmt_desc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmt_desc) == 0; fmt_desc.index++) {
        printf("- Format: %s\n", fmt_desc.description);

        // 获取该格式支持的分辨率
        frmsize.pixel_format = fmt_desc.pixelformat;
        for (frmsize.index = 0; ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0; frmsize.index++) {
            if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
                printf("  Resolution: %dx%d\n", frmsize.discrete.width, frmsize.discrete.height);
            }
        }
    }

    // 设置视频格式(这里选择第一个支持的格式和分辨率)
    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; // 这里选择 YUYV 格式
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        perror("Failed to set video format");
        close(fd);
        return EXIT_FAILURE;
    }

    // 请求内存缓冲区
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    reqbuf.count = 4;

    if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
        perror("Failed to request buffers");
        close(fd);
        return EXIT_FAILURE;
    }

    // 将缓冲区映射到用户空间
    struct v4l2_buffer buf;
    void *buffer_start[reqbuf.count];
    for (unsigned int i = 0; i < reqbuf.count; i++) {
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
            perror("Failed to query buffer");
            close(fd);
            return EXIT_FAILURE;
        }

        buffer_start[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
        if (buffer_start[i] == MAP_FAILED) {
            perror("Failed to map buffer");
            close(fd);
            return EXIT_FAILURE;
        }
    }

    // 将缓冲区放入队列
    for (unsigned int i = 0; i < reqbuf.count; i++) {
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            perror("Failed to queue buffer");
            close(fd);
            return EXIT_FAILURE;
        }
    }

    // 开始数据流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        perror("Failed to start streaming");
        close(fd);
        return EXIT_FAILURE;
    }

    // 打开输出文件
    FILE *output_file = fopen(OUTPUT_FILE, "wb");
    if (!output_file) {
        perror("Failed to open output file");
        close(fd);
        return EXIT_FAILURE;
    }

    // 获取并存储视频数据
    for (unsigned int i = 0; i < 100; i++) { // 为简化示例,这里仅获取 100 帧数据
        // 从队列中取出缓冲区
        if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
            perror("Failed to dequeue buffer");
            close(fd);
            fclose(output_file);
            return EXIT_FAILURE;
        }

        // 写入数据到文件
        fwrite(buffer_start[buf.index], buf.length, 1, output_file);

        // 将缓冲区重新放入队列
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            perror("Failed to queue buffer");
            close(fd);
            fclose(output_file);
            return EXIT_FAILURE;
        }
    }

    // 停止数据流
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
        perror("Failed to stop streaming");
        close(fd);
        fclose(output_file);
        return EXIT_FAILURE;
    }

    // 关闭输出文件
    fclose(output_file);

    // 解除映射
    for (unsigned int i = 0; i < reqbuf.count; i++) {
        if (munmap(buffer_start[i], buf.length) == -1) {
            perror("Failed to unmap buffer");
            close(fd);
            return EXIT_FAILURE;
        }
    }

    // 关闭设备
    close(fd);

    return EXIT_SUCCESS;
}

0 篇笔记 写笔记

UVC修改相机分辨率帧率
熟悉UVC规范的都知道,UVC摄像头或相机可以通过UVC视频流接口支持多种分辨率和格式,但是一般都会有一个默认的视频数据格式和分辨率。如本人手中的一个UVC摄像头,可以看到其支持多种格式和分辨率,我们可以通过potplayer来查看。可以看到,其支持MJPG,YUY2两种各式和多种的分辨率。当然,这......
UVC相机拓扑结构配置要点总结
UVC相机开发的人经常是需要编写UVC相机配置描述符的,这个USB配置描述符包括两部分的内容,分别为视频控制接口部分和视频流接口部分。视频控制部分包括的内容主要描述了UVC相机的拓扑结构。一般情况下的相机拓扑结构是这样子的:不过有的时候,我们也需要扩展单元,所以加上扩展单元描述符就如下:这里......
UVC相机配置描述符解析
UVC相机开发的人经常是需要编写UVC相机配置描述符的,这个USB配置描述符包括两部分的内容,分别为视频控制接口部分和视频流接口部分。视频控制部分包括的内容主要描述了UVC相机的拓扑结构。视频流接口部分的内容主要描述符UVC相机支持的视频格式、分辨率及相关的数据流端点。一般情况下,我们编写UV......
UVC相机处理单元亮度、对比度、色调、饱合度和锐度抓包分析实践
UVC的拓扑结构中,有一个叫做处理单,其主要功能是设置或读取视频函数的处理单元内的视频控件的属性。这些属性主要是相机图显上的参数配置。根据UVC规范,相机处理单元支持的功能通过其UVC相机处理单元描述符bmControls字段的位域掩码来表示,其对应的值为1表示支持,为0表示不支持。具体支持的各......
LINUX&UVC相机打开时的带宽选择
UVC规范明确指出,传输UVC图像数据的端点既可以是同步传输,也可以是批量传输。批量传输对于批量传输,其实根本不存在带宽选择的问题的。只要是批量传输,端点都是可以使能工作的,但是数据传输的效能是根据USB总线上的带宽利用率而决定的。因为USB批量占用的是周期性传输和控制传输剩余的带宽。USB带......
LINUX打开UVC相机代码
打开 /dev/video0 设备。查询设备的基本能力,并打印驱动程序、设备名称等信息。设置视频格式为 640x480 分辨率,YUYV 格式,交错扫描。请求 4 个内存缓冲区用于视频数据。将这些缓冲区映射到用户空间。将缓冲区放入视频队列。开始数据流。在需要时,可以添加视频处理或保存的代码。停止数......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!