PM> Install-Package Xabe.FFmpeg
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.
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 = await 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();
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();
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).
var mediaInfo = await MediaInfo.Get(Resources.MkvWithAudio);
IConversionResult conversionResult = FFmpeg.Conversions.New()
.AddStream(mediaInfo.Streams)
.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"
var mediaInfo = await MediaInfo.Get(Resources.MkvWithAudio);
IConversionResult conversionResult = FFmpeg.Conversions.New()
.AddStream(mediaInfo.Streams)
.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"
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 are basic structure in Xabe.FFmpeg so it is good to know their architecture:
Diagram shows only most important properties and methods, for more information look at implementation.
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;
}
Xabe.FFmpeg uses only streams to operate on every media file. Most common conversions can be done with few simple steps: