【问题标题】:video capturing with V4l2 has dropped/lost frames使用 V4l2 捕获的视频丢帧/丢帧
【发布时间】:2020-04-09 07:59:34
【问题描述】:

我有一个捕获 video4Linux 视频流的科学应用程序。至关重要的是,我们捕捉每一帧并且没有人迷路。不幸的是,到处都缺少框架,我不知道为什么。

为了检测丢帧,我在读取一帧后直接将 v4l2_buffer 的序列号与我自己的计数器进行比较:

void detectDroppedFrame(v4l2_buffer* buffer) {
        _frameCounter++;

        auto isLastFrame = buffer->sequence == 0 && _frameCounter > 1;
        if (!isLastFrame && _frameCounter != buffer->sequence+1)
        {
                std::cout << "\n####### WARNING! Missing frame detected!" << std::endl;               
                _frameCounter = buffer->sequence+1; // re-sync our counter with correct frame number from driver.
        }
}

我的运行 1-file 示例 gist 可以在 github 找到(基于官方 V4L2 捕获示例):https://gist.github.com/SebastianMartens/7d63f8300a0bcf0c7072a674b3ea4817

在笔记本硬件(uvcvideo 驱动程序)上使用 Ubuntu 18.04 虚拟机上的网络摄像头以及在本机运行 ubuntu 18.04 的嵌入式硬件上使用 CSI 摄像头进行测试。帧没有被处理并且缓冲区似乎被抓取得足够快(使用 VIDIOC_QUERYBUF 检查的缓冲区状态显示所有缓冲区都在驱动程序的传入队列中,并且未设置 V4L2_BUF_FLAG_DONE 标志)。我使用 MMAP 和 UserPtr 方法丢失了帧。而且它似乎与像素格式、图像大小和帧速率无关!

在我看来,如果相机/v4l2 驱动程序无法足够快地填充可用缓冲区,而且使用 VIDIOC_S_PRIORITY 命令增加文件描述符优先级也无济于事(仍然可能是线程调度问题?)。

=> V4L2 不转发帧(不将它们放入其传出队列)的可能原因是什么? => 我检测丢失帧的方法是否正确?是否有其他选择或工具?

【问题讨论】:

  • 嗨,我认为您在第 382 行有一个错误。使用 MMAP 时,缓冲区由驱动程序分配,用户空间应使用 mmap 将内存映射到他的内存空间。在您的代码中,缓冲区分配在用户空间而不是 mmap 中。我认为您最好使用 v4l2 开发人员编写的 v4l-utils 包来测试相机
  • 谢谢。但据我了解,ln382 中的“缓冲区”仅保存缓冲区的地址和长度,分配和 mmap 发生在下方(ln 403)。您知道如何利用 v4l2-utils(或任何其他工具)来测试丢失帧吗?
  • 对,我的错。在v4l-utils 中,您可以使用 --verbose 选项进行流式传输,然后它会显示每个缓冲区的序列,或者您可以修补源代码并添加检查
  • 问题在 V4l2 中重现:v4l2-ctl --stream-mmap --stream-count=1000 --verbose 2&gt;&amp;1 &gt;/dev/null | grep Sequence 显示序列号列表。最后的计数将告诉您丢失了多少帧。

标签: c++ v4l2


【解决方案1】:

我在使用 bttv 驱动程序时遇到了类似的问题。所有以全分辨率捕获的尝试都会导致丢帧(通常约为 10% 的帧,通常是突发的)。以半分辨率捕获效果很好。

我发现的解决方案虽然远非理想,但将负载应用到 linux 调度程序。这是一个示例,使用“tvtime”程序进行捕获:

#! /bin/bash
# apply a load in the background
while true; do /bin/true; done &> /dev/null &
# do the video capture
/usr/bin/tvtime "$@"
# kill the loop running in the background
pkill -P $$

这会创建一个在后台重复运行 /bin/true 的循环。几乎任何可执行文件都可以(我最初使用 /bin/date)。循环会产生沉重的负载,但对于多核系统,仍有足够的空间用于其他任务。

这显然不是一个理想的解决方案,但就其价值而言,它使我能够捕捉全分辨率视频而不会丢帧。我不想在驱动程序/内核代码中四处寻找更好的解决方案。

仅供参考,这是我系统的详细信息:

OS:  Ubuntu 20.04, kernel 5.4.0-42
MB:  Gigabyte AB350M-D3H
CPU: AMD Ryzen 5 2400G
GPU: AMD Raven Ridge

Driver name      : bttv
Card type        : BT878 video (Hauppauge (bt878))
Bus info         : PCI:0000:06:00.0
Driver version   : 5.4.44
Capabilities     : 0x85250015

【讨论】:

  • 酷谢谢,我会试试看。您知道为什么调度程序负载似乎有积极影响吗?
  • 我对调度器的了解还不够,甚至无法猜测。
猜你喜欢
  • 1970-01-01
  • 2020-04-20
  • 1970-01-01
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
  • 2020-01-30
相关资源
最近更新 更多