Xabe.FFmpeg requirements

FFmpeg has to be installed on end user device. To use this library one of those conditions must be fulfilled:
  • FFmpeg directory added in PATH (contains ffprobe and ffmpeg executables)
  • FFmpeg.SetExecutablesPath method has to be set to FFmpeg directory path
  • FFmpeg executables have to be in the same directory with application executable
  • FFmpeg executables could be downloaded by using FFmpegDownloader.GetLatestVersion(FFmpegVersion ffmpegVersion) - located in Xabe.FFmpeg.Downloader package

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 = 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();

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

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"

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.

Download FFmpeg executables

Getting latest FFmpeg executables is possible on most common operating system or architectures. Currently there are lack support of Tizen and RaspberryPi. Start downloading it is quite easy. After installing package Xabe.FFmpeg.Downloader, just run this method:

FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official)
This will acquire latest version for your operating system and saves it on your computer. Default directory is where your assemblies are. This can be changed by FFmpeg.ExecutablesPath. Above method create for version.json file in specified directory and if don't find it, download everything and save information about version. Next run will get version from file and check if new version is available.

Differences between versions:

  • Official - Official releases from ffbinaries. It is recommended source of executables.
  • Full - Full release version made by Zenaroe. It already reached end-of-life and it's not updated anymore. All executables are downloaded from this release. Supports only Windows and MacOS
  • Shared - light version made by Zenaroe. For advanced users. All executables are downloaded from this release
  • Android - Android release based on Mobile-FFmpeg. All executables are downloaded from this release

Getting metadata from media file

It is possible to get additional metadata using FFprobe. To run FFprobe with custom arguments:

string result = await Probe.New()
.Start($"-skip_frame nokey -select_streams v:0 -show_entries frame=pkt_pts_time {Resources.Mp4}");

Method returns console FFprobe output in format specified in input arguments.

Getting streams from media file

Basic properties of media file can be read by MediaInfo class:

string filePath = Path.Combine("C:", "samples", "SampleVideo.mp4");
IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(filePath);

IMediaInfo contains basic properties about media:

Xabe.FFmpeg MediaInfo diagram

More properties can be found in specific stream.

Set audio channels

string inputVideoPath = Path.Combine("C:", "Temp", "input.mkv");
string outputPathMp4 = Path.Combine("C:", "Temp", "result.mp4");

IMediaInfo info = await FFmpeg.GetMediaInfo(inputVideoPath);

IStream audioStream = info.AudioStreams.FirstOrDefault()
    ?.SetChannels(2);

return FFmpeg.Conversions.New()
    .AddStream(videoStream, audioStream)
    .SetOutput(outputPathMp4);

Methods

