A .NET FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your C# applications
Find a file
Malte Rosenbjerg ff5b9e3b42 Minor adjustment
Former-commit-id: 82fc6e92c9
2020-05-13 00:11:53 +02:00
.github/workflows Update ci.yml 2020-05-12 17:27:55 +02:00
FFMpegCore Minor adjustment 2020-05-13 00:11:53 +02:00
FFMpegCore.Test Minor adjustment 2020-05-13 00:11:53 +02:00
.gitattributes FFMpegCore: port lib to .net standard 2019-02-08 22:41:40 +02:00
.gitignore FFMpegCore: port lib to .net standard 2019-02-08 22:41:40 +02:00
FFMpegCore.sln FFMpegCore: port lib to .net standard 2019-02-08 22:41:40 +02:00
LICENSE Initial commit 2019-02-08 11:24:38 +02:00
README.md Merge branch 'master' into feature/fluent-api 2020-05-06 20:57:26 +02:00

FFMpegCore

NuGet Badge CI

Setup

NuGet:

Install-Package FFMpegCore

A great way to use FFMpeg encoding when writing video applications, client-side and server-side. It has wrapper methods that allow conversion to all web formats: MP4, OGV, TS and methods of capturing screens from the videos.

Binaries

If you prefer to manually download them, visit ffbinaries.

Windows

command: choco install ffmpeg -Y

location: C:\ProgramData\chocolatey\lib\ffmpeg\tools\ffmpeg\bin

Mac OSX

command: brew install ffmpeg mono-libgdiplus

location: /usr/local/bin

Ubuntu

command: sudo apt-get install -y ffmpeg libgdiplus

location: /usr/bin

Path Configuration

Behavior

If you wish to support multiple client processor architectures, you can do so by creating a folder x64 and x86 in the root directory. Both folders should contain the binaries (ffmpeg.exe and ffprobe.exe) for build for the respective architectures.

By doing so, the library will attempt to use either /root/{ARCH}/(ffmpeg|ffprobe).exe.

If these folders are not defined, it will try to find the binaries in /root/(ffmpeg|ffprobe.exe)

Option 1

The default value (\\FFMPEG\\bin) can be overwritten via the FFMpegOptions class:

public Startup() 
{
    FFMpegOptions.Configure(new FFMpegOptions { RootDirectory = "./bin" });
}

Option 2

The root directory for the ffmpeg binaries can be configured via the ffmpeg.config.json file.

{
  "RootDirectory": "./bin"
}

Compatibility

Some versions of FFMPEG might not have the same argument schema. The lib has been tested with version 3.3 to 4.1

API

FFProbe

FFProbe is used to gather video information

static void Main(string[] args)
{
    string inputFile = "G:\\input.mp4";

    // loaded from configuration
    var video = new VideoInfo(inputFile);

    string output = video.ToString();

    Console.WriteLine(output);
}

Sample output:

Video Path : G:\input.mp4
Video Root : G:\\
Video Name: input.mp4
Video Extension : .mp4
Video Duration : 00:00:09
Audio Format : none
Video Format : h264
Aspect Ratio : 16:9
Framerate : 30fps
Resolution : 1280x720
Size : 2.88 Mb

FFMpeg

Convert your video files to web ready formats:

static void Main(string[] args)
{
    string inputFile = "input_path_goes_here";
    var encoder = new FFMpeg();
    FileInfo outputFile = new FileInfo("output_path_goes_here");

    var video = VideoInfo.FromPath(inputFile);

    // easily track conversion progress
    encoder.OnProgress += (percentage) => Console.WriteLine("Progress {0}%", percentage);

    // MP4 conversion
    encoder.Convert(
        video,
        outputFile,
        VideoType.Mp4,
        Speed.UltraFast,
        VideoSize.Original,
        AudioQuality.Hd,
        true
    );
    // OGV conversion
    encoder.Convert(
        video,
        outputFile,
        VideoType.Ogv,
        Speed.UltraFast,
        VideoSize.Original,
        AudioQuality.Hd,
        true
    );
    // TS conversion
    encoder.Convert(
        video,
        outputFile,
        VideoType.Ts
    );
}

Easily capture screens from your videos:

static void Main(string[] args)
{
    string inputFile = "input_path_goes_here";
    FileInfo output = new FileInfo("output_path_goes_here");

    var video = VideoInfo.FromPath(inputFile);

    new FFMpeg()
        .Snapshot(
            video,
            output,
            new Size(200, 400),
            TimeSpan.FromMinutes(1)
        );
}

Join video parts:

static void Main(string[] args)
{
    FFMpeg encoder = new FFMpeg();

    encoder.Join(
        new FileInfo(@"..\joined_video.mp4"),
        VideoInfo.FromPath(@"..\part1.mp4"),
        VideoInfo.FromPath(@"..\part2.mp4"),
        VideoInfo.FromPath(@"..\part3.mp4")
    );
}

Join image sequences:

static void Main(string[] args)
{
    FFMpeg encoder = new FFMpeg();

    encoder.JoinImageSequence(
        new FileInfo(@"..\joined_video.mp4"),
        1, // FPS
        ImageInfo.FromPath(@"..\1.png"),
        ImageInfo.FromPath(@"..\2.png"),
        ImageInfo.FromPath(@"..\3.png")
    );
}

