使用v4l2在c++中捕获图片,解释过程

mcf*_*nfs 5 c++ image-capture v4l2

我一直在努力编写 C++ 代码来从网络摄像头捕获图片。我成功地做到了,但我想对我所采取的过程进行一些澄清。

所以我的代码可以用6个步骤来描述,我将它们和我的问题一起写在这里: 一、步骤:初始化设备并设置图像格式。

    const char* dev_name = "/dev/video0";
    int width=320;
    int height=240;
    int fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
    struct v4l2_format format = {0};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = width;
    format.fmt.pix.height = height;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUYV //V4L2_PIX_FMT_RGB24
    format.fmt.pix.field = V4L2_FIELD_NONE; //V4L2_FIELD_NONE
    xioctl(fd, VIDIOC_S_FMT, &format);
Run Code Online (Sandbox Code Playgroud)

二. 步骤:请求缓冲区。

    struct v4l2_requestbuffers req = {0};
    req.count = 2;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    xioctl(fd, VIDIOC_REQBUFS, &req);
Run Code Online (Sandbox Code Playgroud)
  1. 问题:这个“请求缓冲区”到底是做什么的?
  2. 问题:这个缓冲区实际上位于相机内部还是我的电脑上?

三.步骤:查询缓冲区和映射。

    struct v4l2_buffer buf;
    buffer* buffers;
    unsigned int i;
    buffers = (buffer*) calloc(req.count, sizeof(*buffers));
    for (i = 0; i < req.count; i++) {
    clear_memmory(&(buf));

    (buf).type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    (buf).memory      = V4L2_MEMORY_MMAP;
    (buf).index       = i;

    xioctl(fd, VIDIOC_QUERYBUF, &buf);

    buffers[i].length = (buf).length;
    printf("A buff has a len of: %i\n",buffers[i].length);
    buffers[i].start = v4l2_mmap(NULL, (buf).length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, (buf).m.offset);
    
    if (MAP_FAILED == buffers[i].start) {
        perror("Can not map the buffers.");
        exit(EXIT_FAILURE);
        }
    }
Run Code Online (Sandbox Code Playgroud)
  1. 问题:我知道我在这里做了一些映射,但是有人可以解释为什么需要这样做以及到底映射了什么。对我来说,听起来实际的缓冲区在其他地方,然后我将它映射到我自己的缓冲区中,这样我就可以从缓冲区中读取内容。这是正确的吗?实际的缓冲区在哪里?

  2. 问题:我也可以避免映射吗?

四.步骤:开始串流。对缓冲区进行排队。

    for (i = 0; i < 1; i++) {
        clear_memmory(&(buf));
        (buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        (buf).memory = V4L2_MEMORY_MMAP;
        (buf).index = i;
        xioctl(fd,VIDIOC_QBUF, &(buf));
        }
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    xioctl(fd,VIDIOC_STREAMON, &type);
Run Code Online (Sandbox Code Playgroud)
  1. 问题:VIDIOC_QBUF 的作用是什么?请在这里非常精确,并主要使用儿童词汇来回答这个问题,因为这非常令人困惑。
  2. 问题:VIDIOC_QBUF 应该在 VIDIOC_STREAMON 之后发生吗?有关系吗?

V. 步骤:将缓冲区出队并保存一帧。

    do {
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        // Timeout.
        tv.tv_sec = 2;
        tv.tv_usec = 0;

        r = select(fd + 1, &fds, NULL, NULL, &tv);
        } while ((r == -1 && (errno = EINTR)));
    if (r == -1) {
        perror("select");
        exit(EXIT_FAILURE);
        }

    clear_memmory(&(buf));
    (buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    (buf).memory = V4L2_MEMORY_MMAP;
    xioctl(fd,VIDIOC_DQBUF, &(buf));
    
    printf("Buff index: %i\n",(buf).index);
    sprintf(out_name, "image%03d.ppm",pic_count);
    fout = fopen(out_name, "w");
    if (!fout) {
        perror("Cannot open image");
        exit(EXIT_FAILURE);
        }
    fwrite(buffers[(buf).index].start, (buf).bytesused, 1, fout);
    fclose(fout);
    pic_count++;
Run Code Online (Sandbox Code Playgroud)
  1. 问题:为什么需要VIDIOC_DQBUF,它有什么作用?
  2. 问题:如果我想捕获 3 帧,并且如果我只有 1 个缓冲区用于 1 张图片,我是否需要排队、出队 3 次?
  3. 问题:如果我想要 1000000 张图片,我可以使缓冲区足够大以容纳所有图片吗?是什么限制了该缓冲区的大小?相机上有那个缓冲区吗?这意味着所有这些照片都会放在里面吗?
  4. 问题:如果我想要在我选择的时刻拍摄 10 张照片,并且如果我选择足够大的缓冲区来容纳 10 张照片。当我想拍照的时候我应该叫什么?只有VIDIOC_QBUF?VIDIOC_QBUF和VIDIOC_DQBUF?,只有VIDIOC_DQBUF?

请不要将我指向https://01.org/linuxgraphics/gfx-docs/drm/media/uapi/v4l/vidioc-qbuf.html或其他网站,因为我已经阅读了我能找到的所有内容,并且我我仍然有上述不清楚之处。我真的想要关于这些问题的详细解释。我预先感谢您提供的所有有用的答案。