Simple Recorder

In this tutorial we’re going to make a screen and audio recorder software.

Prerequisite

What this software will do?

This software will capture the screen and the audio of the computer, this allow you to record gampeplay of videogames or your activity on the computer. To make this software we’ll use LibVLCSharp(we already use it in this tutorial) to record the video, and we’ll use NAudio to record the audio, then we’ll use FFMPEG to merge the audio and the video to a single file.

What we need to install?

Just like the video player tutorial we need to create a new “Windows Form” project, then with nuget we need to install:

The form

In this tutorial we’ll use only one form and its design is pretty simple, it only has 3 buttons, one to start the recording, one to stop it and one to merge the audio and video files into one.
It should look like something like this:
Responsive image

Video Recorder Class

To keep our code clean and simple we’ll use two classes one will record the video, the other for the audio.
Let’s start by adding a “VideoRecorder” class to the project.
First thing first let’s add:

1
using LibVLCSharp.Shared;

Now inside the class we need to setup the LibVLCSharp, let’s add:

1
2
3
4
public LibVLC _libVLC;
public MediaPlayer _mp;
public Media _media;
public bool _isRecording = false;

Inside the main function let’s add:

1
2
3
4
Core.Initialize();
_libVLC = new LibVLC("--verbose=2");
_mp = new MediaPlayer(_libVLC);
_media = new Media(_libVLC, "screen://", FromType.FromLocation);

--verbose=2 inside the LibVLC() allow us to see more informations inside the console, this can help us if there is any error or if something doesn’t work as expected.
screen:// is the source that we want to capture, in this case the screen(it’s the same for multi monitor setups).

Let’s create a new function StartRecVideo and inside it let’s add:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
if(_isRecording)
{
	return;
}
_media.AddOption(":screen-fps=60");
_media.AddOption(":sout=#transcode{vcodec=h264,sfilter=marq:logo}:file{dst=Video.mp4}");
_media.AddOption(":screen-width=1920:screen-height=1080");
_media.AddOption(":screen-mouse-image=Mouse.png");
_isRecording = true;
_mp.Play(_media);

:screen-fps=60 This means that we want to record at 60 frame per second.
sfilter=marq:logo We will not use these filters in this tutorial but i’ve already put them to let you add text and/or images to the video while recording.
:file{dst=Video.mp4} This tell what the name of the file will be.
:screen-width=1920:screen-height=1080 I set this up to record at the risolution 1920x1080, if you have a multi monitor setup this will let the video capture record only the first screen.
:screen-mouse-image=Mouse.png on the recorded file you will see that there is no mouse, this option will set any image called “Mouse.png” as the cursor, I edited a bit and used this one.
_mp.Play(_media); This will start the recording.

Now we need to stop the recording, to do that we need to create a new function StopRecVideo, inside it put:

1
2
_mp.Stop();
_isRecording = false;

Audio Recorder Class

In this second class we will record the audio of the computer.
Just like before, create a new class “AudioRecorder”.
On top add:

1
2
3
using System.Threading;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;

Inside the class add these variables:

1
2
3
4
public IWaveIn _capture;
public WaveOutEvent _waveOut;
public WaveFileWriter _writer;
public bool _isRecording = false;

IWaveIn _capture is the source that we’re recording.
WaveOutEvent _waveOut allow us to play audio, I’ll explain it later.
WaveFileWriter _writer let us to save the recording to a file.

Now let’s add a function StartRecAudio and inside it put:

1
2
3
4
5
6
7
if (_isRecording)
{
    return;
}
_isRecording = true;
_capture = new WasapiLoopbackCapture();
_writer = new WaveFileWriter("Audio.wav", _capture.WaveFormat);

_capture = new WasapiLoopbackCapture(); This allow us to record from the system default device.
_writer = new WaveFileWriter("Audio.wav", _capture.WaveFormat); This create the file that will store the audio.
Then add:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
_capture.DataAvailable += (s, a) =>
{
    _writer.Write(a.Buffer, 0, a.BytesRecorded);
};
_capture.RecordingStopped += (s, a) =>
{
    _writer.Dispose();
    _waveOut.Dispose();
    _writer = null;
    _capture.Dispose();
    _isRecording = false;
};
_capture.StartRecording();

To create the events handlers for the DataAvailable and RecordingStopped we used a lambda expressions, another way to write this without lambda expression is this:

1
2
3
4
5
6
7
_capture.DataAvailable += FunctionName;

//outside the main function
void FunctionName(object s, WaveInEventArgs a)
{
	_writer.Write(a.Buffer, 0, a.BytesRecorded);
}

In this case, we didn’t write so much, so using lambda expressions make the code more compact, but if you don’t like them you don’t have to.
Inside DataAvailable we’re writing to the file every sound that is played on the computer, so if no sound is played it will not record the “silence”, we’re going to fix this later.
Inside RecordingStopped we dispose everything.
_capture.StartRecording() This will simply start to record the audio.
As I tell earlier, if no sound is played on the system the recorder will pause, to let the program record even when nothing is played we’ll play a silence audio.
To do that add:

1
2
Thread thread = new Thread(PlaySilence);
thread.Start();

This will create a new thread that will execute the function PlaySilence, create that function and iside it write:

1
2
3
4
5
6
7
8
var silence = new SilenceProvider((new SignalGenerator() { Frequency = 1000, Gain = 0.2 }).WaveFormat).ToSampleProvider();
_waveOut = new WaveOutEvent();
_waveOut.Init(silence);
_waveOut.Play();
while (_waveOut.PlaybackState == PlaybackState.Playing)
{
    Thread.Sleep(500);
}

