28

Is there some library to capture a screen to be a compressed video file or some solution that can do it?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gramero
  • 1,835
  • 3
  • 24
  • 26

2 Answers2

49

This code uses SharpAvi available on NuGet.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using SharpAvi;
using SharpAvi.Codecs;
using SharpAvi.Output;
using System.Windows.Forms;

namespace Captura
{
    // Used to Configure the Recorder
    public class RecorderParams
    {
        public RecorderParams(string filename, int FrameRate, FourCC Encoder, int Quality)
        {
            FileName = filename;
            FramesPerSecond = FrameRate;
            Codec = Encoder;
            this.Quality = Quality;

            Height = Screen.PrimaryScreen.Bounds.Height;
            Width = Screen.PrimaryScreen.Bounds.Width;
        }

        string FileName;
        public int FramesPerSecond, Quality;
        FourCC Codec;

        public int Height { get; private set; }
        public int Width { get; private set; }

        public AviWriter CreateAviWriter()
        {
            return new AviWriter(FileName)
            {
                FramesPerSecond = FramesPerSecond,
                EmitIndex1 = true,
            };
        }

        public IAviVideoStream CreateVideoStream(AviWriter writer)
        {
            // Select encoder type based on FOURCC of codec
            if (Codec == KnownFourCCs.Codecs.Uncompressed)
                return writer.AddUncompressedVideoStream(Width, Height);
            else if (Codec == KnownFourCCs.Codecs.MotionJpeg)
                return writer.AddMotionJpegVideoStream(Width, Height, Quality);
            else
            {
                return writer.AddMpeg4VideoStream(Width, Height, (double)writer.FramesPerSecond,
                    // It seems that all tested MPEG-4 VfW codecs ignore the quality affecting parameters passed through VfW API
                    // They only respect the settings from their own configuration dialogs, and Mpeg4VideoEncoder currently has no support for this
                    quality: Quality,
                    codec: Codec,
                    // Most of VfW codecs expect single-threaded use, so we wrap this encoder to special wrapper
                    // Thus all calls to the encoder (including its instantiation) will be invoked on a single thread although encoding (and writing) is performed asynchronously
                    forceSingleThreadedAccess: true);
            }
        }
    }

    public class Recorder : IDisposable
    {
        #region Fields
        AviWriter writer;
        RecorderParams Params;
        IAviVideoStream videoStream;
        Thread screenThread;
        ManualResetEvent stopThread = new ManualResetEvent(false);
        #endregion

        public Recorder(RecorderParams Params)
        {
            this.Params = Params;

            // Create AVI writer and specify FPS
            writer = Params.CreateAviWriter();

            // Create video stream
            videoStream = Params.CreateVideoStream(writer);
            // Set only name. Other properties were when creating stream, 
            // either explicitly by arguments or implicitly by the encoder used
            videoStream.Name = "Captura";

            screenThread = new Thread(RecordScreen)
            {
                Name = typeof(Recorder).Name + ".RecordScreen",
                IsBackground = true
            };

            screenThread.Start();
        }

        public void Dispose()
        {
            stopThread.Set();
            screenThread.Join();

            // Close writer: the remaining data is written to a file and file is closed
            writer.Close();

            stopThread.Dispose();
        }

        void RecordScreen()
        {
            var frameInterval = TimeSpan.FromSeconds(1 / (double)writer.FramesPerSecond);
            var buffer = new byte[Params.Width * Params.Height * 4];
            Task videoWriteTask = null;
            var timeTillNextFrame = TimeSpan.Zero;

            while (!stopThread.WaitOne(timeTillNextFrame))
            {
                var timestamp = DateTime.Now;

                Screenshot(buffer);

                // Wait for the previous frame is written
                videoWriteTask?.Wait();

                // Start asynchronous (encoding and) writing of the new frame
                videoWriteTask = videoStream.WriteFrameAsync(true, buffer, 0, buffer.Length);

                timeTillNextFrame = timestamp + frameInterval - DateTime.Now;
                if (timeTillNextFrame < TimeSpan.Zero)
                    timeTillNextFrame = TimeSpan.Zero;
            }

            // Wait for the last frame is written
            videoWriteTask?.Wait();
        }

        public void Screenshot(byte[] Buffer)
        {
            using (var BMP = new Bitmap(Params.Width, Params.Height))
            {
                using (var g = Graphics.FromImage(BMP))
                {
                    g.CopyFromScreen(Point.Empty, Point.Empty, new Size(Params.Width, Params.Height), CopyPixelOperation.SourceCopy);

                    g.Flush();

                    var bits = BMP.LockBits(new Rectangle(0, 0, Params.Width, Params.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
                    Marshal.Copy(bits.Scan0, Buffer, 0, Buffer.Length);
                    BMP.UnlockBits(bits);
                }
            }
        }
    }
}

Example Usage

  1. Create a console application.
  2. Add Reference to System.Drawing and System.Windows.Forms. (The usage of System.Windows.Forms can be avoided if you modify the code).
  3. Install SharpAvi from NuGet.

Code:

using Captura;
using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Using MotionJpeg as Avi encoder,
            // output to 'out.avi' at 10 Frames per second, 70% quality
            var rec = new Recorder(new RecorderParams("out.avi", 10, SharpAvi.KnownFourCCs.Codecs.MotionJpeg, 70));

            Console.WriteLine("Press any key to Stop...");
            Console.ReadKey();

            // Finish Writing
            rec.Dispose();
        }
    }
}
Mathew Sachin
  • 1,429
  • 1
  • 15
  • 20
  • 1
    Can you describe a little bit more here? – Enamul Hassan Sep 26 '15 at 13:26
  • The app uses user32.dll and gdi32.dll to capture screenshots and writes them into the AviWriter along with audio captured using WaveIn api – Mathew Sachin Sep 26 '15 at 13:57
  • 1
    Worked for me! Thanks for share it Mathew! – Jordi Espada Jun 20 '16 at 13:33
  • What is FourCC here. –  Apr 27 '17 at 19:45
  • FourCC stands for 4 'character code'. It is a type defined in SharpAvi, used to identify an encoder installed in the system. – Mathew Sachin Apr 27 '17 at 21:38
  • SharpAvi contains a class: KnownFourCCs.Codecs which contains FourCCs of codecs like MotionJPEG, Xvid, x264, etc. – Mathew Sachin Apr 27 '17 at 21:40
  • What should I pass as FourCC argument –  May 03 '17 at 11:29
  • Try passing `KnownFourCCs.Codecs.MotionJpeg`. – Mathew Sachin May 03 '17 at 15:43
  • It is implemented in SharpAvi and should work even if your system does not have any codecs available. – Mathew Sachin May 03 '17 at 15:43
  • Thankyou Mathew Sachin its recording video but how to save this recording –  May 19 '17 at 13:16
  • I added an example usage in the answer. Hope that it will answer your question. – Mathew Sachin May 19 '17 at 17:23
  • Mathew I did not find save output file please tell me how to save output as mp4 or other video format. – TAHA SULTAN TEMURI Jun 22 '17 at 13:26
  • 2
    Why captured video has speed x2 or even x3? – Yoda Apr 05 '19 at 11:06
  • 1
    This SharpAvi is speeding up videos, regardless of how slow I feed him bitmaps, even when I added blocking queue. Don't waist your time on the code above. – Yoda Apr 08 '19 at 16:08
  • Additionaly even Captura standalone app has exactly the same performance issues on Xeon, 32GB Ram, 500GB SSD, Quadro 2000 laptop. – Yoda Apr 09 '19 at 08:44
  • Issue with `RecorderParams` constructor in VB. It seems case insensitive, so when creating a new `Recorder` object with `RecorderParams` threw a null filepath exception. The parameter was setting to itself. Fixed by adding an underscore in the constructor arguments, to `filename`. Other than that, great answer. Couldn't have been any easier. – Tyler Montney Sep 16 '19 at 20:41
  • Amazing staff, what about put the link to github to the end? Since the code in the page is great enough. The git hub project run with errors. – jw_ Feb 19 '20 at 11:49
  • 1
    How to capture sound at the same time? – jw_ Feb 20 '20 at 02:08
  • When I use SharpAVI in my app on full RDP desktop it works fine. But if my app is launched through RemoteApp, without full desktop, It doesn't let my app to load at all. – Hesoti Aug 28 '22 at 20:29
21

Your best option seems to be Windows Media Encoder.

Here are some resources:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andre Luus
  • 3,692
  • 3
  • 33
  • 46
  • I looking for DirectshowNet, but I don't know what should I use the input filter for screen capture. – Gramero Nov 09 '10 at 06:12
  • The last link seems to be broken (on the way to time out), but it is "just" ***extremely*** slow. It took more than 4 minutes... – Peter Mortensen Dec 30 '22 at 19:36