我正在尝试从文件中读取视频(.mp4)并将其显示在表单上的图像框上。当我计算它的总播放时间时,它实际上比实际视频长度要长。例如,如果视频实际长度为 10 秒,则播放时间为 12-13 秒。这是为什么?我的视频帧率为 31fps
我尝试确保 fps 变量已经具有正确的帧速率。
private void Play_Video(string filePath)
{
VideoCapture cameraCapture = new VideoCapture(filePath);
var stopwatch = new Stopwatch();
stopwatch.Start();
while (true)
{
Mat m = new Mat();
cameraCapture.Read(m);
if (!m.IsEmpty)
{
imageBox1.Image = m;
double fps = cameraCapture.GetCaptureProperty(CapProp.Fps);
Thread.Sleep(1000 / Convert.ToInt32(fps));
}
else
{
break;
}
}
stopwatch.Stop();
double elapsed_time = stopwatch.ElapsedMilliseconds * 0.001;
// My elapsed_time here shows about 13 seconds, when the actual length of video is 10 seconds. Why?
}
新编辑: 我在单独的函数中更新了帧检索,以免在渲染过程中造成延迟,但仍然有几秒钟的延迟。例如,30 秒的视频会播放 32-33 秒。我更新的代码:
private void Play_Video(List<Mat> frames, double fps, Emgu.CV.UI.ImageBox imageBox)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < frames.Count; i++)
{
imageBox.Image = frames[i];
Thread.Sleep(1000 / Convert.ToInt32(fps));
}
stopwatch.Stop();
double elapsed_time = stopwatch.ElapsedMilliseconds * 0.001;
}
这是我获得准确 fps 的唯一方法,因为使用 thread.sleep 需要 10-15 毫秒来循环打开和关闭。我对 Task.Delay 也有同样的问题。这似乎工作得很好,如果有人有更好的解决方案,我很乐意看到它。
private void PlayVideo()
{
double framecount = video.Get(Emgu.CV.CvEnum.CapProp.FrameCount);
double fps = video.Get(Emgu.CV.CvEnum.CapProp.Fps);
video.Set(Emgu.CV.CvEnum.CapProp.PosFrames, (double)(framecount * (double)(savePercent / 100)));
int ms = Convert.ToInt32(((double)1000 / fps));
while (disablePreviewTimer == false)
{
DateTime dt = DateTime.Now;
Emgu.CV.Mat img = video.QueryFrame();
if (img == null)
{
disablePreviewTimer = true;
break;
}
else
{
Emgu.CV.Util.VectorOfByte vb = new Emgu.CV.Util.VectorOfByte();
Emgu.CV.CvInvoke.Resize(img, img, PreviewSize);
Emgu.CV.CvInvoke.Imencode(".jpg", img, vb);
pictureBox5.Image = Image.FromStream(new MemoryStream(vb.ToArray()));
Application.DoEvents();
while ((DateTime.Now - dt).TotalMilliseconds < ms)
{
Application.DoEvents();
}
label6.Text = ((int)(DateTime.Now - dt).TotalMilliseconds).ToString();
}
}
}
有点晚了,但对于其他遇到此问题的人来说,请使用秒表。
private Stopwatch _sw = new();
private long _fpsInterval = 1000/fps;
private long _currentInterval = 0;
//skipping all the setup but you will want to ensure you start your stopwatch.
private void SomeMethod()
{
while(true)
{
if (_sw.ElapsedMilliseconds > _currentInterval)
{
//do all your CV stuff here
_currentInterval += _fpsInterval
}
}
我显然把剩下的留给你,但我努力解决这个确切的问题,并让我所有的东西的时间完全正确,这似乎是确保你在每个间隔内获得一个帧的最佳方法。