我正在用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));
}
哪里出错,以及如何解决?
更新
代码中的box
是Emgu.UI.ImageBox
,这是线程安全的,我可以直接从其他线程访问它:box.Image = dest
答案 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发布渲染事件并实际渲染帧;有一个单独的线程渲染帧连续进入队列,并有一个计时器线程只需选择前帧。这样你就可以释放计时器线程并使其准确无误。