Screen recorder producing huge AVI files











up vote
8
down vote

favorite
2












I'm currently trying to make a screen recorder in C#, and so far it works but the problem is that something as simple as a 20second video will take about 1GB of space. I have it setup so a timer continuously takes screenshots with this method:



void takeScreenshot()
{
Rectangle bounds = Screen.FromControl(this).Bounds;
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
//Add screen to bitmap:
g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}
//Create and save screenshot:
string name = path + "//screenshot-" + fileCount + ".jpeg";
bitmap.Save(name, ImageFormat.Jpeg);
inputImageSequence.Add(name);
fileCount++;

//Dispose of bitmap:
bitmap.Dispose();
}
}


And then it stores those pictures in a temporary folder in the D:// drive, and then when it's done it takes all the pictures and creates an AVI video out of them like this:



//Set bounds of video to screen size:
Rectangle bounds = Screen.FromControl(this).Bounds;
int width = bounds.Width;
int height = bounds.Height;

var framRate = 5;

using (var vFWriter = new VideoFileWriter())
{
//Create new video file:
vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

//Make each screenshot into a video frame:
foreach (var imageLocation in inputImageSequence)
{
Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
vFWriter.WriteVideoFrame(imageFrame);
imageFrame.Dispose();
}
vFWriter.Close();
}
//Delete the screenshots and temporary folder:
DeletePath(path);


Any help on reducing the inefficiency of this is appreciated, I'm fairly new to this kind of programming.










