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



我正在用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

您需要保持帧对象的队列"充值"并使用某种计时器渲染它们。 通常,我会使用不断回收的框架对象池。

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

我的建议是使用计时器

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

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

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

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

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

最新更新