SweerItTer 2025-01-19 21:41 采纳率: 0%
浏览 96

v4l2 在运行到 VIDIOC_DQBUF 时卡住

问题遇到的现象和发生背景

v4l2 在运行到 VIDIOC_DQBUF 时卡住
这个情况只出现咋pixformat为YUYV的时候,使用MJPG就没有这个问题

遇到的现象和发生背景,请写出第一个错误信息

需要提一嘴的是,我用的WSL(虚拟机软件我带不动)

用代码块功能插入代码,请勿粘贴截图。 不用代码块回答率下降 50%
#define BUFCOUNT 4

int Vvideo::initBuffers() {
    struct v4l2_requestbuffers req;
    std::memset(&req, 0, sizeof(req));
    req.count = BUFCOUNT;
    req.type = type;
    req.memory = V4L2_MEMORY_MMAP;

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

    for (int num = 0; num < BUFCOUNT; num++) {
        std::memset(&buffer, 0, sizeof(buffer));
        buffer.type = type;
        buffer.memory = V4L2_MEMORY_MMAP;
        buffer.index = num;

        if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) == -1) {
            perror("Failed to query buffer");
            goto cleanup;
        }

        framebuf[num].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset);
        if (framebuf[num].start == MAP_FAILED) {
            perror("Failed to map buffer");
            goto cleanup;
        }
        framebuf[num].length = buffer.length;

        if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) {
            perror("Failed to queue buffer");
            goto cleanup;
        }
    }

    if (ioctl(fd, VIDIOC_STREAMON, &buffer.type) == -1) {
        perror("Failed to start streaming");
        goto cleanup;
    }

    return 0;

cleanup:
    for (int i = 0; i < BUFCOUNT; i++) {
        if (framebuf[i].start && framebuf[i].start != MAP_FAILED) {
            munmap(framebuf[i].start, framebuf[i].length);
        }
    }
    close(fd);
    return -1;
}

QImage Vvideo::captureFrame() {
    QMutexLocker locker(&mutex); // 加锁

    memset(&buffer, 0, sizeof(buffer));
    buffer.type = type;
    buffer.memory = V4L2_MEMORY_MMAP;

    struct timeval tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;

    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(fd, &fds);

    int r = select(fd + 1, &fds, NULL, NULL, &tv);
    if (r == -1) {
        perror("select");
        return QImage();
    } else if (r == 0) {
        qDebug() << "Timeout waiting for buffer";
        return QImage();
    }

    if (ioctl(fd, VIDIOC_DQBUF, &buffer) == -1) {
        perror("Failed to dequeue buffer");
        return QImage();
    }

    QImage image_;
    if (fmt == V4L2_PIX_FMT_MJPEG || fmt == V4L2_PIX_FMT_JPEG) {
        MJPG2RGB(image_, framebuf[buffer.index].start, framebuf[buffer.index].length);
    } else if (fmt == V4L2_PIX_FMT_YUYV) {
        YUYV2RGB(image_, framebuf[buffer.index].start, framebuf[buffer.index].length);
    } else {
        qDebug() << "Unsupported format";
    }

    if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) {
        perror("Failed to queue buffer");
    }
    return image_;
}
运行结果及详细报错内容

Timeout waiting for buffer
Capture frame failed!
其实没有报错信息,就是会卡住

我的解答思路和尝试过的方法,不写自己思路的,回答率下降 60%

看来许多博客,提到的有扩充缓冲区(我现在用的4个),有说设置超时的(select已经加上了,2秒还是超时)

我想要达到的结果,如果你需要快速回答,请尝试 “付费悬赏”

只需要拿到数据就行

  • 写回答

2条回答 默认 最新

  • 关注

    以下回复参考:皆我百晓生券券喵儿等免费微信小程序作答:

    从你给出的代码和描述来看,这个问题可能由几个原因导致。下面是一些建议和排查方法:

    问题原因:

    1. YUYV 格式的问题:如果只有在使用 V4L2_PIX_FMT_YUYV 格式时出现问题,可能是这个格式的处理方式不同导致的。你需要检查 YUYV2RGB 函数是否正确实现了 YUV 到 RGB 的转换。此外,对于 YUV 格式,还需要注意帧的顺序和大小端问题。
    2. 缓冲区管理问题:虽然你设置了缓冲区数量为 4,但在某些情况下可能还需要更多的缓冲区。可以尝试增加缓冲区数量来解决问题。此外,确保在使用缓冲区之前正确初始化它们。
    3. select 的使用问题:你的代码中使用了 select 来等待缓冲区数据,但在 select 返回后立刻调用了 ioctl(fd, VIDIOC_DQBUF, &buffer)。在某些情况下,可能还需要检查其他的文件描述符状态或进行其他处理。确保你的 select 使用方式是正确的。
    4. WSL 的限制问题:你提到了在使用 WSL(Windows Subsystem for Linux)时遇到了问题。WSL 可能在某些方面对 Linux 的 API 支持不完全或存在性能问题,这可能导致视频捕获出现问题。你可以尝试在没有 WSL 的环境中运行程序,看看问题是否仍然存在。

    排查方法:

    1. 检查日志和错误信息:查看内核日志或应用程序日志,看是否有更详细的错误信息。这可能会提供关于问题的更多线索。
    2. 调试代码:在卡住的地方添加更多的调试输出和检查点,逐步跟踪代码的执行过程,看看问题出现在哪里。
    3. 测试其他环境:在没有 WSL 的环境中测试程序,看看问题是否仍然存在。如果问题解决了,那么可能是 WSL 的问题。
    4. 简化代码:尝试简化代码,只保留最基本的视频捕获功能,然后逐步添加其他功能,每次添加后都进行测试,看看问题是否再次出现。这样可以帮助定位问题的具体位置。

    希望这些建议能帮助你解决问题!如果还有其他问题或需要进一步的帮助,请告诉我。

    评论

报告相同问题?

问题事件

  • 创建了问题 1月19日