[Tutorial] .NET Video Converter with C# FFmpeg wrapper
Some day ago I stuck trying to convert a lot of files. All I want to do is to watch some folder all
convert all files getting into it. I'm lazy so I try to automate my work whenever I can.
This guide
show how to implement simple .NET Video Converter using ffmpeg wrapped by Xabe.FFmpeg
The application will be written in .NET Core 2.0 but this solution works with .NET Framework and GUI.
Steps are simple:
- Application get all files from directory containing video files
- App convert all non .mp4 files to mp4
Creating project
At first I created new .NET Core 2.0 project.
I copied all of files I'm going to convert to "C:\movies".
Next I wrote method getting all files from specific directory ("C:\movies" in this case) excluding mp4 files (because this is destination format, I don't want to convert mp4 files again to mp4). Run().GetAwaiter().GetResult() in Main method allows me to use await which should be helpful later.
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MyVideosConverter
{
class Program
{
static void Main(string[] args)
{
Run().GetAwaiter().GetResult();
}
private static async Task Run()
{
Queue filesToConvert = new Queue(GetFilesToConvert("C:\\movies"));
await Console.Out.WriteLineAsync($"Find {filesToConvert.Count()} files to convert.");
}
private static IEnumerable GetFilesToConvert(string directoryPath)
{
//Return all files excluding mp4 because I want convert it to mp4
return new DirectoryInfo(directoryPath).GetFiles().Where(x => x.Extension != ".mp4");
}
}
}
Installing Xabe.FFmpeg
Next thing I did was installed Xabe.FFmpeg. Documentation of it is available here.
I have used nuget packages:
PM> Install-Package Xabe.FFmpeg
Next thing we want to do is download FFmpeg package to specific dir.
Just add using...
using Xabe.FFmpeg;
...and this to the end of Run() method.
//Set directory where app should look for FFmpeg
FFmpeg.ExecutablesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FFmpeg");
//Get latest version of FFmpeg. It's great idea if you don't know if you had installed FFmpeg.
await FFmpeg.GetLatestVersion();
Convert video to mp4 using C#
We add new method handling conversion
private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Save file to the same location with changed extension
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
var conversion = await FFmpeg.Conversions.FromSnippet.Convert(fileToConvert.FullName, outputFileName);
await conversion.Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}
And add line executing new added method to Run(). Additionaly we will add Console.In.ReadLine() to look at output before console close up. Now it should looks like below:
private static async Task Run()
{
Queue filesToConvert = new Queue(GetFilesToConvert("C:\\movies"));
await Console.Out.WriteLineAsync($"Find {filesToConvert.Count()} files to convert.");
//Set directory where the app should look for FFmpeg executables.
FFmpeg.ExecutablesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FFmpeg");
//Get latest version of FFmpeg. It's great idea if you don't know if you had installed FFmpeg.
await FFmpeg.GetLatestVersion();
//Run conversion
await RunConversion(filesToConvert);
Console.In.ReadLine();
}
After pressing F5 everything should compiles without errors and console will show up. After few moments we should have result in console.
In source directory we have got few more files with .mp4 extensions. (Please forgive me that number of files doesn't match. I did something with it the meantime).
More logging
Ok. Everything works fine but what about logs? We have only information that something was finished.
Now we add more log. The best option is to get output for every percent of conversion is finished.
Hopefully Xabe.FFmpeg provides us event for this.
Let's modify RunConversion method a bit
private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Save file to the same location with changed extension
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
//SetOverwriteOutput to overwrite files. It's useful when we already run application before
var conversion = Conversion.ToMp4(fileToConvert.FullName, outputFileName).SetOverwriteOutput(true);
//Add log to OnProgress
conversion.OnProgress += async (sender, args) =>
{
//Show all output from FFmpeg to console
await Console.Out.WriteLineAsync($"[{args.Duration}/{args.TotalLength}][{args.Percent}%] {fileToConvert.Name}");
};
//Start conversion
await conversion.Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}
After changes console has better output showing exactly how many second was converted.
What if we want to customize our conversion? Just run some additional methods on your conversion object.
private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Save file to the same location with changed extension
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
var mediaInfo = await MediaInfo.Get(fileToConvert);
var videoStream = mediaInfo.VideoStreams.First();
var audioStream = mediaInfo.AudioStreams.First();
//Change some parameters of video stream
videoStream
//Rotate video counter clockwise
.Rotate(RotateDegrees.CounterClockwise)
//Set size to 480p
.SetSize(VideoSize.Hd480)
//Set codec which will be used to encode file. If not set it's set automatically according to output file extension
.SetCodec(VideoCodec.H264);
//Create new conversion object
var conversion = Conversion.New()
//Add video stream to output file
.AddStream(videoStream)
//Add audio stream to output file
.AddStream(audioStream)
//Set output file path
.SetOutput(outputFileName)
//SetOverwriteOutput to overwrite files. It's useful when we already run application before
.SetOverwriteOutput(true)
//Disable multithreading
.UseMultiThread(false)
//Set conversion preset. You have to chose between file size and quality of video and duration of conversion
.SetPreset(ConversionPreset.UltraFast);
//Add log to OnProgress
conversion.OnProgress += async (sender, args) =>
{
//Show all output from FFmpeg to console
await Console.Out.WriteLineAsync($"[{args.Duration}/{args.TotalLength}][{args.Percent}%] {fileToConvert.Name}");
};
//Start conversion
await conversion.Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}