/// <summary>
///     Set stream to copy with orginal codec
/// </summary>
/// <returns>IAudioStream</returns>
IAudioStream CopyStream();
/// <summary>
///     Reverse audio stream
/// </summary>
/// <returns>IAudioStream</returns>
IAudioStream Reverse();
/// <summary>
///     Set audio Channels (-ac option)
/// </summary>
/// <param name="channels">Number of channels to use in the output stream</param>
/// <returns>IAudioStream</returns>
IAudioStream SetChannels(int channels);
/// <summary>
///     Set audio codec and bitrate
/// </summary>
/// <param name="codec">Audio odec</param>
/// <returns>IAudioStream</returns>
IAudioStream SetCodec(AudioCodec codec);
/// <summary>
///     Set audio codec and bitrate
/// </summary>
/// <param name="codec">Audio odec</param>
/// <returns>IAudioStream</returns>
IAudioStream SetCodec(string codec);
/// <summary>
///     Set filter
/// </summary>
/// <param name="filter">Filter</param>
/// <returns>IAudioStream</returns>
IAudioStream SetBitstreamFilter(BitstreamFilter filter);
/// <summary>
///     Sets the Bitrate of the AudioStream
/// </summary>
/// <param name="bitRate">Bitrate for the AudioStream in bytes</param>
/// <returns>IAudioStream</returns>
IAudioStream SetBitrate(long bitRate);
/// <summary>
///     Set Bitrate of the AudioStream
/// </summary>
/// <param name="minBitrate">Bitrate in bits</param>
/// <param name="maxBitrate">Bitrate in bits</param>
/// <param name="buffersize">Buffersize in bits</param>
/// <returns>IAudioStream</returns>
IAudioStream SetBitrate(long minBitrate, long maxBitrate, long bufferSize);
/// <summary>
///     Sets the SampleRate of the AudioStream (-ar option)
/// </summary>
/// <param name="sampleRate">SampleRate in HZ for the Audio Stream</param>
/// <returns>IAudioStream</returns>
IAudioStream SetSampleRate(int sampleRate);
/// <summary>
///     Change speed of media
/// </summary>
/// <param name="multiplicator">Speed value. (0.5 - 2.0). To double the speed set this to 2.0</param>
/// <returns>IAudioStream</returns>
/// <exception cref="ArgumentOutOfRangeException">When speed isn't between 0.5 - 2.0.</exception>
IAudioStream ChangeSpeed(double multiplicator);
/// <summary>
///     Get part of audio
/// </summary>
/// <param name="startTime">Start point</param>
/// <param name="duration">Duration of new audio</param>
/// <returns>IAudioStream</returns>
IAudioStream Split(TimeSpan startTime, TimeSpan duration);
/// <summary>
///     Seeks in input file to position. (-ss argument)
/// </summary>
/// <param name="seek">Position</param>
/// <returns>IAudioStream</returns>
IAudioStream SetSeek(TimeSpan? seek);
/// <summary>
///     Set filter
/// </summary>
/// <param name="filter">Filter</param>
/// <returns>IAudioStream</returns>
IAudioStream SetBitstreamFilter(string filter);
/// <summary>
/// Sets the format for the input file using the -f option before the input file name
/// </summary>
/// <param name="inputFormat">The input format to set</param>
/// <returns>IConversion object</returns>
IAudioStream SetInputFormat(string inputFormat);
/// <summary>
/// Sets the format for the input file using the -f option before the input file name
/// </summary>
/// <param name="inputFormat">The input format to set</param>
/// <returns>IConversion object</returns>
IAudioStream SetInputFormat(Format inputFormat);
/// <summary>
///     "-re" parameter. Read input at native frame rate. Mainly used to simulate a grab device, or live input stream (e.g. when reading from a file). Should not be used with actual grab devices or live input streams (where it can cause packet loss). By default ffmpeg attempts to read the input(s) as fast as possible. This option will slow down the reading of the input(s) to the native frame rate of the input(s). It is useful for real-time output (e.g. live streaming).
/// </summary>
/// <param name="readInputAtNativeFrameRate">Read input at native frame rate. False set parameter to default value.</param>
/// <returns>IConversion object</returns>
IAudioStream UseNativeInputRead(bool readInputAtNativeFrameRate);
/// <summary>
///     "-stream_loop" parameter. Set number of times input stream shall be looped. 
/// </summary>
/// <param name="loopCount">Loop 0 means no loop, loop -1 means infinite loop.</param>
/// <returns>IConversion object</returns>
IAudioStream SetStreamLoop(int loopCount);

Reversing media

Reverse is possible by operating on streams using Reverse() method:

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

IVideoStream videoStream = inputFile.VideoStreams.First()
    .SetCodec(VideoCodec.H264)
    .Reverse();

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(outputPath)
    .Start();

In given example output video file will have only one stream - reversed first video stream from source file.

Use Reverse() method is possible on IAudioStream and IVideoStream.

Changing speed

IVideoStream and IAudioStream allow to change media speed:

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

IVideoStream videoStream = inputFile.VideoStreams.First().SetCodec(VideoCodec.H264).ChangeSpeed(1.5);

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(outputPath)
    .Start();

ChangeSpeed() method accepting 1 argument - multiplayer. Multiplayer has to be between 0.5 and 2.0. If you want to speed up streams, use values greater than 1, if not, less than 1.

Changing size

The simplest way to change video size is by FFmpeg.Conversions.FromSnippet.ChangeSize:

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mkv");
string input = Resources.MkvWithAudio;

var conversion = await FFmpeg.Conversions.FromSnippet.ChangeSize(input, output, VideoSize.Nhd);

IConversionResult result = await conversion.Start();

Changing video format

FFmpeg.Conversions.FromSnippet contains few predefined methods to change video format e.g.:

await FFmpeg.Conversions.FromSnippet.ToOgv(inputVideoPath, outputPathOgv);
await FFmpeg.Conversions.FromSnippet.ToTs(inputVideoPath, outputPathTs);
await FFmpeg.Conversions.FromSnippet.ToWebM(inputVideoPath, outputPathWebm);

More conversion types are possible by using new Conversion:

string inputVideoPath = Path.Combine("C:", "Temp", "input.mkv");
string outputPathMp4 = Path.Combine("C:", "Temp", "result.mp4");

IMediaInfo info = await FFmpeg.GetMediaInfo(inputVideoPath);

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

return FFmpeg.Conversions.New()
    .AddStream(videoStream, audioStream)
    .SetOutput(outputPathMp4);

Working with images

Sometimes it is necessary to handle a lot of images and converting images to videos or vice versa. Changing video to images is possible thanks to ExtractEveryNthFrame:

Func outputFileNameBuilder = (number) => { return 'fileNameNo' + number + ".png"; };
IMediaInfo info = await FFmpeg.GetMediaInfo(Resources.MkvWithAudio).ConfigureAwait(false);
IVideoStream videoStream = info.VideoStreams.First()?.SetCodec(VideoCodec.Png);

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .ExtractEveryNthFrame(10, outputFileNameBuilder)
    .Start();

Above code extracts every 10th frame from input video and saves it to the files: 'fileNameNo001.png', 'fileNameNo002.png' and so on until every 10th frame is saved. For example, if video has 26 frames, only 2 images is saved.

ExtractNthFrame can be used only if one frame is required:

Func outputFileNameBuilder = (number) => { return 'fileNameNo' + number + ".png"; };
IMediaInfo info = await FFmpeg.GetMediaInfo(Resources.MkvWithAudio).ConfigureAwait(false);
IVideoStream videoStream = info.VideoStreams.First()?.SetCodec(VideoCodec.Png);

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .ExtractNthFrame(10, outputFileNameBuilder)
    .Start();

Above example creates file named 'fileNameNo001.png' containing 10th frame of a video.

BuildVideoFromImages is used to do oposite thing - changing collection of images to video:

List files = Directory.EnumerateFiles(Resources.Images).ToList();

await Conversion.New()
    .SetInputFrameRate(1)
    .BuildVideoFromImages(files)
    .SetFrameRate(1)
    .SetOutputPixelFormat(PixelFormat.Yuv420P)
    .SetOutput("outputfile.mp4")
    .Start();

Changing bitrate

Xabe.FFmpeg provide support for changing bitrate on Audio and Video streams:

string inputVideoPath = Path.Combine("C:", "Temp", "input.mkv");
string outputPathMp4 = Path.Combine("C:", "Temp", "result.mp4");

IMediaInfo info = await FFmpeg.GetMediaInfo(inputVideoPath);

IStream videoStream = info.VideoStreams.FirstOrDefault()
    ?.SetBitrate(6000);
IStream audioStream = info.AudioStreams.FirstOrDefault()
    ?.SetBitrate(192000);

return FFmpeg.Conversions.New()
    .AddStream(videoStream, audioStream)
    .SetOutput(outputPathMp4);

Changing video framerate

string inputVideoPath = Path.Combine("C:", "Temp", "input.mkv");
string outputPathMp4 = Path.Combine("C:", "Temp", "result.mp4");

IMediaInfo info = await FFmpeg.GetMediaInfo(inputVideoPath);

IStream videoStream = info.VideoStreams.FirstOrDefault()
    ?.SetFramerate(29.97);

return FFmpeg.Conversions.New()
    .AddStream(videoStream, audioStream)
    .SetOutput(outputPathMp4);

Methods


