# VBForums CodeBank > CodeBank - Visual Basic .NET >  VB.NET -  MPEG-4 Screen recorder. May 23rd, 2015 update

## Peter Porter

Created with Visual Basic 2010, using FFmpeg to compile captured images & audio to an MPEG-4 video. It's not perect, but when I have the time, I 'll tweak it some more.



* Adjusts frames per second (fps) and capture interval according to screen resolution's total pixels. Screen resolutions with a pixel total equal to or less than 921600 (1280x720) are captured at 30fps. Larger pixel totals (1280x1024, 1920x1080, etc...) are captured at 25fps.

* The "Crop to 16.9" checkbox is only visible if your screen resolution is not 16.9 aspect ratio.

* If "Record Mouse" is checked, along with "Crop to 16.9", the capture region follows the mouse according to actual screen bounds height.

* Creates a Folder called "Captured", which is placed in Window's system's "My Videos" folder.

* Extracts FFmpeg from resources and places it in the captured folder. FFmpeg, with dshow, captures audio from the speakers and saves it as an mp3 file, while merging captured images to a temporary mp4. Clicking "Stop" restarts FFmpeg to merge both to a dated mp4 in the captured folder.

* All captured videos are given unique Date & Time filenames.  

*The code had to be reposted since I exceeded the character limit here. You can find it below this thread, posts labeled May 23rd, part 1 thru 4.*

*Important:*
For this project to work, you need to download either the 32bit or 64bit FFmpeg static builds, which needs to be added to this project's resources. The direct links above to the ffmpeg static build downloads for Windows are from it's parent site, Zeranoe FFmpeg. They are older versions, but work fine compared to the latest version which is buggy.

*Your system's Stereo Mix has to be enabled in order for this app to record sound.*

Enabling Stereo Mix for Windows Vista, 7 and 8
   * Right-click over the Speaker icon by the system clock then choose Recording Devices to open the Recording tab of "Sound". 
   * Right-click Stereo Mix, and from it's dropdown menu, click Enable.

Note (Vista, 7 and 8):
If you don't see Stereo Mix, right click within the Recording tab window, then check "Show Disabled Devices".

Enabling Stereo Mix for Windows XP
   * Click the Speaker icon by the system clock.
   * In the window that appears, choose Options then Properties.
   * In the next window, click the "Recording" radio button, then in "Show the following volume controls", click in the box for Stereo Mix, then "OK". 


*This project's zip is clean of binaries, but it only works on 64bit systems.
You can easily change it to 32bit in VB's Configuration Manager.*

Don't forget to add FFmpeg static build (32bit or 64bit) to this project's resources.

----------


## Nightwalker83

Maybe you should attach the project to the first post? I for one would have difficulty whether something is 98 pixels away from something else.

----------


## Peter Porter

Hi Nightwalker83

I've attached a working project file to my first post. The code and project file above was extracted from the main body of my application which is why it doesn't look great, but it works. I'll gradually add more features as I clean-up my main application's code.

Everything works, except for the fact that the VB Timer is way off. Wish I knew a way to incorporate a High Resolution Timer.

----------


## Peter Porter

Ok... I thought of a way of capturing more frames per second. The save function needs to be placed in two backgroundworkers called from the VidRec timer. The VidRec timer will call a save from the first backgroundworker, then configure itself to call the next save from the second backgroundworker, then back again. One backgroundworker will be saving images with odd numbers, and the other even, this way all saved images from both backgroundworkers can be played together in a picturebox.

I'll even test the capture function within a backgroundworker to see if it's faster than a timer. Maybe split this function between 2 or 3 backgroundworkers. 

As soon as this project is updated, I'll repost the source code as well as a new dated zip file.

----------


## sunnydsouza

> Ok... I thought of a way of capturing more frames per second. The save function needs to be placed in two backgroundworkers called from the VidRec timer. The VidRec timer will call a save from the first backgroundworker, then configure itself to call the next save from the second backgroundworker, then back again. One backgroundworker will be saving images with odd numbers, and the other even, this way all saved images from both backgroundworkers can be played together in a picturebox.
> 
> I'll even test the capture function within a backgroundworker to see if it's faster than a timer. Maybe split this function between 2 or 3 backgroundworkers. 
> 
> As soon as this project is updated, I'll repost the source code as well as a new dated zip file.


Hi Peter, sorry to bump into this old thread but am working on a similar tool and was wondering whether you were able to implment the above and get some decent improvement? I couldnt find a newer post with an updated version...so thought to ask..
Currently I am able to generate screenshots and even capture the audio seperately. I can combine them using ffmpeg but I find that the audio/video is out of sync (video lags in coparison to audio)   :Frown: . This might be because my application is not able to produce correct number of screenshots(though i have set video timer to 10). I plan to use the backgroundworker method. Could you please share the updated code please?

----------


## Peter Porter

Hi Sunny,

I have an older version of this program that captures at the correct interval... well it's a bit over the number of frames it should be capturing, which is an easy fix by deleting the few duplicate frames it captures by knowing when this occurs, that's if you can figure out how to prevent it from crashing. It crashes after 600 to 700 frames. When it crashes, you can't save the images. Haven't had time to correct this, which is why I made the updated version attached above which is great for capturing tutorials, then you would have to use some video editing software to sync the audio with it. Could also be done with ffmpeg.

With the above attached version, the saving of each frame slows it down. Worse when you try to capture a video. As soon as I can dig up the old version, I'll attach it to this post. Maybe you can fix it.

----------


## Peter Porter

Updated the Screen Recorder. It now captures at 30fps while recording audio.

----------


## sunnydsouza

> Updated the Screen Recorder. It now captures at 30fps while recording audio.


Thanks peter. However the code in the zip file you provided doesnt open up in my Visual Basic 2008... Could you explain in short the logic as to how to achieved to capture frames at such high fps?

My code cant capture more than 9-10 fps  :Frown:

----------


## Peter Porter

Hi sunnydsouza,

I attached some code to this post, but it's been updated as well as the zipped files in 32bit and 64bit versions. My Screen Recorder now converts straight to MPEG-4 video. Hopefully the code above works for your version of Visual basic. Just one thing, Vidrec, Player and AudioRec are Timers. I haven't updated the player yet, so it will only play audio for now.

----------


## sunnydsouza

> Hi sunnydsouza,
> 
> I attached some code to this post, but it's been updated as well as the zipped files in 32bit and 64bit versions. My Screen Recorder now converts straight to MPEG-4 video. Hopefully the code above works for your version of Visual basic. Just one thing, Vidrec, Player and AudioRec are Timers. I haven't updated the player yet, so it will only play audio for now.


Thanks a lot Peter. I will check this out. Just out of curiosity - Does the video created by ffmpeg capture the audio also with it? And if so, are they both in sync?

Thanks a lot again.

----------


## Peter Porter

It doesn't merge audio with video yet, but I'll have that fixed soon.

