-1

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

现在我可以播放视频了,但我发现 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));
}

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


更新

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

4

3 回答 3

2

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

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

于 2012-08-09T15:24:29.247 回答
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;
}
于 2012-08-09T15:31:08.750 回答
1

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

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

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

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

于 2012-08-09T15:40:09.817 回答