/// <summary>
///     Rotate video
/// </summary>
/// <param name="rotateDegrees">Rotate type</param>
/// <returns>IVideoStream</returns>
IVideoStream Rotate(RotateDegrees rotateDegrees);
/// <summary>
///     Change speed of video
/// </summary>
/// <param name="multiplicator">Speed value. (0.5 - 2.0). To double the speed set this to 2.0</param>
/// <returns>IVideoStream</returns>
/// <exception cref="ArgumentOutOfRangeException">When speed isn't between 0.5 - 2.0.</exception>
IVideoStream ChangeSpeed(double multiplicator);
/// <summary>
///     Melt watermark into video
/// </summary>
/// <param name="imagePath">Watermark</param>
/// <param name="position">Position of watermark</param>
/// <returns>IVideoStream</returns>
IVideoStream SetWatermark(string imagePath, Position position);
/// <summary>
///     Reverse video
/// </summary>
/// <returns>IVideoStream</returns>
IVideoStream Reverse();
/// <summary>
///     Set Flags for conversion (-flags option)
/// </summary>
/// <param name="flags">Flags to use</param>
/// <returns>IVideoStream</returns>
IVideoStream SetFlags(params Flag[] flags);
/// <summary>
///     Set Flags for conversion (-flags option)
/// </summary>
/// <param name="flags">Flags to use</param>
/// <returns>IVideoStream</returns>
IVideoStream SetFlags(params string[] flags);
/// <summary>
///     Set Framerate of the video (-r option)
/// </summary>
/// <param name="framerate">Framerates in FPS</param>
/// <returns>IVideoStream</returns>
IVideoStream SetFramerate(double framerate);
/// <summary>
///     Set Bitrate of the video (-b:v option)
/// </summary>
/// <param name="minBitrate">Bitrate in bits</param>
/// <param name="maxBitrate">Bitrate in bits</param>
/// <param name="buffersize">Buffersize in bits</param>
/// <returns>IVideoStream</returns>
IVideoStream SetBitrate(long minBitrate, long maxBitrate, long bufferSize);
/// <summary>
///     Set Bitrate of the video (-b:v option)
/// </summary>
/// <param name="bitrate">Bitrate in bits</param>
/// <returns>IVideoStream</returns>
IVideoStream SetBitrate(long bitrate);
/// <summary>
///     Set size of video
/// </summary>
/// <param name="size">VideoSize</param>
/// <returns>IVideoStream</returns>
IVideoStream SetSize(VideoSize size);
/// <summary>
///     Set size of video
/// </summary>
/// <param name="width">Width</param>
/// <param name="height">Height</param>
/// <returns>IVideoStream</returns>
IVideoStream SetSize(int width, int height);
/// <summary>
///     Set video codec
/// </summary>
/// <param name="codec">Video codec</param>
/// <returns>IVideoStream</returns>
IVideoStream SetCodec(VideoCodec codec);
/// <summary>
///     Set video codec
/// </summary>
/// <param name="codec">Video codec</param>
/// <returns>IVideoStream</returns>
IVideoStream SetCodec(string codec);
/// <summary>
///     Set stream to copy with orginal codec
/// </summary>
/// <returns>IVideoStream</returns>
IVideoStream CopyStream();
/// <summary>
///     Set filter
/// </summary>
/// <param name="filter">Filter</param>
/// <returns>IVideoStream</returns>
IVideoStream SetBitstreamFilter(BitstreamFilter filter);
/// <summary>
///     Loop over the input stream.(-loop)
/// </summary>
/// <param name="count">Number of repeats</param>
/// <param name="delay">Delay between repeats (in seconds)</param>
/// <returns>IVideoStream</returns>
IVideoStream SetLoop(int count, int delay = 0);
/// <summary>
///     Set output frames count
/// </summary>
/// <param name="number">Number of frames</param>
/// <returns>IVideoStream</returns>
IVideoStream SetOutputFramesCount(int number);
/// <summary>
///     Seeks in input file to position. (-ss argument)
/// </summary>
/// <param name="seek">Position</param>
/// <returns>IVideoStream</returns>
IVideoStream SetSeek(TimeSpan seek);
/// <summary>
///     Burn subtitle into file
/// </summary>
/// <param name="subtitlePath">Path to subtitle file in .srt format</param>
/// <param name="encode">Set subtitles input character encoding. Only useful if not UTF-8.</param>
/// <param name="style">
///     Override default style or script info parameters of the subtitles. It accepts a string containing
///     ASS style format KEY=VALUE couples separated by ","
/// </param>
/// <returns>IVideoStream</returns>
IVideoStream AddSubtitles(string subtitlePath, string encode = null, string style = null);
/// <summary>
///     Burn subtitle into file
/// </summary>
/// <param name="subtitlePath">Path to subtitle file in .srt format</param>
/// <param name="encode">Set subtitles input character encoding. Only useful if not UTF-8.</param>
/// <param name="style">
///     Override default style or script info parameters of the subtitles. It accepts a string containing
///     ASS style format KEY=VALUE couples separated by ","
/// </param>
/// <param name="originalSize">
///     Specify the size of the original video, the video for which the ASS style was composed. This
///     is necessary to correctly scale the fonts if the aspect ratio has been changed.
/// </param>
/// <returns>IVideoStream</returns>
IVideoStream AddSubtitles(string subtitlePath, VideoSize originalSize, string encode = null, string style = null);
/// <summary>
///     Get part of video
/// </summary>
/// <param name="startTime">Start point</param>
/// <param name="duration">Duration of new video</param>
/// <returns>IVideoStream</returns>
IVideoStream Split(TimeSpan startTime, TimeSpan duration);
/// <summary>
///     Set filter
/// </summary>
/// <param name="filter">Filter</param>
/// <returns>IVideoStream</returns>
IVideoStream SetBitstreamFilter(string filter);
/// <summary>
/// Sets the format for the input file using the -f option before the input file name
/// </summary>
/// <param name="inputFormat">The input format to set</param>
/// <returns>IConversion object</returns>
IVideoStream SetInputFormat(string inputFormat);
/// <summary>
/// Sets the format for the input file using the -f option before the input file name
/// </summary>
/// <param name="inputFormat">The input format to set</param>
/// <returns>IConversion object</returns>
IVideoStream SetInputFormat(Format inputFormat);
/// <summary>
///     "-re" parameter. Read input at native frame rate. Mainly used to simulate a grab device, or live input stream (e.g. when reading from a file). Should not be used with actual grab devices or live input streams (where it can cause packet loss). By default ffmpeg attempts to read the input(s) as fast as possible. This option will slow down the reading of the input(s) to the native frame rate of the input(s). It is useful for real-time output (e.g. live streaming).
/// </summary>
/// <param name="readInputAtNativeFrameRate">Read input at native frame rate. False set parameter to default value.</param>
/// <returns>IConversion object</returns>
IVideoStream UseNativeInputRead(bool readInputAtNativeFrameRate);
/// <summary>
///     "-stream_loop" parameter. Set number of times input stream shall be looped. 
/// </summary>
/// <param name="loopCount">Loop 0 means no loop, loop -1 means infinite loop.</param>
/// <returns>IConversion object</returns>
IVideoStream SetStreamLoop(int loopCount);

Add subtitles

There are two ways to add subtitles into video. The first one is to burn it into video. The next one is to add new stream with subtitles, as in .mkv format.

Burning subtitles

IVideoStream allows to burn subtitle into video:

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

IVideoStream videoStream = inputFile.VideoStreams.First().AddSubtitles(Resources.SubtitleSrt);

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(outputPath)
    .Start();

Add subtitles

Subtitles are streams too so could be added to conversion like other streams:

IMediaInfo mediaInfo = await FFmpeg.GetMediaInfo(inputPath);
IMediaInfo subtitleInfo = await FFmpeg.GetMediaInfo(subtitlePath);

ISubtitleStream subtitleStream = subtitleInfo.SubtitleStreams.First()
.SetLanguage(language);

return New()
    .AddStream(mediaInfo.Streams)
    .AddStream(subtitleStream)
    .SetOutput(outputPath);

or easier using FFmpeg.Conversions.FromSnippet.AddSubtitle

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mkv");

IConversion conversion = await FFmpeg.Conversions.FromSnippet.AddSubtitle(Resources.MkvWithAudio, output, Resources.SubtitleSrt);
IConversionResult result = await conversion.Start();

Methods

/// <summary>
///     Set subtitle language
/// </summary>
/// <param name="language">Language</param>
/// <returns>ISubtitleStream</returns>
ISubtitleStream SetLanguage(string language);
/// <summary>
///     Set subtitle codec
/// </summary>
/// <param name="codec">Subtitle codec</param>
/// <returns>ISubtitleStream</returns>
ISubtitleStream SetCodec(SubtitleCodec codec);
/// <summary>
///     Set Subtitle codec
/// </summary>
/// <param name="codec">Subtitle codec</param>
/// <returns>IVideoStream</returns>
ISubtitleStream SetCodec(string codec);
/// <summary>
///     "-re" parameter. Read input at native frame rate. Mainly used to simulate a grab device, or live input stream (e.g. when reading from a file). Should not be used with actual grab devices or live input streams (where it can cause packet loss). By default ffmpeg attempts to read the input(s) as fast as possible. This option will slow down the reading of the input(s) to the native frame rate of the input(s). It is useful for real-time output (e.g. live streaming).
/// </summary>
/// <param name="readInputAtNativeFrameRate">Read input at native frame rate. False set parameter to default value.</param>
/// <returns>IConversion object</returns>
ISubtitleStream UseNativeInputRead(bool readInputAtNativeFrameRate);
/// <summary>
///     "-stream_loop" parameter. Set number of times input stream shall be looped. 
/// </summary>
/// <param name="loopCount">Loop 0 means no loop, loop -1 means infinite loop.</param>
/// <returns>IConversion object</returns>
ISubtitleStream SetStreamLoop(int loopCount);

