如何使用c#编写一个简单的视频播放器,以准确的fps播放视频?

时间:2012-08-09 15:10:14

标签: c# multithreading video opencv emgucv

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

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

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

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

3 个答案:

答案 0 :(得分:2)

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

没有简单的视频 - vlc.exe:13个主题,realplay.exe:19个主题,divX播放器:23个主题。

答案 1 :(得分:1)

我的建议是使用计时器

            _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;
}

答案 2 :(得分:1)

以非常小且准确的间隔定时事件并非易事,Thread.Sleep肯定不是你想要的。

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

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

第三,不要使用相同的线程每33ms发布渲染事件并实际渲染帧;有一个单独的线程渲染帧连续进入队列,并有一个计时器线程只需选择前帧。这样你就可以释放计时器线程并使其准确无误。