I'm working on a Winforms based video player control using .NET 4 / C#. We've received some reports from clients however that the frame playback is sometimes jerky - a few times a minute there'll be a noticeable lag in the playback. The display is a picture box - we change the image every 40 ms (for 25fps video).
After fixing a number of things which could have caused it (and making playback far smoother) we still occasionally see slowdown, particularly when the window is maximized and we need to draw a full sized video player to screen. I noticed that the Picturebox can take up to 7-12ms to refresh after updating the image in this scenario and the impact is obvious.
So I tried a different approach, where we have two pictureboxes (PB1 and PB2). PB1 shows the current frame, while PB2 is hidden. We then update PB2 as soon as the next frame is decoded and refesh the image, all before its time to display the next frame. Once its time to display the next frame, we show PB1 and hide PB2. For the next frame, we update PB1, show it and hide PB2. Rinse and repeat. This operation takes 1ms, however we still see the occasional lag at full screen (despite the console log showing the updates are always 40ms apart), even when run in a test app showing the same two frames over and over.
Is this a limitation of Winforms itself? Is there no way to get millisecond accurate updates of the controls? Here's the code that I mention above, using a test app where we're alternating between two preloaded images:
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();
}
}
}
The timing is spot on and there's no processing happening that could cause a delay. It just seems that the picture box is unable to reliably update once every 40ms.
I'm looking at porting the video player to SFML (embedded in a Winforms) and early tests show no noticeable lag so that's promising. However, is there anyway to get the Picturebox to update reliably or is it just not capable of millisecond accuracy?