Using hardware acceleration

Since version 3.1.0 there is a possibility to use hardware acceleration.

/// <summary>
///     Use hardware acceleration. This option set -threads to 1 for compatibility reasons. This should be use with proper codec (e.g. -c:v h264_nvencorh264_cuvid)
/// </summary>
/// <param name="hardwareAccelerator">Hardware accelerator. List of all accelerators available for your system - "ffmpeg -hwaccels"</param>
/// <param name="decoder">Codec using to decode input video.</param>
/// <param name="encoder">Codec using to encode output video.</param>
/// <param name="device">Number of device (0 = default video card) if more than one video card.</param>
/// <returns>IConversion object</returns>
IConversion UseHardwareAcceleration(HardwareAccelerator hardwareAccelerator, VideoCodec decoder, VideoCodec encoder, int device = 0);
gpu vs cpu conversion

You can read more at official ffmpeg wiki.

RTSP and web streams

One of the most important functionality in library is handling RTSP. Using online streams or saving video from your web cam is as easy as handling local streams. It works the best with SetInputTime to limit recording to a limited time.

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + FileExtensions.Mp4);
var mediaInfo = await FFmpeg.GetMediaInfo("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov");

var conversionResult = await FFmpeg.Conversions.New()
    .AddStream(mediaInfo.Streams)
    .SetInputTime(TimeSpan.FromSeconds(3))
    .SetOutput(output)
    .Start();

CancellationToken is another option to stop capturing.

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + FileExtensions.Mp4);
    var mediaInfo = await FFmpeg.GetMediaInfo("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov");
    
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
FFmpeg.Conversions.New()
    .AddStream(mediaInfo.Streams)
    .SetOutput(output)
    .Start(cancellationTokenSource.Token);

await Task.Delay(5000);
cancellationTokenSource.Cancel();
    

For more advanced usage it is possible to input stream. Example below shows how to rotate it. A lot of others methods can be used instead of Rotate.

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + FileExtensions.Mp4);
var mediaInfo = await FFmpeg.GetMediaInfo("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov");

var videoStream = mediaInfo.VideoStreams.First();
videoStream.Rotate(RotateDegrees.Clockwise);

var conversionResult = await FFmpeg.Conversions.New()
    .AddStream(videoStream)
    .SetOutput(output)
    .Start();

Sending data to RTSP server

In the contrary sometimes we need to send some data to remote server. There are two snippets that can help. SendToRtspServer() and SendDesktopToRtspServer.

/// <summary>
///     Loop file infinitely to rtsp streams with some default parameters like: -re, -preset ultrafast
/// </summary>
/// <param name="inputFilePath">Path to file</param>
/// <param name="rtspServerUri">Uri of RTSP Server in format: rtsp://127.0.0.1:8554/name</param>
/// <returns>IConversion object</returns>
Task<IConversion> SendToRtspServer(string inputFilePath, Uri rtspServerUri);

/// <summary>
///     Send desktop infinitely to rtsp streams with some default parameters like: -re, -preset ultrafast
/// </summary>
/// <param name="rtspServerUri">Uri of RTSP Server in format: rtsp://127.0.0.1:8554/name</param>
/// <returns>IConversion object</returns>
Task<IConversion> SendDesktopToRtspServer(Uri rtspServerUri);

Some parameters are set with default values. Every stream is changed to use stream loop, native input stream and bitrate to 1024000. Framerate is set to 23.976 and libx264 and aac are used as codecs. Except that pixel format is changed to yuv420p and preset UltraFast is enabled.

Cross-Platform Desktop Capture

Captures the entire display at the specified framerate. It works the best with SetInputTime to limit recording to a limited time. That method adds new stream to your conversion with you entire screen. Please note that if you have multiple screens result can be strange and additional parameters has to be set up.

string output = Path.Combine("C:", "Temp", "desktop_capture.mp4");

IConversionResult conversionResult = await FFmpeg.Conversions.New()
    .AddDesktopStream()
    .SetInputTime(TimeSpan.FromSeconds(3))
    .SetOutput(output)
    .Start();

CancellationToken is another option to stop capturing.

string output = Path.Combine("C:", "Temp", "desktop_capture.mp4");

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
FFmpeg.Conversions.New()
    .AddDesktopStream()
    .SetOutput(output)
    .Start(cancellationTokenSource.Token);

await Task.Delay(5000);
cancellationTokenSource.Cancel();

For more advanced usage it is possible to modify desktop stream. Example below shows how to rotate desktop stream. Of course a lot of others methods can be used instead of rotate.

string output = Path.Combine("C:", "Temp", "desktop_capture.mp4");

var conversion = FFmpeg.Conversions.New()
    .AddDesktopStream()
    .SetInputTime(TimeSpan.FromSeconds(3))
    .SetOutput(output);
var desktopStream = conversion.Streams.First() as IVideoStream;
desktopStream.Rotate(RotateDegrees.Clockwise);

await conversion.Start();

AddDesktopStream method has few optional parameters. They are useful if you have multiple screens.

/// <summary>
///     Capture desktop to output file
/// </summary>
/// <param name="xOffset">X Offset</param>
/// <param name="yOffset">Y Offset</param>
/// <param name="videoSize">Input video size</param>
/// <param name="framerate">The desired framerate of the output</param>
/// <returns>IConversion object</returns>
IConversion AddDesktopStream(string videoSize = null, double framerate = 30, int xOffset = 0, int yOffset = 0);
IConversion AddDesktopStream(VideoSize videoSize, double framerate = 30, int xOffset = 0, int yOffset = 0);

Underneath AddDesktopStream method add additional stream that depends on Operating System. On Windows it uses gdigrab, on Linux x11grab and on OSX - avfoundation.

Snippets

Conversions snippets are extensions of Conversion class with snippets. Most simple conversions will be done with built-in templates or with little modification. That was designed to simplify typical conversions and teach how to create own solutions.

FFmpeg.Conversions.FromSnippet is a good point to start using Xabe.FFmpeg library. Every method may be used as template for a more conversions. If you think that your new conversion method is really useful, do not hesitate about include them to Xabe.FFmpeg.FFmpeg.Conversions.FromSnippet by pull request.

Every method is only a snippet and uses only IConversion with specific configuration. See the source to know how chosen conversion works.

Concatenate videos

The easiest way to concatenate video files is by FFmpeg.Conversions.FromSnippet.Concatenate:

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
var conversion = await FFmpeg.Conversions.FromSnippet.Concatenate(output, Resources.MkvWithAudio, Resources.Mp4WithAudio);
await conversion.Start();

Concatenation works with almost unlimited number of files.

Concatenate snippet is one of the most complicated logic in whole library. Providing one generic method to join a lot of file types required few sacrifices. One of them is speed. Concatenation if done by filter_complex concat and all of input videos are scaled to the one with highest resolution. A few other options are set too like aspect, format or anullsrc.

Gifs

FFmpeg allows to create gif file from video. Number of loops (one to infinity) and delay between repeats can be specified in parameters. The easiest way to get gif from video is to use FFmpeg.Conversions.FromSnippet.ToGif() method.

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".gif");

IConversion conversion = await FFmpeg.Conversions.FromSnippet.ToGif(Resources.Mp4, output, 1, 1);
IConversionResult result = await conversion.Start();
/// <summary>
///     Convert image video stream to gif
/// </summary>
/// <param name="inputPath">Input path</param>
/// <param name="outputPath">Output path</param>
/// <param name="loop">Number of repeats</param>
/// <param name="delay">Delay between repeats (in seconds)</param>
/// <returns>Conversion result</returns>
IConversion ToGif(string inputPath, string outputPath, int loop, int delay = 0);

Snapshot

The simplest way to get snapshot is by FFmpeg.Conversions.FromSnippet.Snapshot:

string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png");

IConversion conversion = await FFmpeg.Conversions.FromSnippet.Snapshot(Resources.Mp4WithAudio, output, TimeSpan.FromSeconds(0));
IConversionResult result = await conversion.Start();

