Xabe.FFmpeg requirements

FFmpeg has to be installed on end user device. To use this library one of those conditions must be fulfilled:
Default, the library is trying to find FFmpeg executables names containing "ffprobe", "ffmpeg". This function is case insensitive. Those names can be changed in FFmpeg.FFmpegExecutableName and FFmpeg.FFprobeExecutableName.
Install the Xabe.FFmpeg NuGet package via NuGet:
PM> Install-Package Xabe.FFmpeg

Conversion result

Started conversion returns ConversionResult after it's completed. Returned object informs about conversion duration, output file, and more.

One of most useful value for debug may be "Arguments" property which have all arguments passed to FFmpeg process for conversion.

.NET Video Conversion

Xabe.FFmpeg.Conversion is the main class to handle FFmpeg conversions. User can manipulate audio, video and subtitle through this class.

Sample below shows basic conversion video file from mkv to mp4 format:

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".mp4");
var snippet = FFmpeg.Conversions.FromSnippet.Convert(Resources.MkvWithAudio, output);
IConversionResult result = await snippet.Start();

This could be done also by:

string outputPath = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(Resources.MkvWithAudio);

IStream videoStream = mediaInfo.VideoStreams.FirstOrDefault()
    ?.SetCodec(VideoCodec.H264);
IStream audioStream = mediaInfo.AudioStreams.FirstOrDefault()
    ?.SetCodec(AudioCodec.Aac);

FFmpeg.Conversions.New()
    .AddStream(audioStream, videoStream)
    .SetOutput(outputPath)
    .Start();

Almost all methods in streams return specific stream (IAudioStream, IVideoStream etc.). It allows to create chain of methods. Stream object could be use more than once.

string outputPath = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(Resources.MkvWithAudio);

IStream videoStream = mediaInfo.VideoStreams.FirstOrDefault()
    ?.SetCodec(VideoCodec.H264)
    ?.Reverse()
    ?.SetSize(VideoSize.Hd480);

FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(outputPath)
    .Start();

Method IConversions.Clear() sets IConversion to untouched state. All parameters passed to it are overrided by default values.

IConversion provides events to handle FFmpeg output. OnDataReceived and OnProgress events allow redirect FFmpeg output to user and inform him about progress.

conversion.OnProgress += (sender, args) =>
{
var percent = (int)(Math.Round(args.Duration.TotalSeconds / args.TotalLength.TotalSeconds, 2) * 100);
Debug.WriteLine($"[{args.Duration} / {args.TotalLength}] {percent}%");
};
await conversion.Start();

OnDataReceived:

conversion.OnDataReceived += (sender, args) =>
{
Debug.WriteLine($"{args.Data}{Environment.NewLine}") ;
};
await conversion.Start();

Hash testing format

This muxer computes and prints a cryptographic hash of all the input audio and video frames. To compute the SHA-256 hash of the input converted to raw audio and video, and store it in the file hash.sha256:

string output = Path.Combine("C:", "Temp", "hash.sha256");
string inputVideoPath = Path.Combine("C:", "Temp", "input.mkv");

var mediaInfo = await FFmpeg.GetMediaInfo(inputVideoPath);

var conversionResult = await FFmpeg.Conversions.New()
    .AddStream(mediaInfo.Streams)
    .SetHashFormat(Hash.SHA256)
    .SetOutput(output)
    .Start();

Own arguments

It is impossible to wrap all functionality of FFmpeg in C#. To perform more complex tasks it will be necessary to pass your own arguments directly to FFmpeg.

If you want to add additional parameter to IConversion, use AddParameter method. All parameters added in this way will go near the end of ffmpeg arguments, just before output parameter. This code adds 3 parameters to conversion: -ss (start position), -t (duration) and -s (size).

bool conversionResult = FFmpeg.Conversions.New()
    .SetInput(Resources.MkvWithAudio)
    .AddParameter($"-ss {TimeSpan.FromSeconds(1)} -t {TimeSpan.FromSeconds(1)}")
    .AddParameter("-s 1920x1080")
    .SetOutput(outputPath)
    .Start();

//Output ffmpeg arguments will look like "ffmpeg.exe -i sample.mkv -ss 1 -t 1 -s 1920x1080 output.mp4"

Sometimes is necessary to add additional parameter before inputs. For exaple to capture screen user has to read input at native frame rate using parameter "-re"