share|improve this question




























    up vote
    8
    down vote

    favorite
    2












    I'm currently trying to make a screen recorder in C#, and so far it works but the problem is that something as simple as a 20second video will take about 1GB of space. I have it setup so a timer continuously takes screenshots with this method:



    void takeScreenshot()
    {
    Rectangle bounds = Screen.FromControl(this).Bounds;
    using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
    {
    using (Graphics g = Graphics.FromImage(bitmap))
    {
    //Add screen to bitmap:
    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
    }
    //Create and save screenshot:
    string name = path + "//screenshot-" + fileCount + ".jpeg";
    bitmap.Save(name, ImageFormat.Jpeg);
    inputImageSequence.Add(name);
    fileCount++;

    //Dispose of bitmap:
    bitmap.Dispose();
    }
    }


    And then it stores those pictures in a temporary folder in the D:// drive, and then when it's done it takes all the pictures and creates an AVI video out of them like this:



    //Set bounds of video to screen size:
    Rectangle bounds = Screen.FromControl(this).Bounds;
    int width = bounds.Width;
    int height = bounds.Height;

    var framRate = 5;

    using (var vFWriter = new VideoFileWriter())
    {
    //Create new video file:
    vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

    //Make each screenshot into a video frame:
    foreach (var imageLocation in inputImageSequence)
    {
    Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
    vFWriter.WriteVideoFrame(imageFrame);
    imageFrame.Dispose();
    }
    vFWriter.Close();
    }
    //Delete the screenshots and temporary folder:
    DeletePath(path);


    Any help on reducing the inefficiency of this is appreciated, I'm fairly new to this kind of programming.










    share|improve this question


























      up vote
      8
      down vote

      favorite
      2









      up vote
      8
      down vote

      favorite
      2






      2





      I'm currently trying to make a screen recorder in C#, and so far it works but the problem is that something as simple as a 20second video will take about 1GB of space. I have it setup so a timer continuously takes screenshots with this method:



      void takeScreenshot()
      {
      Rectangle bounds = Screen.FromControl(this).Bounds;
      using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
      {
      using (Graphics g = Graphics.FromImage(bitmap))
      {
      //Add screen to bitmap:
      g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
      }
      //Create and save screenshot:
      string name = path + "//screenshot-" + fileCount + ".jpeg";
      bitmap.Save(name, ImageFormat.Jpeg);
      inputImageSequence.Add(name);
      fileCount++;

      //Dispose of bitmap:
      bitmap.Dispose();
      }
      }


      And then it stores those pictures in a temporary folder in the D:// drive, and then when it's done it takes all the pictures and creates an AVI video out of them like this:



      //Set bounds of video to screen size:
      Rectangle bounds = Screen.FromControl(this).Bounds;
      int width = bounds.Width;
      int height = bounds.Height;

      var framRate = 5;

      using (var vFWriter = new VideoFileWriter())
      {
      //Create new video file:
      vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

      //Make each screenshot into a video frame:
      foreach (var imageLocation in inputImageSequence)
      {
      Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
      vFWriter.WriteVideoFrame(imageFrame);
      imageFrame.Dispose();
      }
      vFWriter.Close();
      }
      //Delete the screenshots and temporary folder:
      DeletePath(path);


      Any help on reducing the inefficiency of this is appreciated, I'm fairly new to this kind of programming.










      share|improve this question















      I'm currently trying to make a screen recorder in C#, and so far it works but the problem is that something as simple as a 20second video will take about 1GB of space. I have it setup so a timer continuously takes screenshots with this method:



      void takeScreenshot()
      {
      Rectangle bounds = Screen.FromControl(this).Bounds;
      using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
      {
      using (Graphics g = Graphics.FromImage(bitmap))
      {
      //Add screen to bitmap:
      g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
      }
      //Create and save screenshot:
      string name = path + "//screenshot-" + fileCount + ".jpeg";
      bitmap.Save(name, ImageFormat.Jpeg);
      inputImageSequence.Add(name);
      fileCount++;

      //Dispose of bitmap:
      bitmap.Dispose();
      }
      }


      And then it stores those pictures in a temporary folder in the D:// drive, and then when it's done it takes all the pictures and creates an AVI video out of them like this:



      //Set bounds of video to screen size:
      Rectangle bounds = Screen.FromControl(this).Bounds;
      int width = bounds.Width;
      int height = bounds.Height;

      var framRate = 5;

      using (var vFWriter = new VideoFileWriter())
      {
      //Create new video file:
      vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

      //Make each screenshot into a video frame:
      foreach (var imageLocation in inputImageSequence)
      {
      Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
      vFWriter.WriteVideoFrame(imageFrame);
      imageFrame.Dispose();
      }
      vFWriter.Close();
      }
      //Delete the screenshots and temporary folder:
      DeletePath(path);


      Any help on reducing the inefficiency of this is appreciated, I'm fairly new to this kind of programming.







      c# compression video






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 28 at 7:24









      t3chb0t

      33.8k746111




      33.8k746111










      asked Nov 28 at 3:26









      BenCompSci

      585




      585






















          4 Answers
          4






          active

          oldest

          votes

















          up vote
          13
          down vote













          takeScreenshot()




          • Based on the .NET Naming Guidelines methods should be named using PascalCase casing takeScreenshot() => TakeScreenshot()

          • You are enclosing the usage of the Bitmap inside a using statement which is the way to go but calling Dispose() on that Bitmap is superfluous because that is what a using statement is doing.


          • If you have two using statements without any code between you can stack them which saves one level of indentation like so (already removed the Dispose())



            using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
            using (Graphics g = Graphics.FromImage(bitmap))
            {
            //Add screen to bitmap:
            g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

            //Create and save screenshot:
            string name = path + "//screenshot-" + fileCount + ".jpeg";
            bitmap.Save(name, ImageFormat.Jpeg);
            inputImageSequence.Add(name);
            fileCount++;
            }


          • Comments should explain why something is done in the way it is done. Let the code itself tell what is done by using meaningful named things. In this way your comments won't lie like e.g //Create and save screenshot: which isn't what that code does. Saving yes but no creating of a screenshot is taking place.




          Creating the video




          • Try to be consistent. Here you create a Bitmap and call Dispose() instead of using a using statement. In addition, sometime you use var and sometimes you use the concrete type although the type is seen at first glance.

          • If you are using the as operator you should always add a null check for that object because an as operator won't throw an exception but the resulting object may be null which will trigger an exception somewhere else.


          Implementing these points will look like this:



          Rectangle bounds = Screen.FromControl(this).Bounds;
          var width = bounds.Width;
          var height = bounds.Height;

          var framRate = 5;

          using (var vFWriter = new VideoFileWriter())
          {
          vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

          foreach (var imageLocation in inputImageSequence)
          {
          using(var imageFrame = (Bitmap)System.Drawing.Image.FromFile(imageLocation))
          {
          vFWriter.WriteVideoFrame(imageFrame);
          }
          }
          vFWriter.Close();
          }
          DeletePath(path);




          My guess about the big file size is that you are using the VideoCodec.Raw. Try to change it to some other codec and see if this helps.






          share|improve this answer























          • @Glorfindel thanks for the edits.
            – Heslacher
            Nov 28 at 13:03


















          up vote
          8
          down vote













          You could write the screenshots directly to the video stream. This could save you a lot of disk space depending on your capture time.



          To make the video file smaller you need to use a codec that compresses the output like MPEG-4.



          Finally you could encapsulate the recording in its own class:



          public sealed class ScreenRecorder : IDisposable
          {
          private readonly VideoFileWriter videoFileWriter = new VideoFileWriter();
          private readonly string videoFilePath;
          private readonly Rectangle bounds;

          public ScreenRecorder(string videoFilePath, Rectangle bounds, int frameRate = 5, VideoCodec videoCodec = VideoCodec.MPEG4)
          {
          if (string.IsNullOrWhiteSpace(videoFilePath))
          {
          throw new ArgumentException("Must be a valid filename", nameof(videoFilePath));
          }

          if(frameRate < 1)
          {
          throw new ArgumentOutOfRangeException(nameof(frameRate));
          }

          this.videoFilePath = videoFilePath;
          this.bounds = bounds;
          videoFileWriter.Open(videoFilePath, bounds.Width, bounds.Height, frameRate, videoCodec);
          }

          public void TakeScreenshot()
          {
          if (disposed)
          {
          throw new ObjectDisposedException(nameof(ScreenRecorder));
          }

          using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
          {
          using (Graphics g = Graphics.FromImage(bitmap))
          {
          //Add screen to bitmap:
          g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
          }

          videoFileWriter.WriteVideoFrame(bitmap);
          }
          }

          public void Stop() => Dispose();

          #region IDisposable Support
          private bool disposed = false; // To detect redundant calls

          void Dispose(bool disposing)
          {
          if (!disposed)
          {
          if (disposing)
          {
          videoFileWriter.Dispose();
          }

          disposed = true;
          }
          }

          public void Dispose()
          {
          Dispose(true);
          }
          #endregion
          }





          share|improve this answer






























            up vote
            4
            down vote













            Using string concatenation for creating file paths opens you up to a whole lot of headache. Should you use /, //, or \? The answer depends on the environment you are running in. Luckily, the Path class can do all this logic for us:



            Instead of



            path + "//screenshot-" + fileCount + ".jpeg";


            you could do



            Path.Combine(path, $"screenshot-{fileCount}.jpeg");


            and instead of



            outputPath+"//video.avi"
            Path.Combine(outputPath, "video.avi")


            This way you don't have to worry about using the right fileseparator.






            share|improve this answer




























              up vote
              0
              down vote













              Thanks to all your help as well as the help of many others, I have actually fully compelted the screen recorder and have typed up an explanation here if anyone is interested: https://benbcompsci.wordpress.com/2018/12/04/c-screen-recorder/






              share|improve this answer





















                Your Answer





                StackExchange.ifUsing("editor", function () {
                return StackExchange.using("mathjaxEditing", function () {
                StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
                StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
                });
                });
                }, "mathjax-editing");

                StackExchange.ifUsing("editor", function () {
                StackExchange.using("externalEditor", function () {
                StackExchange.using("snippets", function () {
                StackExchange.snippets.init();
                });
                });
                }, "code-snippets");

                StackExchange.ready(function() {
                var channelOptions = {
                tags: "".split(" "),
                id: "196"
                };
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function() {
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled) {
                StackExchange.using("snippets", function() {
                createEditor();
                });
                }
                else {
                createEditor();
                }
                });

                function createEditor() {
                StackExchange.prepareEditor({
                heartbeatType: 'answer',
                convertImagesToLinks: false,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: null,
                bindNavPrevention: true,
                postfix: "",
                imageUploader: {
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                },
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                });


                }
                });














                draft saved

                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208582%2fscreen-recorder-producing-huge-avi-files%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                4 Answers
                4






                active

                oldest

                votes








                4 Answers
                4






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes








                up vote
                13
                down vote













                takeScreenshot()




                • Based on the .NET Naming Guidelines methods should be named using PascalCase casing takeScreenshot() => TakeScreenshot()

                • You are enclosing the usage of the Bitmap inside a using statement which is the way to go but calling Dispose() on that Bitmap is superfluous because that is what a using statement is doing.


                • If you have two using statements without any code between you can stack them which saves one level of indentation like so (already removed the Dispose())



                  using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                  using (Graphics g = Graphics.FromImage(bitmap))
                  {
                  //Add screen to bitmap:
                  g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

                  //Create and save screenshot:
                  string name = path + "//screenshot-" + fileCount + ".jpeg";
                  bitmap.Save(name, ImageFormat.Jpeg);
                  inputImageSequence.Add(name);
                  fileCount++;
                  }


                • Comments should explain why something is done in the way it is done. Let the code itself tell what is done by using meaningful named things. In this way your comments won't lie like e.g //Create and save screenshot: which isn't what that code does. Saving yes but no creating of a screenshot is taking place.




                Creating the video




                • Try to be consistent. Here you create a Bitmap and call Dispose() instead of using a using statement. In addition, sometime you use var and sometimes you use the concrete type although the type is seen at first glance.

                • If you are using the as operator you should always add a null check for that object because an as operator won't throw an exception but the resulting object may be null which will trigger an exception somewhere else.


                Implementing these points will look like this:



                Rectangle bounds = Screen.FromControl(this).Bounds;
                var width = bounds.Width;
                var height = bounds.Height;

                var framRate = 5;

                using (var vFWriter = new VideoFileWriter())
                {
                vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

                foreach (var imageLocation in inputImageSequence)
                {
                using(var imageFrame = (Bitmap)System.Drawing.Image.FromFile(imageLocation))
                {
                vFWriter.WriteVideoFrame(imageFrame);
                }
                }
                vFWriter.Close();
                }
                DeletePath(path);




                My guess about the big file size is that you are using the VideoCodec.Raw. Try to change it to some other codec and see if this helps.






                share|improve this answer























                • @Glorfindel thanks for the edits.
                  – Heslacher
                  Nov 28 at 13:03















                up vote
                13
                down vote













                takeScreenshot()




                • Based on the .NET Naming Guidelines methods should be named using PascalCase casing takeScreenshot() => TakeScreenshot()

                • You are enclosing the usage of the Bitmap inside a using statement which is the way to go but calling Dispose() on that Bitmap is superfluous because that is what a using statement is doing.


                • If you have two using statements without any code between you can stack them which saves one level of indentation like so (already removed the Dispose())



                  using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                  using (Graphics g = Graphics.FromImage(bitmap))
                  {
                  //Add screen to bitmap:
                  g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

                  //Create and save screenshot:
                  string name = path + "//screenshot-" + fileCount + ".jpeg";
                  bitmap.Save(name, ImageFormat.Jpeg);
                  inputImageSequence.Add(name);
                  fileCount++;
                  }


                • Comments should explain why something is done in the way it is done. Let the code itself tell what is done by using meaningful named things. In this way your comments won't lie like e.g //Create and save screenshot: which isn't what that code does. Saving yes but no creating of a screenshot is taking place.




                Creating the video




                • Try to be consistent. Here you create a Bitmap and call Dispose() instead of using a using statement. In addition, sometime you use var and sometimes you use the concrete type although the type is seen at first glance.

                • If you are using the as operator you should always add a null check for that object because an as operator won't throw an exception but the resulting object may be null which will trigger an exception somewhere else.


                Implementing these points will look like this:



                Rectangle bounds = Screen.FromControl(this).Bounds;
                var width = bounds.Width;
                var height = bounds.Height;

                var framRate = 5;

                using (var vFWriter = new VideoFileWriter())
                {
                vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

                foreach (var imageLocation in inputImageSequence)
                {
                using(var imageFrame = (Bitmap)System.Drawing.Image.FromFile(imageLocation))
                {
                vFWriter.WriteVideoFrame(imageFrame);
                }
                }
                vFWriter.Close();
                }
                DeletePath(path);




                My guess about the big file size is that you are using the VideoCodec.Raw. Try to change it to some other codec and see if this helps.






                share|improve this answer























                • @Glorfindel thanks for the edits.
                  – Heslacher
                  Nov 28 at 13:03













                up vote
                13
                down vote










                up vote
                13
                down vote









                takeScreenshot()




                • Based on the .NET Naming Guidelines methods should be named using PascalCase casing takeScreenshot() => TakeScreenshot()

                • You are enclosing the usage of the Bitmap inside a using statement which is the way to go but calling Dispose() on that Bitmap is superfluous because that is what a using statement is doing.


                • If you have two using statements without any code between you can stack them which saves one level of indentation like so (already removed the Dispose())



                  using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                  using (Graphics g = Graphics.FromImage(bitmap))
                  {
                  //Add screen to bitmap:
                  g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

                  //Create and save screenshot:
                  string name = path + "//screenshot-" + fileCount + ".jpeg";
                  bitmap.Save(name, ImageFormat.Jpeg);
                  inputImageSequence.Add(name);
                  fileCount++;
                  }


                • Comments should explain why something is done in the way it is done. Let the code itself tell what is done by using meaningful named things. In this way your comments won't lie like e.g //Create and save screenshot: which isn't what that code does. Saving yes but no creating of a screenshot is taking place.




                Creating the video




                • Try to be consistent. Here you create a Bitmap and call Dispose() instead of using a using statement. In addition, sometime you use var and sometimes you use the concrete type although the type is seen at first glance.

                • If you are using the as operator you should always add a null check for that object because an as operator won't throw an exception but the resulting object may be null which will trigger an exception somewhere else.


                Implementing these points will look like this:



                Rectangle bounds = Screen.FromControl(this).Bounds;
                var width = bounds.Width;
                var height = bounds.Height;

                var framRate = 5;

                using (var vFWriter = new VideoFileWriter())
                {
                vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

                foreach (var imageLocation in inputImageSequence)
                {
                using(var imageFrame = (Bitmap)System.Drawing.Image.FromFile(imageLocation))
                {
                vFWriter.WriteVideoFrame(imageFrame);
                }
                }
                vFWriter.Close();
                }
                DeletePath(path);




                My guess about the big file size is that you are using the VideoCodec.Raw. Try to change it to some other codec and see if this helps.






                share|improve this answer














                takeScreenshot()




                • Based on the .NET Naming Guidelines methods should be named using PascalCase casing takeScreenshot() => TakeScreenshot()

                • You are enclosing the usage of the Bitmap inside a using statement which is the way to go but calling Dispose() on that Bitmap is superfluous because that is what a using statement is doing.


                • If you have two using statements without any code between you can stack them which saves one level of indentation like so (already removed the Dispose())



                  using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                  using (Graphics g = Graphics.FromImage(bitmap))
                  {
                  //Add screen to bitmap:
                  g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);

                  //Create and save screenshot:
                  string name = path + "//screenshot-" + fileCount + ".jpeg";
                  bitmap.Save(name, ImageFormat.Jpeg);
                  inputImageSequence.Add(name);
                  fileCount++;
                  }


                • Comments should explain why something is done in the way it is done. Let the code itself tell what is done by using meaningful named things. In this way your comments won't lie like e.g //Create and save screenshot: which isn't what that code does. Saving yes but no creating of a screenshot is taking place.




                Creating the video




                • Try to be consistent. Here you create a Bitmap and call Dispose() instead of using a using statement. In addition, sometime you use var and sometimes you use the concrete type although the type is seen at first glance.

                • If you are using the as operator you should always add a null check for that object because an as operator won't throw an exception but the resulting object may be null which will trigger an exception somewhere else.


                Implementing these points will look like this:



                Rectangle bounds = Screen.FromControl(this).Bounds;
                var width = bounds.Width;
                var height = bounds.Height;

                var framRate = 5;

                using (var vFWriter = new VideoFileWriter())
                {
                vFWriter.Open(outputPath+"//video.avi", width, height, framRate, VideoCodec.Raw);

                foreach (var imageLocation in inputImageSequence)
                {
                using(var imageFrame = (Bitmap)System.Drawing.Image.FromFile(imageLocation))
                {
                vFWriter.WriteVideoFrame(imageFrame);
                }
                }
                vFWriter.Close();
                }
                DeletePath(path);




                My guess about the big file size is that you are using the VideoCodec.Raw. Try to change it to some other codec and see if this helps.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 28 at 13:02









                Glorfindel

                2541614




                2541614










                answered Nov 28 at 5:43









                Heslacher

                44.8k460155




                44.8k460155












                • @Glorfindel thanks for the edits.
                  – Heslacher
                  Nov 28 at 13:03


















                • @Glorfindel thanks for the edits.
                  – Heslacher
                  Nov 28 at 13:03
















                @Glorfindel thanks for the edits.
                – Heslacher
                Nov 28 at 13:03




                @Glorfindel thanks for the edits.
                – Heslacher
                Nov 28 at 13:03












                up vote
                8
                down vote













                You could write the screenshots directly to the video stream. This could save you a lot of disk space depending on your capture time.



                To make the video file smaller you need to use a codec that compresses the output like MPEG-4.



                Finally you could encapsulate the recording in its own class:



                public sealed class ScreenRecorder : IDisposable
                {
                private readonly VideoFileWriter videoFileWriter = new VideoFileWriter();
                private readonly string videoFilePath;
                private readonly Rectangle bounds;

                public ScreenRecorder(string videoFilePath, Rectangle bounds, int frameRate = 5, VideoCodec videoCodec = VideoCodec.MPEG4)
                {
                if (string.IsNullOrWhiteSpace(videoFilePath))
                {
                throw new ArgumentException("Must be a valid filename", nameof(videoFilePath));
                }

                if(frameRate < 1)
                {
                throw new ArgumentOutOfRangeException(nameof(frameRate));
                }

                this.videoFilePath = videoFilePath;
                this.bounds = bounds;
                videoFileWriter.Open(videoFilePath, bounds.Width, bounds.Height, frameRate, videoCodec);
                }

                public void TakeScreenshot()
                {
                if (disposed)
                {
                throw new ObjectDisposedException(nameof(ScreenRecorder));
                }

                using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                //Add screen to bitmap:
                g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                }

                videoFileWriter.WriteVideoFrame(bitmap);
                }
                }

                public void Stop() => Dispose();

                #region IDisposable Support
                private bool disposed = false; // To detect redundant calls

                void Dispose(bool disposing)
                {
                if (!disposed)
                {
                if (disposing)
                {
                videoFileWriter.Dispose();
                }

                disposed = true;
                }
                }

                public void Dispose()
                {
                Dispose(true);
                }
                #endregion
                }





                share|improve this answer



























                  up vote
                  8
                  down vote













                  You could write the screenshots directly to the video stream. This could save you a lot of disk space depending on your capture time.



                  To make the video file smaller you need to use a codec that compresses the output like MPEG-4.



                  Finally you could encapsulate the recording in its own class:



                  public sealed class ScreenRecorder : IDisposable
                  {
                  private readonly VideoFileWriter videoFileWriter = new VideoFileWriter();
                  private readonly string videoFilePath;
                  private readonly Rectangle bounds;

                  public ScreenRecorder(string videoFilePath, Rectangle bounds, int frameRate = 5, VideoCodec videoCodec = VideoCodec.MPEG4)
                  {
                  if (string.IsNullOrWhiteSpace(videoFilePath))
                  {
                  throw new ArgumentException("Must be a valid filename", nameof(videoFilePath));
                  }

                  if(frameRate < 1)
                  {
                  throw new ArgumentOutOfRangeException(nameof(frameRate));
                  }

                  this.videoFilePath = videoFilePath;
                  this.bounds = bounds;
                  videoFileWriter.Open(videoFilePath, bounds.Width, bounds.Height, frameRate, videoCodec);
                  }

                  public void TakeScreenshot()
                  {
                  if (disposed)
                  {
                  throw new ObjectDisposedException(nameof(ScreenRecorder));
                  }

                  using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                  {
                  using (Graphics g = Graphics.FromImage(bitmap))
                  {
                  //Add screen to bitmap:
                  g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                  }

                  videoFileWriter.WriteVideoFrame(bitmap);
                  }
                  }

                  public void Stop() => Dispose();

                  #region IDisposable Support
                  private bool disposed = false; // To detect redundant calls

                  void Dispose(bool disposing)
                  {
                  if (!disposed)
                  {
                  if (disposing)
                  {
                  videoFileWriter.Dispose();
                  }

                  disposed = true;
                  }
                  }

                  public void Dispose()
                  {
                  Dispose(true);
                  }
                  #endregion
                  }





                  share|improve this answer

























                    up vote
                    8
                    down vote










                    up vote
                    8
                    down vote









                    You could write the screenshots directly to the video stream. This could save you a lot of disk space depending on your capture time.



                    To make the video file smaller you need to use a codec that compresses the output like MPEG-4.



                    Finally you could encapsulate the recording in its own class:



                    public sealed class ScreenRecorder : IDisposable
                    {
                    private readonly VideoFileWriter videoFileWriter = new VideoFileWriter();
                    private readonly string videoFilePath;
                    private readonly Rectangle bounds;

                    public ScreenRecorder(string videoFilePath, Rectangle bounds, int frameRate = 5, VideoCodec videoCodec = VideoCodec.MPEG4)
                    {
                    if (string.IsNullOrWhiteSpace(videoFilePath))
                    {
                    throw new ArgumentException("Must be a valid filename", nameof(videoFilePath));
                    }

                    if(frameRate < 1)
                    {
                    throw new ArgumentOutOfRangeException(nameof(frameRate));
                    }

                    this.videoFilePath = videoFilePath;
                    this.bounds = bounds;
                    videoFileWriter.Open(videoFilePath, bounds.Width, bounds.Height, frameRate, videoCodec);
                    }

                    public void TakeScreenshot()
                    {
                    if (disposed)
                    {
                    throw new ObjectDisposedException(nameof(ScreenRecorder));
                    }

                    using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                    {
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                    //Add screen to bitmap:
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                    }

                    videoFileWriter.WriteVideoFrame(bitmap);
                    }
                    }

                    public void Stop() => Dispose();

                    #region IDisposable Support
                    private bool disposed = false; // To detect redundant calls

                    void Dispose(bool disposing)
                    {
                    if (!disposed)
                    {
                    if (disposing)
                    {
                    videoFileWriter.Dispose();
                    }

                    disposed = true;
                    }
                    }

                    public void Dispose()
                    {
                    Dispose(true);
                    }
                    #endregion
                    }





                    share|improve this answer














                    You could write the screenshots directly to the video stream. This could save you a lot of disk space depending on your capture time.



                    To make the video file smaller you need to use a codec that compresses the output like MPEG-4.



                    Finally you could encapsulate the recording in its own class:



                    public sealed class ScreenRecorder : IDisposable
                    {
                    private readonly VideoFileWriter videoFileWriter = new VideoFileWriter();
                    private readonly string videoFilePath;
                    private readonly Rectangle bounds;

                    public ScreenRecorder(string videoFilePath, Rectangle bounds, int frameRate = 5, VideoCodec videoCodec = VideoCodec.MPEG4)
                    {
                    if (string.IsNullOrWhiteSpace(videoFilePath))
                    {
                    throw new ArgumentException("Must be a valid filename", nameof(videoFilePath));
                    }

                    if(frameRate < 1)
                    {
                    throw new ArgumentOutOfRangeException(nameof(frameRate));
                    }

                    this.videoFilePath = videoFilePath;
                    this.bounds = bounds;
                    videoFileWriter.Open(videoFilePath, bounds.Width, bounds.Height, frameRate, videoCodec);
                    }

                    public void TakeScreenshot()
                    {
                    if (disposed)
                    {
                    throw new ObjectDisposedException(nameof(ScreenRecorder));
                    }

                    using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
                    {
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                    //Add screen to bitmap:
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                    }

                    videoFileWriter.WriteVideoFrame(bitmap);
                    }
                    }

                    public void Stop() => Dispose();

                    #region IDisposable Support
                    private bool disposed = false; // To detect redundant calls

                    void Dispose(bool disposing)
                    {
                    if (!disposed)
                    {
                    if (disposing)
                    {
                    videoFileWriter.Dispose();
                    }

                    disposed = true;
                    }
                    }

                    public void Dispose()
                    {
                    Dispose(true);
                    }
                    #endregion
                    }






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Nov 28 at 10:19

























                    answered Nov 28 at 9:33









                    Johnbot

                    2,179716




                    2,179716






















                        up vote
                        4
                        down vote













                        Using string concatenation for creating file paths opens you up to a whole lot of headache. Should you use /, //, or \? The answer depends on the environment you are running in. Luckily, the Path class can do all this logic for us:



                        Instead of



                        path + "//screenshot-" + fileCount + ".jpeg";


                        you could do



                        Path.Combine(path, $"screenshot-{fileCount}.jpeg");


                        and instead of



                        outputPath+"//video.avi"
                        Path.Combine(outputPath, "video.avi")


                        This way you don't have to worry about using the right fileseparator.






                        share|improve this answer

























                          up vote
                          4
                          down vote













                          Using string concatenation for creating file paths opens you up to a whole lot of headache. Should you use /, //, or \? The answer depends on the environment you are running in. Luckily, the Path class can do all this logic for us:



                          Instead of



                          path + "//screenshot-" + fileCount + ".jpeg";


                          you could do



                          Path.Combine(path, $"screenshot-{fileCount}.jpeg");


                          and instead of



                          outputPath+"//video.avi"
                          Path.Combine(outputPath, "video.avi")


                          This way you don't have to worry about using the right fileseparator.






                          share|improve this answer























                            up vote
                            4
                            down vote










                            up vote
                            4
                            down vote









                            Using string concatenation for creating file paths opens you up to a whole lot of headache. Should you use /, //, or \? The answer depends on the environment you are running in. Luckily, the Path class can do all this logic for us:



                            Instead of



                            path + "//screenshot-" + fileCount + ".jpeg";


                            you could do



                            Path.Combine(path, $"screenshot-{fileCount}.jpeg");


                            and instead of



                            outputPath+"//video.avi"
                            Path.Combine(outputPath, "video.avi")


                            This way you don't have to worry about using the right fileseparator.






                            share|improve this answer












                            Using string concatenation for creating file paths opens you up to a whole lot of headache. Should you use /, //, or \? The answer depends on the environment you are running in. Luckily, the Path class can do all this logic for us:



                            Instead of



                            path + "//screenshot-" + fileCount + ".jpeg";


                            you could do



                            Path.Combine(path, $"screenshot-{fileCount}.jpeg");


                            and instead of



                            outputPath+"//video.avi"
                            Path.Combine(outputPath, "video.avi")


                            This way you don't have to worry about using the right fileseparator.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Nov 28 at 14:22









                            JAD

                            6591320




                            6591320






















                                up vote
                                0
                                down vote













                                Thanks to all your help as well as the help of many others, I have actually fully compelted the screen recorder and have typed up an explanation here if anyone is interested: https://benbcompsci.wordpress.com/2018/12/04/c-screen-recorder/






                                share|improve this answer

























                                  up vote
                                  0
                                  down vote













                                  Thanks to all your help as well as the help of many others, I have actually fully compelted the screen recorder and have typed up an explanation here if anyone is interested: https://benbcompsci.wordpress.com/2018/12/04/c-screen-recorder/






                                  share|improve this answer























                                    up vote
                                    0
                                    down vote










                                    up vote
                                    0
                                    down vote









                                    Thanks to all your help as well as the help of many others, I have actually fully compelted the screen recorder and have typed up an explanation here if anyone is interested: https://benbcompsci.wordpress.com/2018/12/04/c-screen-recorder/






                                    share|improve this answer












                                    Thanks to all your help as well as the help of many others, I have actually fully compelted the screen recorder and have typed up an explanation here if anyone is interested: https://benbcompsci.wordpress.com/2018/12/04/c-screen-recorder/







                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered Dec 4 at 7:10









                                    BenCompSci

                                    585




                                    585






























                                        draft saved

                                        draft discarded




















































                                        Thanks for contributing an answer to Code Review Stack Exchange!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid



                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.


                                        Use MathJax to format equations. MathJax reference.


                                        To learn more, see our tips on writing great answers.





                                        Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                                        Please pay close attention to the following guidance:


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid



                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.


                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function () {
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208582%2fscreen-recorder-producing-huge-avi-files%23new-answer', 'question_page');
                                        }
                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown