我正在使用 .NET 4 / C# 开发基于 Winforms 的视频播放器控件。我们从客户那里收到了一些报告,但是帧播放有时会很不稳定——每分钟播放几次会有明显的延迟。显示器是一个图片框 - 我们每 40 毫秒更改一次图像(对于 25fps 视频)。
在修复了一些可能导致它的问题(并使播放更加流畅)之后,我们仍然偶尔会看到速度变慢,特别是当窗口最大化并且我们需要在屏幕上绘制一个全尺寸的视频播放器时。我注意到在这种情况下,在更新图像后,Picturebox 最多可能需要 7-12ms 才能刷新,并且影响很明显。
所以我尝试了一种不同的方法,我们有两个图片框(PB1 和 PB2)。PB1 显示当前帧,而 PB2 被隐藏。然后,我们在下一帧解码后立即更新 PB2 并刷新图像,这一切都在显示下一帧之前。一旦到了显示下一帧的时间,我们就显示 PB1 并隐藏 PB2。对于下一帧,我们更新 PB1,显示它并隐藏 PB2。冲洗并重复。此操作需要 1 毫秒,但我们仍然会看到全屏时偶尔出现延迟(尽管控制台日志显示更新始终相隔 40 毫秒),即使在测试应用程序中运行时一遍又一遍地显示相同的两帧。
这是Winforms本身的限制吗?有没有办法获得控件的毫秒级精确更新?这是我上面提到的代码,使用一个测试应用程序,我们在两个预加载的图像之间交替:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Bitmap image1;
private Bitmap image2;
private int i = 0;
public Form1()
{
InitializeComponent();
image1 = new Bitmap("image1.bmp");
image2 = new Bitmap("image2.bmp");
pictureBox1.Image = ResizeBitmap(image1, pictureBox1.Width, pictureBox1.Height);
pictureBox2.Image = ResizeBitmap(image2, pictureBox2.Width, pictureBox2.Height);
pictureBox1.Show();
pictureBox2.Hide();
}
public delegate void invoke();
private void redraw()
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
Console.WriteLine(DateTime.UtcNow.Millisecond);
stopwatch.Restart();
if (i % 2 == 0)
{
pictureBox1.BringToFront();
}
else
{
pictureBox2.BringToFront();
}
stopwatch.Stop();
Console.WriteLine("Bring to front: " + stopwatch.ElapsedMilliseconds);
Console.WriteLine(DateTime.UtcNow.Millisecond);
i++;
}
private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height)
{
Bitmap result = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(result))
g.DrawImage(sourceBMP, 0, 0, width, height);
return result;
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Show();
pictureBox2.Show();
pictureBox1.Image = ResizeBitmap(image1, pictureBox1.Width, pictureBox1.Height);
pictureBox2.Image = ResizeBitmap(image2, pictureBox2.Width, pictureBox2.Height);
pictureBox1.Refresh();
pictureBox2.Refresh();
}
private void Form1_Load(object sender, EventArgs e)
{
Action<object> action = (object obj) =>
{
while (true)
{
BeginInvoke(new invoke(redraw));
System.Threading.Thread.Sleep(40);
}
};
System.Threading.Tasks.Task t1 = new System.Threading.Tasks.Task(action, "a");
t1.Start();
}
}
}
时间是正确的,没有任何可能导致延迟的处理发生。似乎图片框无法每 40 毫秒可靠地更新一次。
我正在考虑将视频播放器移植到 SFML(嵌入在 Winforms 中),早期测试显示没有明显的滞后,所以很有希望。但是,无论如何要让 Picturebox 可靠地更新,还是它不能达到毫秒精度?