I've updated the code again so the video and audio are saved in the same folder called Captured which this program creates in the MyVideos or Videos folder.

----------


## Peter Porter

It doesn't merge audio with video yet, but I'll have that fixed soon.

I've updated the code again so the video and audio are saved in the same folder called Captured which this program creates in the MyVideos or Videos folder.

----------


## Nightwalker83

Is it possible to add an option to capture the mouse cursor to?

----------


## ankan

Awesome but i have one more question and that's - how to change the video codec (e.g for 3gp, avi, wmv, mkv) ?

----------


## mrhydn

Hi Peter,

I use your code (64 bit. Because My OS is 64 bit). I can Record Screen Activities when I click stop button. But, I'm recording about 2 minutes but recorded file (test.mp4) is about 22 second. It's very fast.
I just want to record video. I don't have record audio. By the way, the recorded file size haven't very big. It must be small.

This is my convert code line.

proc.StartInfo.Arguments = "-f image2pipe -i pipe:.bmp -pix_fmt yuv420p -vcodec libx264 -r 25 -bufsize 3000k -b:v 2600k -y " + s + "\Captured\test.mp4"

This is VidREC_Tick Sub:

Dim writer As New BinaryWriter(proc.StandardInput.BaseStream)

        Frames = Format(fcounter, "000000")
        Label3.Text = Frames

        fcounter = fcounter + 1

        Dim BMP As New System.Drawing.Bitmap(MonitorWidth, MonitorHeight)
        Dim Cap As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(BMP)
        Cap.CopyFromScreen(New Point(0, 0), New Point(0, 0), ScreenSize)
        BMP.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp)

        Label3.Text = Convert.ToString(Frames)

        If Label1.Text = "stop" Then
            VidREC.Enabled = False
            ComboBox1.SelectedIndex = 2
            writer.Close()

        End If
        BMP.Dispose()
        Cap.Dispose()

        GC.Collect()


How can I set  time of original record between time of recorded video?(Duration)

Thanks.
Emrah

----------


## Peter Porter

Sorry for the wait, mrhydn, Nightwalker83, ankan. I have another version of my screen recorder that merges audio with the video. I created back in January before I dropped this project. The video plays back slow, or sometimes the frame rate fluctuates, so it's not in sync. The audio might be 5 minutes long, but the video might end up being over 5 minutes, or just a little under. I'll attach an update soon.

----------


## Edgemeal

> I thought I corrected the frame rate. Maybe it runs fast depending on the system. Works great on my pc. When I have time I'll look over my program again.


First of all thanks for sharing!

I would love to see a fix for the playback speed, I've tried all sorts of command line options for ffmpeg but the output file was always playing about 2x faster then the real capture time.

*EDIT*  UPDATE 

I changed my experiment code again, capture at lower FPS, (still a lot of junk in this, bunch of DoEvents,etc..)
This captures the mouse pointer and draws a red circle around the mouse pointer when left mouse button is down.

----------


## Inferrd

@Edgemeal: Nice work on the screen grab code.  :Thumb: 

From your timings, it looks like you are hoping for 75 fps (13.33ms timing loops). Might be a tad optimistic? The best I can achieve is about 18 fps for a screen grab and stream to FFmpeg process with a screen resolution of 1920 x 1080.

I think half the problem stems from the fps values, both those expected and those that are passed as settings to FFmpeg.

For example, let's say I was specifying 30 fps for the video playback rate in the call to FFmpeg

```
proc.StartInfo.Arguments = "-f image2pipe -i pipe:.bmp -pix_fmt yuv420p -c:v libx264 -an -r 30 -bufsize 60000k -b:v 1800k -y -threads 2 " & CaptureDir & "test.mp4"
```

but I was only able to grab screencaps at 15fps. Then that would account for the doubling in speed on playback.

You might think the obvious solution would be to cap the screen grab rate at a value that your hardware can handle, and set the playback rate via FFmpeg to the same value. That would be too easy  :Smilie: .  

I capped the grab rate to 12.5 fps (timing loop = 80ms) and passed -r 12.5 to FFmpeg. After recording for 60 seconds, the code had grabbed 737 frames (should have been 750; the grab rate is not consistent, with about 5% of screencaps taking longer than 80ms.) The output video file played back at 12.5 fps as expected, but only contained 371 frames. So at 12.5 fps, 371 frames play back in 29 seconds. That's still twice the expected speed, so matching frame rate to grab rate hasn't worked.

After a bit of research, it seems you can pass two frame rates to FFmpeg. From the documentation (end of section 2):


> To force the frame rate of the input file (valid for raw formats only) to 1 fps and the frame rate of the output file to 24 fps:
> 
> ffmpeg *-r 1* -i input.m2v *-r 24* output.avi


 Quite how that applies to streaming a bitmap, I don't know, but it does have an affect on the results.

After a fair bit of trial and error and a lot of observation, I deduced the following points:
If you don't specify a value for the input rate, it defaults to 25.
If you don't specify a value for the output rate, it defaults to the input rate, or if no input rate has been specified, the input rate's default value.
The actual number of frames present in the output video seems to be derived as:

num frames in video = num screen caps * ( output rate / input rate )
From that last point, it can be shown (I won't bore you with the maths) that the undesired speed factor is given by:

speed factor = input rate / screen grab rate
So in the above example, where I hadn't specified an input rate, it will have defaulted to 25fps, the screen grab rate was capped at 12.5 fps, which gives:

speed factor = 25 / 12.5 = 2
and indeed, the video payed twice as fast as I expected.


Another example:

Screen grab rate capped at 12.5fps (80ms timing loop)
Grabbed 744 frames in 60 seconds
Actual Screen Grab rate = 744 / 60 = 12.4fps

Input rate set = 18fps
Output rate set = 12.5fps

and here come the calculations:

Expected frames in video = num screen grabs * ( output rate / input rate )
 = 744 * ( 12.5 / 18 ) = 517

Speed factor = input rate / screen grab rate = 18 / 12.4 = 1.4516
which gives expected video run time of 60 / 1.4516 = 41.3337 seconds.
And these values compare well to the actual values for the video produced:

video contains 518 frames,
playback rate = 12.5 fps,
run time = 41.440 seconds.

So my current thinking would be to cap the screen grab rate at a value that the PC hardware can comfortably cope with (12.5 to 15 fps for my PC @ 1920x1080 screen res.), ony pass the input rate as an argument to FFmpeg (which will then be used by default as the output rate), and as the input rate value, use the same value as the screen grab rate is capped to.


```
proc.StartInfo.Arguments = "-r 12.5 -f image2pipe -i pipe:.bmp -pix_fmt yuv420p -c:v libx264 -an -bufsize 60000k -b:v 1800k -y -threads 2 " & CaptureDir & "test.mp4"
```

As long as the input rate always equals the screen grab rate, then the speed factor (input rate / screen grab rate ) will always be 1.


I should add that I know nothing about FFmpeg, and there may well be a command line that does all this in a sensible manner.  I should read the documentation for FFmpeg, but there's far too much of it for me to do that at the moment. Perhaps someone knows of a simpler method, and would be willing to share?

----------


## Edgemeal

> @Edgemeal: Nice work on the screen grab code.


Thanks for checking it out, I know I tried what you did and was just mad I couldn't get more FPS, I give up this for now.

btw, I updated post #17 for anyone interested in the way I add the mouse pointer and red circle to the captures.

*UPDATE* Did a couple quick timing test @ 1920x1080, my capture takes about 24ms, and the write to ffmpeg is another 24-30ms, and every so often there is a spike where it adds another 20~30ms to the total time. I just assumed writing to ffmpeg would be super fast.

Also tried saving the bmp captures directly to files on the fly and it only takes about 5.5 ~ 5.7m to save them.

----------


## Peter Porter

Hi Edgemeal. I do have a fix that I created back in January. It's not perfect. Sometimes the video plays back too slow, or the frame rate fluctuates, so it's never in sync. Basically it merges the saved video with the saved audio, but maybe if I could merge both together while recording, it could improve the syncronization. I'm gonna play with it a bit now and upload this version, maybe tonight.

----------


## Peter Porter

Great screen recorder, Edgemeal!

I posted an update, but I'm gonna rewrite it using both of our ideas. My project is still not perfect. It's records YouTube fullscreen videos almost in sync, but if you're not recording videos, then it's grossly out of sync. Gonna add options to where a user can set what kinda of capture is going to be performed so the program could adjust to a set frame rate.

What I'm trying to workout now is to play the created video in a user's system's default Media Player. It's currently broke.

----------


## Edgemeal

Was playing with your july 14 code, not much difference, playback is still too fast, On my pc calls to ffmpeg are taking about 30ms, and screenshot (@ 1920x1080) adds another 25ms+, so time to capture a frame might take 60ms which already limits me to 16.6FPS, so for quick test I changed both -r commands to -r 15 and playback speed was about perfect, but 15FPS is just too choppy for capturing video, So its really not your code, my PC just cant do a screenshot and also call ffmpeg fast enough to do decent FPS captures.

----------


## JoeDeGeek

Capture2Vid.zip

I just tried editing Peter Porter's project a bit... 

 With new skin and few extra functions.. 
   1. Recording Mouse pointer too.
   2. Displaying Left and Right mouse button clicks.

 And an embedded Windows Media Player.  :Cool:  :Cool: 

 Note: I havent added the ffmpeg.exe in this pakage.also i have compressed it two times cos of size restrictions.

----------


## Peter Porter

Great job, JoeDeGeek!

I'll have my latest version uploaded soon for you to tweak. Still looks the same, but with a few extras, a whole lot better than before.

----------


## Shohag_ifas

> First of all thanks for sharing!
> I would love to see a fix for the playback speed, I've tried all sorts of command line options for ffmpeg but the output file was always playing about 2x faster then the real capture time.


Dear sir, greetings. hope you are fine

sir, i have tried your sample/example:

And this is my modified code:


```
        Dim objBitmap As System.Drawing.Bitmap
        Dim objFProcess As System.Diagnostics.Process
        Dim objStream As System.IO.BinaryWriter
        Dim strFiles() As String
        Dim strEachFile As String
        Dim intFile As Integer
        Dim intFrame As Integer
        Dim intFLoop As Integer
        objFProcess = New System.Diagnostics.Process
        objFProcess.StartInfo.FileName = "c:\ffmpeg.exe"
        objFProcess.StartInfo.Arguments = "-r 1 -f image2pipe -i pipe:.bmp -pix_fmt yuv420p -crf 35.0 -vcodec libx264 -an -coder 1 -rc_lookahead 50 -threads 0 D:\test.mp4"
        objFProcess.StartInfo.UseShellExecute = False
        objFProcess.StartInfo.RedirectStandardInput = True
        objFProcess.StartInfo.RedirectStandardOutput = True
        objFProcess.StartInfo.RedirectStandardError = True
        'objFProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        'objFProcess.StartInfo.CreateNoWindow = True
        objFProcess.Start()
        objStream = New System.IO.BinaryWriter(objFProcess.StandardInput.BaseStream)
        strFiles = My.Computer.Clipboard.GetText().Split({System.Environment.NewLine}, StringSplitOptions.None)
        intFrame = 0
        intFile = 0
        For Each strEachFile In strFiles
            'MessageBox.Show(strEachFile)
            intFile = intFile + 1
            objBitmap = New System.Drawing.Bitmap(strEachFile)
            For intFLoop = 0 To 24
                intFrame = intFrame + 1
                cmd1.Text = intFile.ToString & "-" & intFrame.ToString
                My.Application.DoEvents()
                objBitmap.Save(objStream.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp) Rem this line give me below (attached) error :(
            Next
            objBitmap.Dispose()
        Next
        System.Threading.Thread.Sleep(3000)
        objStream.Close()
        MessageBox.Show("Done!")
        'objFProcess.Kill()
```

And Here is the error:
Attachment 123451

FYI your sample also raise same error  :Frown: 

can you please help me out?

thanks in advance....

best regards

----------


## Peter Porter

I've attached the screen capture zipped project file in my first post above. Capture is now done with a background worker.



```
Option Strict On

Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Threading
Imports System.Drawing
Imports System.Drawing.Image
Imports System.Runtime.InteropServices

Public Class Form1

    'dll import and initializing function needed for recording and saving audio
    <DllImport("winmm.dll")> _
    Private Shared Function mciSendString(ByVal command As String, ByVal buffer As String, ByVal bufferSize As Integer, ByVal hwndCallback As IntPtr) As Integer
    End Function

    'for dragging form
    Dim drag As Boolean
    Dim a As Integer
    Dim b As Integer

    'to get screen resolution
    Dim MW As Integer = Screen.PrimaryScreen.Bounds.Width
    Dim MH As Integer = Screen.PrimaryScreen.Bounds.Height
    Dim MH2 As Integer
    Public ScreenSize As Size = New Size(MW, MH)
    Dim bdr As Integer = 152
    Dim bdr2 As Integer

    'get mouse position, and set it's bitmap
    Dim mousex As Integer
    Dim mousey As Integer
    Dim mousecursor As Bitmap

    'for the ffmpeg processes, started in Record button sub
    Dim proc As New Process
    Dim proc2 As New Process

    'for the ffmpeg merge process, located in BW2 (background worker)
    Dim proc3 As New Process

    'for the play process
    Dim proc4 As New Process

    'Used within the video recording Backgroundworker sub, renamed BW1
    Dim audiostr As String = "Stereo Mix (Realtek High Defini"
    Dim istr As FileStream
    Dim stopwatch1 As New Stopwatch
    Dim stopwatch2 As New Stopwatch
    Dim fcounter As Integer = 1
    Dim Frames As String

    'for date and time in final video's filename
    Dim strDate As String

    'timer for 2nd Backgroundworker, BW2
    Dim stopwatch3 As New Stopwatch

    'directory path to system's video folder where Captured folder will be created
    Dim dir As String = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos)


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'creates required folder for captured video and audio if it doesn't already exist
        If My.Computer.FileSystem.DirectoryExists(dir) = False Then
            Throw New Exception("The specified Directory doesn't exist. Please check it's spelling.")
        ElseIf My.Computer.FileSystem.DirectoryExists(dir & "\" & "\Captured") Then
        Else
            My.Computer.FileSystem.CreateDirectory(dir & "\" & "\Captured")
        End If

        MH2 = MH
        'set crop of capture if the primary screen height is 1024
        If MW = 1280 And MH = 1024 Then
            MH2 = 720
        Else
        End If

    End Sub

    'next 3 subs is for dragging the application
    Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
        drag = True
        a = Windows.Forms.Cursor.Position.X - Me.Left
        b = Windows.Forms.Cursor.Position.Y - Me.Top
    End Sub


    Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If drag Then
            Me.Top = Windows.Forms.Cursor.Position.Y - b
            Me.Left = Windows.Forms.Cursor.Position.X - a
        End If
    End Sub


    Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        drag = False
    End Sub


    Private Sub Record_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Record.Click
        CloseButton.Enabled = False
        StopRec.Enabled = True
        Play.Enabled = False
        bdr = 152
        stopwatch2.Reset()

        'starts ffmpeg process for mp3 capture
        proc.StartInfo.FileName = Application.StartupPath + "\ffmpeg.exe"
        proc.StartInfo.Arguments = String.Format("-f dshow -i audio=""" + audiostr + """ -acodec libmp3lame -b:a 128k -y " + dir + "\Captured\CapturedAudio.mp3")
        proc.StartInfo.UseShellExecute = False
        proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        proc.StartInfo.RedirectStandardInput = True
        proc.StartInfo.RedirectStandardOutput = True
        proc.StartInfo.CreateNoWindow = True
        proc.Start()

        'starts ffmpeg process for captured frames
        proc2.StartInfo.FileName = Application.StartupPath + "\ffmpeg.exe"
        proc2.StartInfo.Arguments = String.Format("-f image2pipe -r 30 -i pipe:.bmp -pix_fmt yuv420p -c:v libx264 -crf 18 -g 1 -y -r 30 " + dir + "\Captured\Temp.mp4")
        proc2.StartInfo.UseShellExecute = False
        proc2.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        proc2.StartInfo.RedirectStandardInput = True
        proc2.StartInfo.RedirectStandardOutput = True
        proc2.StartInfo.CreateNoWindow = True
        proc2.Start()

        Label4.Text = "Recording"

        BW1.RunWorkerAsync()

        Record.Enabled = False
    End Sub


    Private Sub StopRec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopRec.Click
        strDate = DateTime.Now.ToString("ddMMMyyyy_hhmmss")
        CloseButton.Enabled = True
        Label4.Text = "Saving..."
        MediaMerger.Enabled = True
        Play.Enabled = True
        Record.Enabled = True
    End Sub


    Private Sub Play_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Play.Click
        proc4.StartInfo.FileName = dir + "\Captured\Movie.mp4"
        proc4.Start()
    End Sub


    Private Sub MinimizeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MinimizeButton.Click
        Me.WindowState = FormWindowState.Minimized
    End Sub


    Private Sub CloseButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CloseButton.Click
        Me.Close()
    End Sub


    'delegate & sub used by BW2 (backgroundworker)
    Delegate Sub SetLabelTextInvoker(ByVal label As Label, ByVal Text As String)

    Sub SetLabelText(ByVal Label As Label, ByVal Text As String)
        If Label4.InvokeRequired = True Then
            Label4.Invoke(New SetLabelTextInvoker(AddressOf SetLabelText), Label, Text)
        Else
            Label4.Text = Text
        End If
    End Sub


    'delegate & sub used by BW1 (backgroundworker)
    Delegate Sub SetLabelTextInvoker2(ByVal label As Label, ByVal Text As String)

    Sub SetLabelText2(ByVal Label As Label, ByVal Text As String)
        If Label6.InvokeRequired = True Then
            Label6.Invoke(New SetLabelTextInvoker2(AddressOf SetLabelText2), Label, Text)
        Else
            Label6.Text = Text
        End If
    End Sub


    'backgroundworker for capturing images and relaying them to ffmpeg for video creation
    Private Sub BW1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW1.DoWork
        On Error GoTo error_handler2

        

        Dim writer As New BinaryWriter(proc2.StandardInput.BaseStream)

        For i As Integer = 0 To 1000000

            stopwatch1.Start()
            stopwatch2.Start()
            Frames = Format(fcounter, "000000")

            Dim BMP As New System.Drawing.Bitmap(MW, MH2, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
            Dim Cap As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(BMP)
            Cap.CopyFromScreen(New Point(0, bdr), New Point(0, 0), ScreenSize, System.Drawing.CopyPixelOperation.SourceCopy)




            'if checkbox is checked, then the mouse will be recorded
            If CheckBox1.Checked = True Then
                mousex = Cursor.Position.X
                mousey = Cursor.Position.Y - bdr
                mousecursor = New Bitmap(My.Resources.mouse)
                Dim g As Graphics = Graphics.FromImage(BMP)
                g.DrawImage(mousecursor, mousex, mousey)

                If MW = 1280 And MH = 1024 Then
                    'capture region follows the mouse
                    If mousey <= bdr + 30 Then
                        bdr = CInt(mousey - 30)
                        If bdr < 0 Then
                            bdr = 0
                        Else
                        End If
                    Else
                        If mousey >= 690 Then
                            bdr2 = mousey + bdr
                            bdr = bdr2 - mousey
                            bdr = bdr + 30
                            If bdr >= 304 Then
                                bdr = 304
                            Else
                            End If
                        Else
                        End If
                    End If

                Else
                End If
            Else
            End If


            BMP.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp)

            If Label4.Text = "Saving..." Then
                BMP.Dispose()
                Cap.Dispose()
                GC.Collect()
                writer.Close()
                stopwatch1.Stop()
                stopwatch2.Stop()
                proc.StandardInput.WriteLine("q")
                BW1.CancelAsync()
            Else
                BW1.ReportProgress(i)
            End If

            fcounter = fcounter + 1
            i = i + 1

            BMP.Dispose()
            Cap.Dispose()
            GC.Collect()

            'below code displays time during capture
            'uses SetLabelTextInvoker2 delegate, SetLabelText2 sub, label6 & custom stopwatch (timer2)
            SetLabelText2(Label6, CStr(stopwatch2.Elapsed.ToString("hh\:mm\:ss\.fff")))

            'Do Until Loop for ensuring capture interval, which is purposely not set to-
            '400000 (actual interval for 25fps), because the For Next loop wastes time during loop
            Do
            Loop Until stopwatch1.Elapsed.Ticks >= 333040
            stopwatch1.Reset()

        Next
error_handler2:
    End Sub


    Private Sub BW1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW1.ProgressChanged
        'for Captured Frames counter
        Label2.Text = Convert.ToString(Frames)
    End Sub


    'started by the MediaMerger timer, this backgroundworker detects if ffmpeg is done merging audio with video
    Private Sub BW2_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW2.DoWork
        For i As Integer = 0 To 1000000
            stopwatch3.Start()
            BW2.ReportProgress(i)
            If Process.GetProcessesByName("ffmpeg").Length > 0 Then
            Else
                stopwatch3.Stop()
                SetLabelText(Label4, "Done")
                BW2.CancelAsync()
                Exit Sub
            End If
        Next
    End Sub

    Private Sub BW2_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW2.ProgressChanged
    End Sub


    Private Sub MediaMerger_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MediaMerger.Tick
        'starts ffmpeg detector to detect if it's still running
        BW2.RunWorkerAsync()

        'stopwatch delayer to give enough time for captured audio and video to save before merge
        stopwatch1.Start()
        Do
        Loop Until stopwatch1.Elapsed.Ticks >= 999999
        stopwatch1.Stop()

        'merges the audio with the video, with a final date & Time filename, ex. 15Feb2015_124202.mp4
        proc3.StartInfo.FileName = Application.StartupPath + "\ffmpeg.exe"
        proc3.StartInfo.Arguments = String.Format("-r 30 -i " + dir + "\Captured\Temp.mp4 -i " + dir + "\Captured\CapturedAudio.mp3 -r 30 -c:v copy -c:a aac -async 30 -strict experimental -map 0 -map 1 -y " + dir + "\Captured\" + strDate + ".mp4")
        proc3.StartInfo.UseShellExecute = False
        proc3.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        proc3.StartInfo.RedirectStandardInput = True
        proc3.StartInfo.RedirectStandardOutput = True
        proc3.StartInfo.CreateNoWindow = True
        proc3.Start()

        StopRec.Enabled = False

        'resets Frames' counter
        fcounter = 1

        MediaMerger.Enabled = False
    End Sub
End Class
```

----------


## Peter Porter

The screen capture video recorder has been updated.

* It now has the option to record the mouse smoothly, which can also be selected or deselected during the capture.

* After capture, the final merged video has a unique Date & Time filename.

* Audio is captured to mp3, which is merged with the captured video automatically.

Hope you find this project useful!

----------


## Peter Porter

Updated the screen recorder.

* Mouse capture and display is now accurate.

* Captured region follows the mouse automatically, but only if the screen resolution is 1280x1024, which is cropped to 1280x720.

* Other tweaks.

----------


## ident

Some strange things going on with that example. Like for example you use an environment variable thats created by the system yet if it does not exist ask the user to check the spelling. How do they have any control over it?

----------


## Peter Porter

ident, I removed that check for that environment variable. It only checks for the Captured folder now.

I've made some other changes and fixes, but I'm gonna rebuild this whole project from scratch to see if that fixes one major issue. I can't publish a working installation executable. But the weird thing, from my updated app that I haven't posted to replace the above zip, if I copy the executable from the bin Debug folder to another folder outside the project and run it, my app works fine.

----------


## Peter Porter

*Code for April 10th, 2015 update*

Since it's too long to add to the top post, I have to split it between 4 posts:

From the Properties sidepanel:
* Set FormBorderStyle to None
* DoubleBuffered to True
* Size 645x72

Controls:
* 2 backgroundworkers named BW1 and BW2
* Timer named MediaMerger
* 5 Buttons named Record, StopRec, Play, MinimizeButton, CloseButton
* CheckBox: From it's properties, enter "Record Mouse" for Text
* 7 labels
Label 1's Text is "Captured Frames:"
Label 2's Text is "000000"
Label 3's Text is "Status:
Label 4's Text is "--------------"
Label 5's Text is "Time:"
Label 6's Text is "00:00:00.000"
Label 7's Text is "hr  min  sec  msec"

Extra forms used as regionbars:
*  2 forms named TopBar and BottomBar

Project's Resources:
* Mouse image, png or jpeg
* FFmpeg.exe - download either the 32bit or 64bit static builds and add ffmpeg.exe to resources.
* BackgroundImage for Topbar and Bottombar forms. (No image? Set their BackColor's to DarkSeaGreen)

Form1's code:


```
Option Strict On

Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Threading
Imports System.Drawing
Imports System.Drawing.Image
Imports System.Runtime.InteropServices

Public Class Form1

    ' P/Invoke declarations
    <DllImport("gdi32.dll")> _
    Private Shared Function BitBlt(ByVal hdcDest As IntPtr, ByVal xDest As Integer, ByVal yDest As Integer, ByVal wDest As Integer, ByVal hDest As Integer, ByVal hdcSource As IntPtr, _
   ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal rop As CopyPixelOperation) As Boolean
    End Function
    <DllImport("user32.dll")> _
    Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDc As IntPtr) As Boolean
    End Function
    <DllImport("gdi32.dll")> _
    Private Shared Function DeleteDC(ByVal hDc As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32.dll")> _
    Private Shared Function DeleteObject(ByVal hDc As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32.dll")> _
    Private Shared Function CreateCompatibleBitmap(ByVal hdc As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As IntPtr
    End Function
    <DllImport("gdi32.dll")> _
    Private Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) As IntPtr
    End Function
    <DllImport("gdi32.dll")> _
    Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal bmp As IntPtr) As IntPtr
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function GetDesktopWindow() As IntPtr
    End Function
    <DllImport("user32.dll")> _
    Public Shared Function GetWindowDC(ByVal ptr As IntPtr) As IntPtr
    End Function

    'for dragging form
    Dim drag As Boolean
    Dim a As Integer
    Dim b As Integer

    'to get screen resolution
    Dim MW As Integer = Screen.PrimaryScreen.Bounds.Width
    Dim MH As Integer = Screen.PrimaryScreen.Bounds.Height
    Dim AspectH As Integer = CInt(Screen.PrimaryScreen.Bounds.Width / 1920 * 1080)
    Dim totalp As Integer
    Public ScreenSize As Size = New Size(MW, MH)
    Dim bdr As Integer = 0
    Dim bdr2 As Integer

    'get mouse position, and set it's bitmap
    Dim mousex As Integer
    Dim mousey As Integer
    Dim mousecursor As Bitmap

    'for the ffmpeg processes, started in Record button sub
    Dim proc As New Process
    Dim proc2 As New Process

    'for the ffmpeg merge process, located in BW2 (background worker)
    Dim proc3 As New Process

    'for the play process
    Dim proc4 As New Process

    'for overwritting temp file before deletion
    Dim BMP3 As New Bitmap(1, 1)

    'Used within the video recording Backgroundworker sub (BW1)
    Dim audiostr As String = "Stereo Mix (Realtek High Defini"
    Dim istr As FileStream
    Dim stopwatch1 As New Stopwatch
    Dim stopwatch2 As New Stopwatch
    Dim fcounter As Integer = 1
    Dim Frames As String
    Dim rr As Integer
    Dim fps As Integer
    Dim CLoop As Integer

    'for date and time in final video's filename
    Dim strDate As String

    'timer for 2nd Backgroundworker, BW2
    Dim stopwatch3 As New Stopwatch

    'directory path to system's video folder where Captured folder will be created
    Dim dir As String = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos)


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim pProcess() As Process = System.Diagnostics.Process.GetProcessesByName("ffmpeg")

        For Each p As Process In pProcess
            p.Kill()
        Next


        'creates required folder for captured video and audio if it doesn't already exist
        If My.Computer.FileSystem.DirectoryExists(dir & "\" & "\Captured") = False Then
            My.Computer.FileSystem.CreateDirectory(dir & "\" & "\Captured")
        Else
        End If

        'delayer, to give enough time for the captured folder to be created
        stopwatch1.Start()
        Do
        Loop Until stopwatch1.Elapsed.Ticks >= 333040
        stopwatch1.Reset()
        stopwatch1.Stop()

        'extracts ffmpeg from resources to the captured folder
        If My.Computer.FileSystem.FileExists("C:\Users\Web\Videos\Captured\ffmpeg.exe") = False Then
            savefromresources("C:\Users\Web\Videos\Captured\ffmpeg.exe", My.Resources.ffmpeg)
        Else
        End If

        'application location at runtime
        Dim appx As Integer = MW
        Dim appy As Integer = MH
        appx = CInt((appx - 645) / 2)
        appy = CInt((appy - 72) / 2)
        Me.Location = New Point(appx, appy)

        'sets fps for ffmpeg, and BW1's (backgroundworker) capture interval loop
        totalp = MW * MH
        If totalp <= 921600 Then
            fps = 30
            CLoop = 333333
        Else
            fps = 25
            CLoop = 400000
        End If

        'if AspectH (primary screen's width / 1920 * 1080) is less than the
        'primary screen's height, the "Crop to 16.9" checkbox is set to visible
        If AspectH < Screen.PrimaryScreen.Bounds.Height Then
            CheckBox2.Visible = True
        Else
        End If

    End Sub


    Public Sub savefromresources(ByVal filepath As String, ByVal file As Object)
        Dim fbyte() As Byte = CType(file, Byte())
        My.Computer.FileSystem.WriteAllBytes(filepath, fbyte, True)
    End Sub


    'next 3 subs is for dragging the application
    Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
        drag = True
        a = Windows.Forms.Cursor.Position.X - Me.Left
        b = Windows.Forms.Cursor.Position.Y - Me.Top
    End Sub


    Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If drag Then
            Me.Top = Windows.Forms.Cursor.Position.Y - b
            Me.Left = Windows.Forms.Cursor.Position.X - a
        End If
    End Sub


    Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        drag = False
    End Sub


    Private Sub Record_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Record.Click
        CheckBox2.Enabled = False

        If CheckBox2.Checked = False And totalp > 921600 Then
            fps = 25
            CLoop = 400000
            MW = Screen.PrimaryScreen.Bounds.Width
            MH = Screen.PrimaryScreen.Bounds.Height
            bdr = 0
        Else
            fps = 30
            CLoop = 333040
            bdr = CInt((Screen.PrimaryScreen.Bounds.Height - AspectH) / 2)
        End If

        Me.WindowState = FormWindowState.Minimized

        'set crop of capture if the primary screen height is 1024
        If CheckBox2.Checked = True Then
            TopBar.Top = -152
            BottomBar.Top = Screen.PrimaryScreen.Bounds.Height - 152
            TopBar.Show()
            BottomBar.Show()
            TopBar.TopMost = True
            BottomBar.Size = New System.Drawing.Size(1280, 111)
            bdr = CInt((Screen.PrimaryScreen.Bounds.Height - AspectH) / 2)
            MH = AspectH
            If CheckBox1.Checked = True Then
                BottomBar.Size = New System.Drawing.Size(1280, 304)
                BottomBar.TopMost = True
            Else
            End If
        Else
        End If

        'overwritting/deleting temp mp4. Reason for this, sometimes capturing wont overwrite the temp mp4.
        Try
            BMP3.Save("C:\Users\Web\Videos\Captured\Temp.mp4")
        Catch ex As Exception

            BottomBar.Size = New System.Drawing.Size(1280, BottomBar.Height - 30)
            MsgBox("Temp.mp4 is in use. It must be free in order to capture.")
            Exit Sub
        End Try

        stopwatch1.Start()
        Do
        Loop Until stopwatch1.Elapsed.Ticks >= 333040
        stopwatch1.Reset()
        stopwatch1.Stop()
        System.IO.File.Delete("C:\Users\Web\Videos\Captured\Temp.mp4")

        CloseButton.Enabled = False
        StopRec.Enabled = True
        Play.Enabled = False
        stopwatch2.Reset()

        'starts ffmpeg process for mp3 capture
        proc.StartInfo.FileName = "C:\Users\Web\Videos\Captured\ffmpeg.exe"
        proc.StartInfo.Arguments = String.Format("-f dshow -i audio=""" + audiostr + """ -acodec libmp3lame -b:a 128k -y " + dir + "\Captured\CapturedAudio.mp3")
        proc.StartInfo.UseShellExecute = False
        proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        proc.StartInfo.RedirectStandardInput = True
        proc.StartInfo.RedirectStandardOutput = True
        proc.StartInfo.CreateNoWindow = True
        proc.Start()

        'starts ffmpeg process for captured frames
        proc2.StartInfo.FileName = "C:\Users\Web\Videos\Captured\ffmpeg.exe"
        proc2.StartInfo.Arguments = String.Format("-f image2pipe -r " & fps & " -i pipe:.bmp -pix_fmt yuv420p -c:v libx264 -crf 18 -g 1 -y -r " & fps & " " & dir & "\Captured\Temp.mp4")
        proc2.StartInfo.UseShellExecute = False
        proc2.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
        proc2.StartInfo.RedirectStandardInput = True
        proc2.StartInfo.RedirectStandardOutput = True
        proc2.StartInfo.CreateNoWindow = True
        proc2.Start()


       
        Label4.Text = "Recording"

        BW1.RunWorkerAsync()

        Record.Enabled = False
    End Sub


    Private Sub StopRec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopRec.Click
        CheckBox2.Enabled = True
        strDate = DateTime.Now.ToString("ddMMMyyyy_hhmmss")
        Label4.Text = "Saving..."
            TopBar.Hide()
            BottomBar.Hide()
            MediaMerger.Enabled = True
            'starts background worker to detect if the ffmpeg audio save process is still running
            BW2.RunWorkerAsync()
    End Sub


    Private Sub Play_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Play.Click
        proc4.StartInfo.FileName = dir + "\Captured\" & strDate & ".mp4"
        proc4.Start()
    End Sub


    Private Sub MinimizeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MinimizeButton.Click
        Me.WindowState = FormWindowState.Minimized
    End Sub


    Private Sub CloseButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CloseButton.Click
        Me.Close()
    End Sub
```

Continued in next post...

----------


## Peter Porter

...continued. Form1's code:


```
'delegate & sub used by BW2 (backgroundworker)
    Delegate Sub SetLabelTextInvoker(ByVal label As Label, ByVal Text As String)

    Sub SetLabelText(ByVal Label As Label, ByVal Text As String)
        If Label4.InvokeRequired = True Then
            Label4.Invoke(New SetLabelTextInvoker(AddressOf SetLabelText), Label, Text)
        Else
            Label4.Text = Text
        End If
    End Sub


    'delegate & sub used by BW1 (backgroundworker)
    Delegate Sub SetLabelTextInvoker2(ByVal label As Label, ByVal Text As String)

    Sub SetLabelText2(ByVal Label As Label, ByVal Text As String)
        If Label6.InvokeRequired = True Then
            Label6.Invoke(New SetLabelTextInvoker2(AddressOf SetLabelText2), Label, Text)
        Else
            Label6.Text = Text
        End If
    End Sub


    'backgroundworker for capturing images and relaying them to ffmpeg for video creation
    Private Sub BW1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW1.DoWork

        Try

            Dim writer As New BinaryWriter(proc2.StandardInput.BaseStream)

            For i As Integer = 0 To 1000000

                stopwatch1.Start()
                stopwatch2.Start()
                Frames = Format(fcounter, "000000")

              
                Dim hDesk As IntPtr = GetDesktopWindow()
                Dim hSrce As IntPtr = GetWindowDC(hDesk)
                Dim hDest As IntPtr = CreateCompatibleDC(hSrce)
                Dim hBmp As IntPtr = CreateCompatibleBitmap(hSrce, MW, MH)
                Dim hOldBmp As IntPtr = SelectObject(hDest, hBmp)
                Dim b As Boolean = BitBlt(hDest, 0, 0, MW, MH, hSrce, 0, bdr, CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                Dim bmp As Bitmap = Bitmap.FromHbitmap(hBmp)
                SelectObject(hDest, hOldBmp)
                DeleteObject(hBmp)
                DeleteDC(hDest)
                ReleaseDC(hDesk, hSrce)


                'if checkbox is checked, then the mouse will be recorded
                If CheckBox1.Checked = True Then
                    mousex = Cursor.Position.X
                    mousey = Cursor.Position.Y - bdr
                    mousecursor = New Bitmap(My.Resources.mouse)
                    Dim g As Graphics = Graphics.FromImage(BMP)
                    g.DrawImage(mousecursor, mousex, mousey)

                    If AspectH < Screen.PrimaryScreen.Bounds.Height And CheckBox2.Checked = True Then
                        'capture region follows the mouse
                        If mousey <= bdr + 30 Then
                            bdr = CInt(mousey - 30)
                            If bdr < 0 Then
                                bdr = 0
                            Else
                            End If
                        Else
                            If mousey >= 690 Then

                                bdr = bdr + 30
                                If bdr >= 304 Then
                                    bdr = 304
                                Else
                                End If
                            Else
                            End If
                        End If

                    Else
                    End If
                Else
                End If


                BMP.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Bmp)

                If Label4.Text = "Saving..." Then
                    BMP.Dispose()
                    'Cap.Dispose()
                    GC.Collect()
                    writer.Close()
                    stopwatch1.Stop()
                    stopwatch2.Stop()
                    proc.StandardInput.WriteLine("q")
                    BW1.CancelAsync()
                Else
                    BW1.ReportProgress(i)
                End If

                fcounter = fcounter + 1
                i = i + 1

                BMP.Dispose()
                'Cap.Dispose()
                GC.Collect()

                'below code displays time during capture
                'uses SetLabelTextInvoker2 delegate, SetLabelText2 sub, label6 & custom stopwatch (timer2)
                SetLabelText2(Label6, CStr(stopwatch2.Elapsed.ToString("hh\:mm\:ss\.fff")))

                'Do Until Loop for ensuring capture interval, which is purposely not set to-
                '400000 (actual interval for 25fps), because the For Next loop wastes time during loop
                Do
                Loop Until stopwatch1.Elapsed.Ticks >= CLoop
                stopwatch1.Reset()

            Next
        Catch ex As Exception
        End Try

    End Sub


    Private Sub BW1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW1.ProgressChanged
        'for Captured Frames counter
        Label2.Text = Convert.ToString(Frames)

        If CheckBox1.Checked = True Then
            BottomBar.Size = New System.Drawing.Size(1280, 304)
            If mousey <= bdr + 30 Then
                If bdr <= 0 Then
                    TopBar.Top = bdr - 304
                Else
                    TopBar.Top = bdr - 304
                    BottomBar.Top = bdr + 720
                End If
            Else
                If mousey >= 512 Then
                    If bdr >= 304 Then
                        BottomBar.Top = 1025
                    Else
                        TopBar.Top = bdr - 304
                        BottomBar.Top = bdr + 720
                    End If
                End If
            End If
        Else

        End If

        

    End Sub


    'started by the Stop button & MediaMerger timer, this backgroundworker detects if ffmpeg process is still running
    Private Sub BW2_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW2.DoWork
        For i As Integer = 0 To 1000000

            BW2.ReportProgress(i)
            If Process.GetProcessesByName("ffmpeg").Length > 0 Then
            Else

                SetLabelText(Label4, "Done")
                BW2.ReportProgress(i)
                BW2.CancelAsync()
                Exit Sub
            End If
        Next
    End Sub

    Private Sub BW2_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW2.ProgressChanged

        If Label4.Text = "Done" Then
            CloseButton.Enabled = True
            Play.Enabled = True
            Record.Enabled = True
        End If

    End Sub


    Private Sub MediaMerger_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MediaMerger.Tick

        If Label4.Text = "Done" Then
            Label4.Text = " Merging Media"

            stopwatch1.Start()
            Do
            Loop Until stopwatch1.Elapsed.Ticks >= 999999
            stopwatch1.Reset()

            'starts background worker again to detect if the ffmpeg merge process is still running
            BW2.RunWorkerAsync()

            'merges the audio with the video, with a final date & Time filename, ex. 15Feb2015_124202.mp4
            proc3.StartInfo.FileName = "C:\Users\Web\Videos\Captured\ffmpeg.exe"
            proc3.StartInfo.Arguments = String.Format("-r " & fps & " -i " & dir & "\Captured\Temp.mp4 -i " & dir & "\Captured\CapturedAudio.mp3 -r " & fps & " -c:v copy -c:a aac -async " & fps & " -strict experimental -map 0 -map 1 -y " & dir & "\Captured\" & strDate & ".mp4")
            proc3.StartInfo.UseShellExecute = False
            proc3.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
            proc3.StartInfo.RedirectStandardInput = True
            proc3.StartInfo.RedirectStandardOutput = True
            proc3.StartInfo.CreateNoWindow = True
            proc3.Start()

            StopRec.Enabled = False

            'resets Frames' counter
            fcounter = 1

            MediaMerger.Enabled = False
            
        Else
        End If

    End Sub

    Private Sub CheckBox2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox2.CheckedChanged
        fps = 30
        CLoop = 333040
    End Sub
End Class
```

Next to posts contain code and information for the regionbar forms (TopBar, BottomBar).

----------


## Peter Porter

*Code for TopBar form*

From the Properties sidepanel:
* Set FormBorderStyle to None
* DoubleBuffered to True
* BackColor to DarkSeaGreen
* Size 1280x304
* Opacity to 75%



```
Public Class TopBar

    Private Sub TopBar_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Location = New Point(0, -152)
        Me.TopMost = True
    End Sub
End Class
```

----------


## Peter Porter

*Code for BottomBar form*

From the Properties sidepanel:
* Set FormBorderStyle to None
* DoubleBuffered to True
* BackColor to DarkSeaGreen
* Size 1280x304
* Opacity to 75%



```
Public Class BottomBar

    Private Sub BottomBar_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Location = New Point(0, 874)
        Me.TopMost = True
    End Sub

End Class
```

----------


## Peter Porter

Latest version adjusts frames per second (fps) and capture interval according to screen resolution's total pixels. Screen resolutions with a pixel total equal to or less than 921600 pixels (1280x720) are captured at 30fps. Larger pixel totals (1280x1024, 1920x1080, etc...) are captured at 25fps.

It no longer automatically crops. You must check "Crop to 16.9". If "Record mouse" is checked, along with "Crop to 16.9", the capture region follows the mouse according to actual screen bounds height.

----------


## Peter Porter

Just made a minor change.

The "Crop to 16.9" checkbox is only visible if your monitor does not have an asect ratio of 16.9.

----------


## Peter Porter

Fixed some issues, and cleaned up the code.

Here's a preview of what I have in the works, using the same capture code above.

----------


## Peter Porter

I fixed a few bugs.

No more endless saves, or crashes during a restart of the screen recorder. This was caused by stopping the app from the VB dashboard during a recording, which left an FFmpeg process running. Now a check is done for any FFmpeg processes at initial runtime, which is then stopped before any recording is made.

Regionbars issues have been solved. They are realigned, resized, and displayed for every recording.

----------


## vinodvin42

> Fixed some issues, and cleaned up the code.
> 
> Here's a preview of what I have in the works, using the same capture code above.


Hi Peter,

Could you please send me the link to download latest MPEG-4 Screen recorder, I am not getting the sound file in your first project

Thanks
Vinod

----------


## Peter Porter

vinodvin42, the polished looking version you quoted wont be available online as a project file. It has some extras, but it uses the same recording code as my older version.

Have you downloaded the FFmpeg static build and placed it within this project's resources folder? It needs FFmpeg in order to record audio as well as video.

The recorder was designed to work with older versions of FFmpeg below, but might work with the latest static builds now.
32bit version
64bit version

If you have FFmpeg, the only other thing could be is that you don't have your system's Stereo Mix enabled.

*Enabling Stereo Mix for Windows Vista, 7 and 8*
* Right-click over the Speaker icon by the system clock then choose Recording Devices to open the Recording tab of "Sound".
* Right-click Stereo Mix, and from it's dropdown menu, click Enable.

*Note (Vista, 7 and 8):*
If you don't see Stereo Mix, right click within the Recording tab window, then check "Show Disabled Devices".

*Enabling Stereo Mix for Windows XP*
* Click the Speaker icon by the system clock.
* In the window that appears, choose Options then Properties.
* In the next window, click the "Recording" radio button, then in "Show the following volume controls", click in the box for Stereo Mix, then "OK".

----------


## Peter Porter

Sorry, I've been busy with other things. I'll try to post an update soon for the downloadable version.

----------


## drdrift

Hello Im new here on forum.

Can someone help me, first how to solve this FPS problem, its not 30fps in output video and also its not recording sound with ffmpeg.

Thank you

----------


## Piggyuniform

Nice Job, Can You Try My Screen Recorder Too?  :Smilie:

----------


## fenyoapa

Very nice , thank you. Maybe someone else got into this: the Test.mp4 file is empty (0kB) , hence merging fails. Solution (Windows): the graphics card driver must be installed/set properly, the screen resolution and/or Size of text and other elements must be at the Default (Suggested) (100%) value. (Start menu -> Settings -> System -> Display).
Other: my output mp4 file is very fast when played back, I can't solve this, sad...

----------


## pctweaks

Hi there, the video captured is playing back real fast and the audio has tons of static ?

----------


## fenyoapa

> Hi there, the video captured is playing back real fast and the audio has tons of static ?


Hi, what do you mean on "static"? Audio seems ok (a little noisy, but it is my sound card/mic). And yes, the 'Temp.mp4' and the final (merged) mp4 are played back very fast, but only the video, the audio is ok. 
When the video playback is finished it shows the last bmp frame (when I clicked on the 'stop' button) during the audio plays till end in normal speed.
I played with fps (tried without it too) and -rtbufsize params, but no luck.

----------


## Peter Porter

> Hi there, the video captured is playing back real fast and the audio has tons of static ?


Hi pctweaks,

Not sure if this will fix the static, but try this:

*** If you have Windows 7, right click on the speaker icon on the bottom right of your computer, and click "Recording Devices".

*** On the Sound window, double click on "Stereo Mix".

*** In it's properties window, click on the "Listen" tab, and un-check the "Listen to this device" box.

I've been trying to solve the audio video syncronage problem for a while now, but no luck.

----------


## Peter Porter

> Hi, what do you mean on "static"? Audio seems ok (a little noisy, but it is my sound card/mic). And yes, the 'Temp.mp4' and the final (merged) mp4 are played back very fast, but only the video, the audio is ok.


Gonna create another version where FFmpeg does all of the work, capturing the screen and audio. If it still has syncronage issues, then we'll know for sure it's FFmpeg. The only problem then is that the mouse will always be recorded. There will be no way to control it's visibility.

----------


## pctweaks

I got Aforge.net to record perfect, but getting sound in the video is the issue. Here is the source file for the project that I have been working on, let me know if you figure a way to add sound. 

Thanks,
PcTweaks

Source Download

----------