With this code we created the silence, then we created a new WaveOutEvent and we played it.
All this code allow us to record the audio even when nothing is played because this silence will always trigger the DataAvailable event.

The last function of the class that we need to create is StopRecAudio, inside it we need to write:

1
2
3
4
5
if (!_isRecording)
{
    return;
}
_capture.StopRecording();

Form Code

Now we can add the code to the form.
First, let’s add the 2 classes that we’ve created.

1
2
private AudioRecorder audioRec = new AudioRecorder();
private VideoRecorder videoRec = new VideoRecorder();

Now inside the “start rec” button write:

1
2
audioRec.StartRecAudio();
videoRec.StartRecVideo();

Inside the “stop rec” button put:

1
2
audioRec.StopRecAudio();
videoRec.StopRecVideo();

In the last button we’ll run a batch file that will execute FFMPEG.
To download FFMPEG go here: https://ffmpeg.org
Copy the 3 .exe file from the FFMPEG and put them inside your working directory(debug or release).
Now create a .bat file(I call it file.bat) and inside it write:

1
2
ffmpeg -i video.mp4 -i audio.wav -filter:v fps=fps=60 output.mp4
pause

Let’s add the code to the third button:

1
2
3
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "file.bat";
proc.Start();

What’s happening

When we click the “rec button” we’re recording the video with LibVLCSharp and store it in a Video.mp4 file, but we’re also recording the audio with NAudio and store it inside the Audio.wav file(Remeber both files will be overwritten if you start a new recording).
When we stop the recording we have the audio and the video in separate files, to merge them into one we’ve used FFMPEG, and we’ve created an output.mp4 file(if the file exist it will ask to you if you want to overwrite the file).

You can look at the full source code below.

Source Code

Form1.Designer.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
namespace ScreenRecorder
{
    partial class Form1
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.button3 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "Start Rec";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(93, 12);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 1;
            this.button2.Text = "Stop Rec";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // button3
            // 
            this.button3.Location = new System.Drawing.Point(174, 12);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(101, 23);
            this.button3.TabIndex = 2;
            this.button3.Text = "Make Single File";
            this.button3.UseVisualStyleBackColor = true;
            this.button3.Click += new System.EventHandler(this.button3_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(290, 45);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button3;
    }
}

Form1.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ScreenRecorder
{
    public partial class Form1 : Form
    {
        private AudioRecorder audioRec = new AudioRecorder();
        private VideoRecorder videoRec = new VideoRecorder();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            audioRec.StartRecAudio();
            videoRec.StartRecVideo();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            audioRec.StopRecAudio();
            videoRec.StopRecVideo();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.FileName = "file.bat";
            proc.Start();
        }
    }
}

VideoRecorder.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibVLCSharp.Shared;

namespace ScreenRecorder
{
    class VideoRecorder
    {

        public LibVLC _libVLC;
        public MediaPlayer _mp;
        public Media _media;
        public bool _isRecording = false;
        public VideoRecorder()
        {
            Core.Initialize();
            _libVLC = new LibVLC("--verbose=2");
            _mp = new MediaPlayer(_libVLC);
            _media = new Media(_libVLC, "screen://", FromType.FromLocation);
        }

        public void StartRecVideo()
        {
            if(_isRecording)
            {
                return;
            }
            _media.AddOption(":screen-fps=60");
            _media.AddOption(":sout=#transcode{vcodec=h264,sfilter=marq:logo}:file{dst=Video.mp4}");
            _media.AddOption(":screen-width=1920:screen-height=1080");
            _media.AddOption(":screen-mouse-image=Mouse.png");
            _isRecording = true;
            _mp.Play(_media);

        }

        public void StopRecVideo()
        {
            if (!_isRecording)
            {
                return;
            }
            _mp.Stop();
            _isRecording = false;
        }
    }
}

AudioRecorder.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;

namespace ScreenRecorder
{
    class AudioRecorder
    {

        public IWaveIn _capture;
        public WaveOutEvent _waveOut;
        public WaveFileWriter _writer;
        public bool _isRecording = false;

        public void StartRecAudio()
        {
            if (_isRecording)
            {
                return;
            }
            _isRecording = true;
            _capture = new WasapiLoopbackCapture();
            _writer = new WaveFileWriter("Audio.wav", _capture.WaveFormat);
            _capture.DataAvailable += (s, a) =>
            {
                _writer.Write(a.Buffer, 0, a.BytesRecorded);
            };
            _capture.RecordingStopped += (s, a) =>
            {
                _writer.Dispose();
                _waveOut.Dispose();
                _writer = null;
                _capture.Dispose();
                _isRecording = false;
            };
            _capture.StartRecording();
            
            Thread thread = new Thread(PlaySilence);
            thread.Start();
        }

        public void StopRecAudio()
        {
            if (!_isRecording)
            {
                return;
            }
            _capture.StopRecording();
        }

        public void PlaySilence()
        {
            var silence = new SilenceProvider((new SignalGenerator() { Frequency = 1000, Gain = 0.2 }).WaveFormat).ToSampleProvider();
            _waveOut = new WaveOutEvent();
            _waveOut.Init(silence);
            _waveOut.Play();
            while (_waveOut.PlaybackState == PlaybackState.Playing)
            {
                Thread.Sleep(500);
            }
        }
    }

}

Conclusion

In this tutorial we learned how to:

That’s a lot of things, if you want further explanations contact me on Twitter 👍
Did you know that I used this program to record the video of the “video player” tutorial?

Did you like this tutorial?
Did you find it useful?
Let me know on Twitter! @CodeSailer