bool conversionResult = FFmpeg.Conversions.New()
    .SetInput(Resources.MkvWithAudio)
    .AddParameter("-re", ParameterPosition.PreInput)
    .SetOutput(outputPath)
    .Start();

//Output ffmpeg arguments will look like "ffmpeg.exe -re -i sample.mkv output.mp4"

Also user can pass only his own arguments, without using IConversion class. Simplest conversion, from one format to another, can be obtained in this way:

string inputFile = Path.Combine(Environment.CurrentDirectory, "Resources", "SampleVideo_360x240_1mb.mkv");
string outputPath = Path.ChangeExtension(Path.GetTempFileName(), Extensions.Mp4);
string arguments = $"-i "{inputFile}" "{outputPath}"";

bool conversionResult = await FFmpeg.Conversion.New().Start(arguments);

In a result, Arguments variable should look like this (depends on OS and directories):

-i "C:/Xabe.FFmpeg/test/Xabe.FFmpeg.Test/bin/Debug/netcoreapp2.0/Resources/SampleVideo_360x240_1mb.mkv" "C:/Temp/tmpA1AA.mp4"

That string will be passed directly to FFmpeg so final command running in console will look like:

ffmpeg.exe -i "C:/Xabe.FFmpeg/test/Xabe.FFmpeg.Test/bin/Debug/netcoreapp2.0/Resources/SampleVideo_360x240_1mb.mkv" "C:/Temp/tmpA1AA.mp4"

Stop conversion

Started conversion could be stopped. This requires passing CancellationToken to Start method.

var cancellationTokenSource = new CancellationTokenSource();
string outputPath = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(Resources.MkvWithAudio);

IStream videoStream = mediaInfo.VideoStreams.FirstOrDefault()
    ?.SetCodec(VideoCodec.H264);

await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(outputPath)
    .Start(cancellationTokenSource.Token);

CancellationTokenSource can be cancelled manually...

cancellationTokenSource.Cancel();

or automatically after period of time:

cancellationTokenSource.CancelAfter(500);

Streams

Streams are basic structure in Xabe.FFmpeg so it is good to know their architecture:

Xabe.FFmpeg streams hierarchy

Diagram shows only most important properties and methods, for more information look at implementation.

Time extensions for FFmpeg

FFmpeg has it's own internal time format. E.g. if user wants to manually set seek parameter ("-ss") he has to convert it to FFmpeg specific format - "{hours:D}:{minutes:D2}:{seconds:D2}.{milliseconds:D3}".

There are 2 methods which can help with that problem: TimeSpan.ToFFmpeg() and string.ParseFFmpegTime()

public static string ToFFmpeg(this TimeSpan ts)
{
    int milliseconds = ts.Milliseconds;
    int seconds = ts.Seconds;
    int minutes = ts.Minutes;
    var hours = (int)ts.TotalHours;

    return $"{hours:D}:{minutes:D2}:{seconds:D2}.{milliseconds:D3}";
};
public static TimeSpan ParseFFmpegTime(this string text)
{
    List parts = text.Split(':')
        .Reverse()
        .ToList();

    var milliseconds = 0;
    var seconds = 0;

    if (parts[0].Contains('.'))
    {
        string[] secondsSplit = parts[0].Split('.');
        seconds = int.Parse(secondsSplit[0]);
        milliseconds = int.Parse(secondsSplit[1]);
    }
    else
    {
        seconds = int.Parse(parts[0]);
    }

    int minutes = int.Parse(parts[1]);
    int hours = int.Parse(parts[2]);

    return new TimeSpan(0, hours, minutes, seconds, milliseconds);
}

But honestly, you do not have to know what underneath those methods. That is sample use:

public IConversion SetSeek(TimeSpan? seek)
{
    if (seek.HasValue)
    {
        _seek = $"-ss {seek.Value.ToFFmpeg()} ";
    }
    return this;
}

C# FFmpeg wrapper conception

Xabe.FFmpeg uses only streams to operate on every media file. Most common conversions can be done with few simple steps:

  1. Extract streams from input file or create new streams with outside source (e.g. mp4 file)
  2. Manipulate with streams with embedded methods
  3. Add selected streams to conversion
  4. Set output
  5. Start conversion
Operating on streams (not on files) allows to work with multi-streams media files and single-stream files with the same way.