【问题标题】:How to use c# to write a simple video player which plays the video with accurate fps?如何使用 c# 编写一个简单的视频播放器,以准确的 fps 播放视频?
【发布时间】:2012-08-06 20:35:23
【问题描述】:

我正在用 Emgu 写一个简单的视频播放,它是 opencv 的 c# 包装器。

现在我可以播放视频了,但我发现 fps 不准确,比正常快一点。

在我的代码中,我使用Thread.sleep() 在两帧之间等待一段时间。代码是:

int fps = getFromFile(file); // which is 30
while (true) {
    var frame = CvInvoke.cvQueryFrame(capture);
    if (frame.ToInt32() == 0) break;

    Image<Bgr, byte> dest = new Image<Bgr, byte>(Convert.ToInt32(width), Convert.ToInt32(height));
    CvInvoke.cvCopy(frame, dest, IntPtr.Zero);
    box.Image = dest;

    Thread.Sleep(1000 / Convert.ToInt32(fps));
}

哪里出了问题,如何解决?


更新

代码中的boxEmgu.UI.ImageBox,是线程安全的,我可以直接从其他线程访问:box.Image = dest

【问题讨论】:

  • 1) Thread.Sleep 不是非常精确或保证 2) 等待时间需要考虑未等待的时间和其他时间偏移,也许考虑到总时间(参见 #1)3 ) 通常不建议在 UI 线程中休眠 .. 并且 UI 元素只能从 UI(希望创建)线程访问。
  • '简单视频' - 不存在。
  • 有趣的是,你说它更快,因为我认为它会更慢,因为你正在睡眠 33.3333 毫秒,但高于睡眠的操作也需要时间。你应该睡 (1000 / fps - )。
  • @ananthonline,我认为它应该比正常慢。但是我用我的播放器和一个著名的播放器打开同一个视频,他们同时开始,但很快我的播放器就开始播放了。
  • 你应该知道 OpenCV 不是为像视频显示这样的事情而构建的。所有关于 fps、帧数、视频位置的信息通常都是不准确的。 VideoCapture 对象只是为了帮助快速制作图像处理应用程序的原型——它可以加载帧——但是缺少了制作优秀视频播放器的所有小细节

标签: c# multithreading video opencv emgucv


【解决方案1】:

您需要保持一个框架对象队列“加满”并使用某种计时器来渲染它们。通常,我会使用一个不断回收的框架对象池。

没有简单的视频 - vlc.exe:13 个线程,realplay.exe:19 个线程,divX player:23 个线程。

【讨论】:

    【解决方案2】:

    我的建议是使用计时器

                _capture = new Capture(ofd.FileName);
                _videoTimer = new Timer();
                double fps = _capture.GetCaptureProperty(CAP_PROP.CV_CAP_PROP_FPS);
                _videoTimer.Interval =  Convert.ToInt16(1000 / fps); //the timer interval
                _videoTimer.Tick += new EventHandler(_videoTimer_Tick);
                _videoTimer.Start();
    

    现在在图像框中显示它

    private void _videoTimer_Tick(object sender, EventArgs e)
    {
               Image<Bgr, Byte> img = _capture.QueryFrame();
                if (img == null) return; 
                box.Image = img;
    }
    

    【讨论】:

    • 我只是用你的代码写一个新的播放器。这一次,它比平时慢了一点。因为fps这里是30,1000/fps是33,但真正的应该是33.333...
    • 我怀疑人眼会注意到差异。但你可以调整 1000/fps * 0.85 或类似的因素。
    【解决方案3】:

    以非常小的和准确的间隔对事件进行计时并非易事,而 Thread.Sleep 肯定不是您想要的。

    首先,Thread.Sleep 不保证您的线程将在指定的时间内恢复,它只是保证您将被挂起至少该时间。

    其次,大多数计时器在您需要的毫秒级别上并不准确。其中包括 System.Threading.Timer 和 System.Timers.Timer。使用这些,您最多会获得不稳定的帧速率。我选择的解决方案是使用System.Diagostics.Stopwatch 编写我自己的带有延迟补偿的事件循环。这是相当困难的,但 OpenTK 的GameWindow class 给出了一个很好的例子。另一个解决方案可能是 Multimedia Timers,但这些不是直接从 .NET 获得的,我没有使用这些的经验。

    第三,不要使用同一个线程每 33 毫秒发布一次渲染事件并实际渲染帧;有一个单独的线程将帧连续渲染到队列中,并且有一个计时器线程只需选择前帧。这样,您就可以释放计时器线程并使其尽可能准确。

    【讨论】:

      猜你喜欢
      • 2018-09-18
      • 1970-01-01
      • 1970-01-01
      • 2019-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多