[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:

  1. Application get all files from directory containing video files
  2. App convert all non .mp4 files to mp4

Creating project

At first I created new .NET Core 2.0 project.

Create MyVideoConverter project

I copied all of files I'm going to convert to "C:\movies".

Directory with 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.

Console output without log

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).

directory with movies after conversion

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.

console output after adding log

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}]");
    }
}