I made a video recorder that takes screenshots each frame and saves it in a temporary folder. When I press the Stop recording button it puts it all together in one video. I now need to have my program check the latest 2 screenshots it has taken and compare them to find changes, if change wasn't found the last image should get deleted. In the other hand if the 2 images had any difference, both images would stay there.
Thank you for helping me out!
Below is my Screen Recorder script
ScreenRecorder.cs
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Accord.Video;
using Accord.Video.FFMPEG;
using GroupDocs.Comparison.Options;
using GroupDocs.Comparison;
namespace Timelapser_version_1._2
{
class ScreenRecorder
{
//Video variables:
private Rectangle bounds;
private string outputPath = "";
private string tempPath = "";
private int fileCount = 1;
private List<string> inputImageSequence = new List<string>();
//File variables:
private string audioName = "mic.wav";
private string videoName = "video.mp4";
private string finalName = "FinalVideo.mp4";
//Time variable:
Stopwatch watch = new Stopwatch();
//Audio variables:
public static class NativeMethods
{
[DllImport("winmm.dll", EntryPoint = "mciSendStringA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
public static extern int record(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);
}
//ScreenRecorder Object:
public ScreenRecorder(Rectangle b, string outPath)
{
//Create temporary folder for screenshots:
CreateTempFolder("tempScreenCaps");
//Set variables:
bounds = b;
outputPath = outPath;
}
//Create temporary folder:
private void CreateTempFolder(string name)
{
//Check if a C or D drive exists:
if (Directory.Exists("D://"))
{
string pathName = $"D://{name}";
Directory.CreateDirectory(pathName);
tempPath = pathName;
}
else
{
string pathName = $"C://Documents//{name}";
Directory.CreateDirectory(pathName);
tempPath = pathName;
}
}
//Change final video name:
public void setVideoName(string name)
{
finalName = name;
}
//Delete all files and directory:
private void DeletePath(string targetDir)
{
string[] files = Directory.GetFiles(targetDir);
string[] dirs = Directory.GetDirectories(targetDir);
//Delete each file:
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
//Delete the path:
foreach (string dir in dirs)
{
DeletePath(dir);
}
Directory.Delete(targetDir, false);
}
//Delete all files except the one specified:
private void DeleteFilesExcept(string targetDir, string excDir)
{
string[] files = Directory.GetFiles(targetDir);
//Delete each file except specified:
foreach (string file in files)
{
if (file != excDir)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
}
}
//Clean up program on crash:
public void cleanUp()
{
if (Directory.Exists(tempPath))
{
DeletePath(tempPath);
}
}
//Return elapsed time:
public string getElapsed()
{
return string.Format("{0:D2}:{1:D2}:{2:D2}", watch.Elapsed.Hours, watch.Elapsed.Minutes, watch.Elapsed.Seconds);
}
//Record video:
public void RecordVideo()
{
//Keep track of time:
watch.Start();
int blac=0;
string a ="screenshot-1+%d";
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);
}
//Save screenshot:
string name = tempPath + "//screenshot-" + fileCount + ".png";
bitmap.Save(name, ImageFormat.Png);
inputImageSequence.Add(name);
fileCount++;
//Dispose of bitmap:
bitmap.Dispose();
}
}
//Record audio:
public void RecordAudio()
{
NativeMethods.record("open new Type waveaudio Alias recsound", "", 0, 0);
NativeMethods.record("record recsound", "", 0, 0);
}
//Save audio file:
private void SaveAudio()
{
string audioPath = "save recsound " + outputPath + "//" + audioName;
NativeMethods.record(audioPath, "", 0, 0);
NativeMethods.record("close recsound", "", 0, 0);
}
//Save video file:
public void SaveVideo(int width, int height, int frameRate)
{
using (VideoFileWriter vFWriter = new VideoFileWriter())
{
vFWriter.Open(outputPath + "//"+ videoName, width, height, frameRate, VideoCodec.MPEG4);
//Make each screenshot into a video frame:
foreach (string imageLocation in inputImageSequence)
{
Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
vFWriter.WriteVideoFrame(imageFrame);
imageFrame.Dispose();
}
//Close:
vFWriter.Close();
}
}
//Combine video and audio files:
private void CombineVideoAndAudio(string video, string audio)
{
//FFMPEG command to combine video and audio:
string args = $"/c ffmpeg -i \"{video}\" -i \"{audio}\" -shortest {finalName}";
ProcessStartInfo startInfo = new ProcessStartInfo
{
CreateNoWindow = false,
FileName = "cmd.exe",
WorkingDirectory = outputPath,
Arguments = args
};
//Execute command:
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
public void Stop()
{
//Stop watch:
watch.Stop();
//Video variables:
int width = bounds.Width;
int height = bounds.Height;
int frameRate = 10;
//Save audio:
SaveAudio();
//Save video:
SaveVideo(width, height, frameRate);
//Combine audio and video files:
CombineVideoAndAudio(videoName, audioName);
//Delete the screenshots and temporary folder:
DeletePath(tempPath);
//Delete separated video and audio files:
DeleteFilesExcept(outputPath, outputPath + "\\" + finalName);
}
}
}
forms1.cs
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Timelapser_version_1._2
{
public partial class Timelapser : Form
{
string outputPath = "";
bool folderSelected = false;
string finalVidName = "FinalVideo.mp4";
// Screen recorder object:
ScreenRecorder screenRec = new ScreenRecorder(new Rectangle(), " ");
public Timelapser()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
bool containsMP4 = finalVidName.Contains(".mp4");
if (folderSelected && containsMP4)
{
screenRec.setVideoName(finalVidName);
timer1.Start();
}
else if(!folderSelected && containsMP4)
{
MessageBox.Show("You must select an output Folder before recording","Error");
}
else if (folderSelected && !containsMP4)
{
MessageBox.Show("You must select video name that ends in '.mp4'", "Error");
finalVidName = "FinalVideo.mp4";
}
}
private void Stop_Click(object sender, EventArgs e)
{
timer1.Stop();
screenRec.Stop();
Application.Restart();
}
private void timer1_Tick(object sender, EventArgs e)
{
screenRec.RecordVideo();
screenRec.RecordAudio();
Timelabel.Text = screenRec.getElapsed();
}
private void SelectFolder_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderBrowser = new FolderBrowserDialog();
folderBrowser.Description = "Select an Output Folder";
if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
outputPath = @folderBrowser.SelectedPath;
folderSelected = true;
Rectangle bounds = Screen.FromControl(this).Bounds;
screenRec = new ScreenRecorder(bounds,outputPath);
}
else
{
MessageBox.Show("Please select a folder");
}
}
I found the solution, to be able to check each 2 screenshots we need the function below.
public static List<bool> GetHash(Bitmap bmpSource)
{
List<bool> lResult = new List<bool>();
//create new image with 128x128 pixel
Bitmap bmpMin = new Bitmap(bmpSource, new System.Drawing.Size(128, 128));
for (int j = 0; j < bmpMin.Height; j++)
{
for (int i = 0; i < bmpMin.Width; i++)
{
//reduce colors to true / false
lResult.Add(bmpMin.GetPixel(i, j).GetBrightness() < 0.1f);
}
}
return lResult;
}
This function gets the screenshots and creates a 128x128 version of them. Also it changes the colour to black and white to get a true or false.
Then we need to call the function for each 2 screenshots as shown below.
List<bool> iHash1 = GetHash(new Bitmap(tempPath + "//screenshot-" + k + ".png"));
List<bool> iHash2 = GetHash(new Bitmap(tempPath + "//screenshot-" + (k + 1) + ".png"));
//determine the number of equal pixel (x of 256)
long equalElements = iHash1.Zip(iHash2, (i, j) => i == j).Count(eq => eq);
Debug.Write(equalElements);
if (equalElements > 16300 )
{
var filePath = tempPath + "//screenshot-" + (k+1) + ".png";
File.Delete(filePath);
}