【问题标题】:Dropping video frames丢弃视频帧
【发布时间】:2010-11-19 09:45:49
【问题描述】:

我想我的问题很简单。它更多的是关于数学而不是编码。 我每秒从网络摄像头接收 15 帧。但我必须放弃其中的一些。例如,如果客户端请求 8 fps - 我确实每秒丢弃一帧,这没关系。但是,如果请求 12 fps 或 6 fps,我应该如何丢弃?

我认为有一些常见的价值分配算法。非常感谢!

【问题讨论】:

    标签: c++ algorithm frames


    【解决方案1】:

    我看到的方式(例如在 gstreamer 中)是有两个异步循环,一个从相机接收帧,一个将帧发送到客户端。后一个循环仅具有可用的相机最近捕获的帧,因此客户端未“拾取”的帧将被自动丢弃,因为它们被下一个传入帧覆盖。

    您需要进行一些同步以确保您不会读取一半捕获的帧,但除此之外,这是一个直接的解决方案,并允许交换的两侧在任一方向上以不同的速度运行。

    【讨论】:

    • 我不明白为什么每个人都投票赞成这个答案?说循环 - 你的意思是线程?我认为为如此简单的任务添加线程并不好。它可以在一个且唯一的线程中处理。
    【解决方案2】:

    基本上,您需要深入了解 FPS 概念。

    X 的 FPS 意味着每 1/X 秒显示一帧,并在显示器上保持可见 1/X 秒。通常,这些帧会在显示设备的每次垂直同步时在显示器上更新。

    现在有了这些信息,可以将帧速率转换可视化为解码器解码以临时显示在输入 FPS 和采样器在输出 FPS 的最终显示上显示帧。采样器从解码器正在更新帧的临时显示器中采样帧。此逻辑应确保解码器和采样器之间的同步,以便采样器最终不会对部分更新的帧进行采样。

    此逻辑适用于降低或提高 FPS。

    这可以实现为具有同步访问的单个缓冲区。现在让解码器以输入 FPS 写入该缓冲区,采样器从该缓冲区中挑选帧并以输出 fps 显示。

    这将确保屏幕更新一致,并以垂直同步显示最新的可用帧。

    【讨论】:

    • 问题不在于屏幕
    【解决方案3】:

    通过每 2 帧丢弃 8 fps,如果您有 15 fps,则会引入错误。

    这是一个非常简单的算法,适用于所有情况:

    #include <iostream>
    
    double t = 0.0;
    
    const double fps1 = 15.0;
    const double fps2 = 12.0;
    
    const double t1 = 1.0 / fps1;
    const double t2 = 1.0 / fps2;
    
    // true - drop the frame
    // false - do NOT drop the frame
    bool NextTick()
    {
      t += t1;
      if ( t > t2 )
      {
        t -= t2;
        return false;
      }
      return true;
    }
    
    int main()
    {
      for ( unsigned int i =0;i<20;++i)
      {
          if ( NextTick() )
          {
              std::cout<<"dropping the frame"<<std::endl;
          }
          else
          {
              std::cout<<"display the frame"<<std::endl;
          }
      }
    }
    

    【讨论】:

    • 我试过你的代码。但我也有 fps2 = 15。并且 for 循环高达 15*10(10 秒的视频,15fps)。在算法结束时,我丢了 1 帧 :) 不好...
    • 上述算法是限制FPS的标准方式。第一次检查很可能会进行一些四舍五入。
    • 是的,我很抱歉 - 这看起来真的像一个圆角。如果它比我的更快 - 我会使用它! //去检查
    • 我确信这件事没有考虑到帧可能以可变的间隔出现。某些部分的延迟可能较大,而另一些部分的延迟可能较小。如果 t
    【解决方案4】:

    有一个足够大的环形缓冲区来容纳一整秒的视频。

    将每个视频帧写入此缓冲区。更新指向已写入的变量。

    在单独的线程中,使用计时器延迟 1000 / desired_fps 的周期(12fps 为 84 毫秒),然后选择写入的最后一帧并发送。

    【讨论】:

    • 我喜欢这个解决方案。但我不喜欢再添加一个线程。
    • 为什么不呢?杰西和我给出了非常相似的答案,都需要一个线程。有时,即使情况很简单,但最简单的方法是使用线程。我在应用程序中有线程,其中线程的设置和拆卸逻辑超过了线程执行的内部逻辑。无论如何,您可以使用与捕获相同的线程,但您会冒着跳过要发送的帧的风险。
    【解决方案5】:

    你对这样的解决方案有什么看法:

    我有 15 个带有 0 和 1 的静态 const 字节数组。 1 - 显示帧,0 - 否。当我收到帧时,我增加计数器 cnt = [0..15] 并查看它指向的位置 - 零或一。这就是我决定是显示还是丢帧的方式。

    我认为这个解决方案比实现新线程更容易和更快。

    【讨论】:

    • 这种解决方案很有效,但我认为仍然可能存在丢帧的风险,因为如果你只有一个线程,要么从相机或大部分时间写入输出,如果它在错误的时间做错了,您可能最终会丢失一个传入的帧(取决于缓冲的工作方式)。您使用此解决方案遇到的另一个问题是,当您的网络摄像头坏了并且您购买了一个开始发送 24fps(例如)或您的客户想要显示 10fps(例如)的新摄像头时会发生什么?
    • 这样做会引入很大的舍入误差。但是,如果它适合您的需要,请继续
    • 好吧,现在我看到您的解决方案更精确。但我的速度要快 3-4 倍。现在 - 我会用我的。但无论如何,非常感谢!
    猜你喜欢
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 2021-11-21
    相关资源
    最近更新 更多