Strip audio track from videos:

static void Main(string[] args)
{
    string inputFile = "input_path_goes_here",
       outputFile = "output_path_goes_here";

    new FFMpeg()
        .Mute(
            VideoInfo.FromPath(inputFile),
            new FileInfo(outputFile)
        );
}

Save audio track from video:

static void Main(string[] args)
{
    string inputVideoFile = "input_path_goes_here",
       outputAudioFile = "output_path_goes_here";

    new FFMpeg()
        .ExtractAudio(
            VideoInfo.FromPath(inputVideoFile),
            new FileInfo(outputAudioFile)
        );
}

Add audio track to video:

static void Main(string[] args)
{
    string inputVideoFile = "input_path_goes_here",
       inputAudioFile = "input_path_goes_here",
       outputVideoFile = "output_path_goes_here";

    FFMpeg encoder = new FFMpeg();

    new FFMpeg()
        .ReplaceAudio(
            VideoInfo.FromPath(inputVideoFile),
            new FileInfo(inputAudioFile),
            new FileInfo(outputVideoFile)
        );
}

Add poster image to audio file (good for youtube videos):

static void Main(string[] args)
{
    string inputImageFile = "input_path_goes_here",
       inputAudioFile = "input_path_goes_here",
       outputVideoFile = "output_path_goes_here";

    FFMpeg encoder = new FFMpeg();

    ((Bitmap)Image.FromFile(inputImageFile))
        .AddAudio(
            new FileInfo(inputAudioFile),
            new FileInfo(outputVideoFile)
        );

    /* OR */

    new FFMpeg()
        .PosterWithAudio(
            inputImageFile,
            new FileInfo(inputAudioFile),
            new FileInfo(outputVideoFile)
        );
}

Control over the 'FFmpeg' process doing the job:

static void Main(string[] args)
{
    string inputVideoFile = "input_path_goes_here",
       outputVideoFile = "input_path_goes_here";

    FFMpeg encoder = new FFMpeg();

    // start the conversion process
    Task.Run(() => {
        encoder.Convert(new VideoInfo(inputVideoFile), new FileInfo(outputVideoFile));
    });

    // stop encoding after 2 seconds (only for example purposes)
    Thread.Sleep(2000);
    encoder.Stop();
}

Enums

Video Size enumeration:

public enum VideoSize
{
    HD,
    FullHD,
    ED,
    LD,
    Original
}

Speed enumeration:

public enum Speed
{
    VerySlow,
    Slower,
    Slow,
    Medium,
    Fast,
    Faster,
    VeryFast,
    SuperFast,
    UltraFast
}

Audio codecs enumeration:

public enum AudioCodec
{
        Aac,
        LibVorbis
}

Audio quality presets enumeration:

public enum AudioQuality
{
        Ultra = 384,
        Hd = 192,
        Normal = 128,
        Low = 64
}

Video codecs enumeration:

public enum VideoCodec
{
        LibX264,
        LibVpx,
        LibTheora,
        Png,
        MpegTs
}

ArgumentBuilder

Custom video converting presets could be created with help of ArgumentContainer class:

var container = new ArgumentContainer();
container.Add(new VideoCodecArgument(VideoCodec.LibX264));
container.Add(new ScaleArgument(VideoSize.Hd));

or use Fluent API

var container = new ArgumentContainer()
                .VideoCodec(VideoCodec.LibX264)
                .Scale(VideoSize.Hd);
var ffmpeg = new FFMpeg();
var result = ffmpeg.Convert(container, new FileInfo("input.mp4"), new FileInfo("output.mp4"));

Other availible arguments could be found in FFMpegCore.FFMPEG.Argument namespace.

If you need to create your custom argument, you just need to create new class, that is inherited from Argument, Argument<T> or Argument<T1, T2> For example:

public class OverrideArgument : Argument
{
    public override string GetStringValue()
    {
        return "-y";
    }
}

Input piping

With input piping it is possible to write video frames directly from program memory without saving them to jpeg or png and then passing path to input of ffmpeg. This feature also allows us to convert video on-the-fly while frames are beeing generated/created/processed.

IPipeSource interface is used as source of data. It could be represented as encoded video stream or raw frames stream. Currently IPipeSource interface has single implementation, RawVideoPipeSource that is used for raw stream encoding.

For example:

Method that is generate bitmap frames:

IEnumerable<IVideoFrame> CreateFrames(int count)
{
    for(int i = 0; i < count; i++)
    {
        yield return GetNextFrame(); //method of generating new frames
    }
}

Then create ArgumentsContainer with InputPipeArgument

var videoFramesSource = new RawVideoPipeSource(CreateFrames(64)) //pass IEnumerable<IVideoFrame> or IEnumerator<IVideoFrame> to constructor of RawVideoPipeSource
{
    FrameRate = 30 //set source frame rate
};
var container = new ArgumentsContainer
{
    new InputPipeArgument(videoFramesSource),
    ... //Other encoding arguments
    new OutputArgument("temporary.mp4")
};

var ffmpeg = new FFMpeg();
var result = ffmpeg.Convert(arguments);

if you want to use System.Drawing.Bitmap as IVideoFrame, there is BitmapVideoFrameWrapper wrapper class.

Contributors

License

Copyright © 2018, Vlad Jerca. Released under the MIT license.