Screen capture video using AForge.Video.FFMPEG / AForge.Video.VFW. Frame Rate issue

Raheel Khan picture Raheel Khan · Apr 27, 2012 · Viewed 9.3k times · Source

My WinForms .NET 4 C# application records the desktop while the user interacts with it.

It uses the AForge FFMPEG or VFW wrappers depending on the speed of the system. The capturing is done in a background thread of course.

In either case, the wrappers require the frame rate to be specified in advance. This is problematic since capturing frequency is undeterministic and easily affected by how busy the target machine is. On a good system, I get a maximum of 10 FPS.

So two issues here:

  • How to align frames based on actual capture frame rate?
  • How to increase the frame rate, perhaps by using a solution other than AForge?

The code I use is listed below for clarity:

private void ThreadWork ()  
{  
    int count = 0;  
    string filename = "";  
    RECT rect = new RECT();  
    System.Drawing.Bitmap bitmap = null;  
    System.Drawing.Graphics graphics = null;  
    System.DateTime dateTime = System.DateTime.Now;  
    System.IntPtr hWndForeground = System.IntPtr.Zero;  
    AForge.Video.FFMPEG.VideoFileWriter writer = null;  

    filename = @"c:\Users\Raheel Khan\Desktop\Temp\Capture.avi";
    if (System.IO.File.Exists(filename))
        System.IO.File.Delete(filename);

    writer = new AForge.Video.FFMPEG.VideoFileWriter();
    writer.Open
    (
        filename,
        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
        10, // FPS
        AForge.Video.FFMPEG.VideoCodec.MPEG4
    );

    bitmap = new Bitmap
    (
        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
        System.Drawing.Imaging.PixelFormat.Format24bppRgb
    );
    graphics = System.Drawing.Graphics.FromImage(bitmap);

    dateTime = System.DateTime.Now;

    while (System.DateTime.Now.Subtract(dateTime).TotalSeconds < 10) // 10 seconds.
    {
        graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);

        rect = new RECT();
        hWndForeground = GetForegroundWindow();
        GetWindowRect(hWndForeground, ref rect);
        graphics.DrawRectangle(Pens.Red, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
        graphics.DrawString(count++.ToString(), this.Font, Brushes.Red, 10, 10);

        writer.WriteVideoFrame(bitmap);

        System.Threading.Thread.Sleep(10);
    }

    writer.Close();
    writer.Dispose();

    System.Diagnostics.Process.Start(System.IO.Path.GetDirectoryName(filename));
}

Answer

Sugrue picture Sugrue · Apr 27, 2012

You could capture the frames to a temp folder as jpegs or to a mjpeg file, and then reprocess it to an avi video file when the user desires it. This will cut down on the amount of processing you need to do 'on the fly' and free up resources - and allow your system to reach a higher and more regular frame rate.