FFMPEG .Net Wrapper – Part 1. Creating arguments

By | March 5, 2018

FFMPEG is a powerful command line tool that is widely used is all types of commercial and non-commercial applications. Chances are that if you used some type of video converter, it was FFMPEG that did all the heavy lifting. Using this command line tool is not the easiest thing to do. Official documentation is rich in various options that can be used in different situations. The standard way of interaction is through command line and it requires writing what we want to do in a certain order. Basically, the simplest example would look something like this


	ffmpeg.exe   -i inputFile  -“options”   outputFile

There could be slight modifications when switch “-i” that stands for input file is not the first item in the row, but overall in most cases we have to follow this pattern. If you ever tried to convert video by writing all switches and options in command line than you will know that this method is prone to errors. Some developers try different approach by creating batch scripts. The script would convert entire content of given folder. Not a bad idea, but what we lack is some level of flexibility. It is hard to reuse what we create when our needs change. In this case we need some different approach. The most common way is to create a wrapper that would simplify the process of passing arguments to the conversion tool. Google search will reveal that there are various projects available on GitHub for us to use. Some of them are older and development has already stopped. There are also commercial type projects that might be interesting to some of developers.

I decided to tackle this project mostly because I have a need for FFFMPEG wrapper in my main Web Application. This was actually interesting experience. I had a clear goal of creating something that can be easy modified and its functionality can be extended at the same time. The wrapper should be able to do couple things

    convert audio and video files
    have ability to create overload conversion methods
    provide feedback about current progress position to UI
    extend functionality by using various filters,
    extract frames, extract streams, cut and merge videos

As I mentioned before we need to pass required arguments to FFMPEG. It gets little bit complicated. The line below is an example of arguments that would be passed during video conversion


-vf scale=-1:720 -c:v libx264 -preset veryfast -crf 23 -c:a aac -b:a 160k  

Some of the parts of this line must remain constant and some of them can be modified. To make it simple we can change these small sections : “-1:720”, “libx264”, “veryfast”, “aac”, “160k”. These are parameters that would affect video resolution, converted file output format, encoding speed, audio type and bitrate. In some situations not all of these parameters are required in order to successfully convert video file. Therefore we have problem, because we should not retype these arguments for every possible combination of outcomes. Instead we need a method for combining multiple parts into one single argument line. And the perfect place for storing all our pieces is a Dictionary. We can search for keys defined with enums and return corresponding value.

Starting point is a class called EncodingArgs. It has private dictionary field called “_arguments” and new instance of the dictionary is created in the class constructor. There is also a method that retrieves value from a dictionary. For the demonstration purposes we include values that are required for converting video file. In addition we create necessary enum classes.


public class EncodingArgs
    {

        private Dictionary _arguments;
        

        public EncodingArgs()
        {
            _arguments = new Dictionary();

            
            //Audio codec
            _arguments.Add("Ac3", " -c:a ac3 ");
            _arguments.Add("Aac", " -c:a aac ");
            _arguments.Add("Mp3", " -c:a mp3 ");
            
            //Bitrate
            _arguments.Add("BitrateLow", " -ab 64k ");
            _arguments.Add("BitrateMedium", " -ab 112k ");
            _arguments.Add("BitrateNormal", " -ab 128k ");
            _arguments.Add("BitrateHigh", " -ab 160k ");
            _arguments.Add("BitrateVeryHigh", " -ab 192k ");

            //CRF
            _arguments.Add("CrfNormal", " -crf 23 ");
            _arguments.Add("CrfLow", " -crf 18 ");
            _arguments.Add("CrfHigh", " -crf 26 ");

            //Resize
            _arguments.Add("Mobile960", " -vf scale=960:-1 ");
            _arguments.Add("TV720p", " -vf scale=-1:720 ");
            _arguments.Add("FullHD1080p", " -vf scale=-1:1080 ");

            //Video Encoder
            _arguments.Add("Libx264", " -c:v libx264 ");
            _arguments.Add("Libxvid", " -c:v libxvid ");
            _arguments.Add("Libx265", " -c:v libx265 ");
            _arguments.Add("Mpeg2video", " -c:v Mpeg2video ");

            //Presets
            _arguments.Add("UltraFast", " -preset ultrafast ");
            _arguments.Add("SuperFast", " -preset superfast ");
            _arguments.Add("VeryFast", " -preset veryfast ");
            _arguments.Add("Faster", " -preset faster ");
            _arguments.Add("Fast", " -preset fast ");
            _arguments.Add("Medium", " -preset medium ");
            _arguments.Add("Slow", " -preset slow ");
            _arguments.Add("Slower", " -preset slower ");
            _arguments.Add("VerySlow", " -preset veryslow ");

        }

        public string GetValue(string argument)
        {

            string value = "";

            _arguments.TryGetValue(argument, out value);

            return value;
        }

    }

public enum VideoResize
    {
        Mobile960,
        TV720p,
        FullHD1080p
    }

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

public enum VideoEncoder
    {
        Libx264,
        Libxvid,
        Libx265,
    }

public enum ConstantRateFactor
    {
        CrfLow,
        CrfNormal,
        CrfHigh
    }

public enum AudioCodec
    {
        Aac,
        Ac3,
        Mp3
    }


public enum Bitrate
    {
        BitrateLow,
        BitrateMedium,
        BitrateNormal,
        BitrateHigh,
        BitrateVeryHigh,
    }

As you can see the strings that are stored in a dictionary consist of both switches and predefined values for given option. This way we can construct command line arguments by arranging selected pieces. This action is done in another class called VideoArgs. We can also create another class called AudioArgs that would contain methods for audio conversion. Convert method returns a string that is properly formatted. However, note that even though we retrieve most of the values from the dictionary, there are still some option switches that need to be included.

“ -i ” input file
“ -y “ override existing file
“-v quiet” hide information that is displayed before encoding begins
“-stats ” displays encoding statistics

Some of these options can be included in dictionary like everything else however switch referring to input file should be hard coded. We can also create conversion overload methods that would include or omit some options. At this point I decided to leave them the way it is. I will explain later why I included option ” -v quiet -stats ” in this class instead of storing it in EncodingArgs dictionary.


public class VideoArgs
    {
       
        private EncodingArgs _arguments;
        
        public VideoArgs()
        {
          
            _arguments = new EncodingArgs();
      
        }
    
        public string Convert(string inputFile,VideoEncoder videoEncoder, VideoResize videoResize, VideoPreset videoPreset, ConstantRateFactor videoConstantRateFactor, AudioCodec audioCodec, Bitrate audioBitrate, string outputFile)
        {
            var arguments = "-i " + inputFile +
                 " -v quiet -stats " +
                 _arguments.GetValue(videoResize.ToString()) +
                 _arguments.GetValue(videoEncoder.ToString()) +
                 _arguments.GetValue(videoPreset.ToString()) +
                 _arguments.GetValue(ConstantRateFactor.ToString()) +
                 _arguments.GetValue(audioCodec.ToString()) +
                 _arguments.GetValue(audioBitrate.ToString()) +
                 " -y " + outputFile;

            return arguments;

        }

     
    }

Leave a Reply

Your email address will not be published. Required fields are marked *