Conversion returns snapshot in format specified in output path so outputPath should be with correct extension. Image has exactly the same size as a video.

Watermarks

Example of use watermarks:

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");

IConversion conversion = await FFmpeg.Conversions.FromSnippet.SetWatermark(Resources.Mp4WithAudio, output, Resources.PngSample, Position.Center);
IConversionResult result = await conversion.Start();

Watermark can be set in different position in a video:

  • Position.UpperRight
  • Position.BottomRight
  • Position.Right
  • Position.BottomLeft
  • Position.UpperLeft
  • Position.Left
  • Position.Center
  • Position.Bottom
  • Position.Up
/// <summary>
///     Melt watermark into video
/// </summary>
/// <param name="inputPath">Input video path</param>
/// <param name="outputPath">Output file</param>
/// <param name="inputImage">Watermark</param>
/// <param name="position">Position of watermark</param>
/// <returns>Conversion result</returns>
Task<IConversion> SetWatermark(string inputPath, string outputPath, string inputImage, Position position);
Big Buck Bunny with melted watermark

Big Buck Bunny with melted watermark in the center

Add audio

The simplest way to add audio to video file is by AddAudio method. It works for most cases. By default it takes first video stream from input video file and first audio stream from input audio file and all subtitles from video file. It can work unpredictable when you will try to change format/extension of input file.

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
var conversion = await FFmpeg.Conversions.FromSnippet.AddAudio(Resources.Mp4, Resources.Mp3, output);
IConversionResult result = await conversion
    .Start();

Extracting audio

If you think video streams are redundant or you have multiple audio streams and you need only default one you can use ExtractAudio snippet. It creates new file with only one audio stream:

string output = Path.ChangeExtension(Path.GetTempFileName(), "mp3");
var conversion = FFmpeg.Conversions.FromSnippet.ExtractAudio(Resources.Mp4WithAudio, output);
await conversion.Start();

Visualise audio

Sometimes you needs end user attention but it is hard with audio file. VisualiseAudio adds generated visualisation like in an old music players:

string output = Path.ChangeExtension(Path.GetTempFileName(), "mp4");

var conversion = await FFmpeg.Conversions.FromSnippet.VisualiseAudio(Resources.Mp4WithAudio, output, size, pixelFormat, mode, amplitudeScale, frequencyScale)
await conversion.Start();
Example of audio visualisation method result

VisualiseAudio(VideoSize.Hd1080, PixelFormat.yuv420p, VisualisationMode.line, AmplitudeScale.log, FrequencyScale.lin)

There are plenty of possible outputs. Screen above is only one of the options.

Extracting video

If you think audio streams are redundant or you have multiple video streams and you need only default one you can use ExtractVideo snippet. It creates new file with only one video stream:

string output = Path.ChangeExtension(Path.GetTempFileName(), Path.GetExtension(Resources.Mp4WithAudio));
var conversion = FFmpeg.Conversions.FromSnippet.ExtractVideo(Resources.Mp4WithAudio, output);
await conversion.Start();

Split

IVideoStream and IAudioStream allows split media but the easiest (but not most efficient) way is by FFmpeg.Conversions.FromSnippet.Split:

string output = Path.ChangeExtension(Path.GetTempFileName(), ".mp4");
IConversion conversion = await FFmpeg.Conversions.FromSnippet.Split(Resources.Mp4WithAudio, output, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(8));
IConversionResult result = await conversion.Start();

Helper splits all streams in media file and copies them (splitted) to output. In example output contains a media file with duration of 6 seconds contains both audio and video streams.

M3U8

Xabe.FFmpeg allows to download m3u8 playlist:

string output = Path.ChangeExtension(Path.GetTempFileName(), "mp4");

var conversion = await FFmpeg.Conversions.FromSnippet.SaveM3U8Stream(uri, output, TimeSpan.FromSeconds(1))
await conversion.Start();
/// <summary>
///     Save M3U8 stream
/// </summary>
/// <param name="uri">Uri to stream</param>
/// <param name="outputPath">Output path</param>
/// <param name="duration">Duration of stream</param>
/// <returns>Conversion result</returns>
Task<IConversion> SaveM3U8Stream(Uri uri, string outputPath, TimeSpan? duration = null);