diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 08be2f8..5fc06b0 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -1,4 +1,5 @@ using FFMpegCore.FFMPEG.Argument; +using FFMpegCore.FFMPEG.Argument.Fluent; using FFMpegCore.FFMPEG.Enums; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -31,6 +32,18 @@ private string GetArgumentsString(params Argument[] args) return builder.BuildArguments(container); } + private string GetArgumentsString(ArgumentContainer container) + { + var resContainer = new ArgumentContainer { new InputArgument("input.mp4") }; + foreach (var a in container) + { + resContainer.Add(a.Value); + } + resContainer.Add(new OutputArgument("output.mp4")); + + return builder.BuildArguments(resContainer); + } + [TestMethod] public void Builder_BuildString_IO_1() { @@ -47,6 +60,15 @@ public void Builder_BuildString_Scale() Assert.AreEqual(str, "-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\""); } + + [TestMethod] + public void Builder_BuildString_Scale_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Scale(VideoSize.Hd)); + + Assert.AreEqual(str, "-i \"input.mp4\" -vf scale=-1:720 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_AudioCodec() { @@ -68,6 +90,14 @@ public void Builder_BuildString_Quiet() Assert.AreEqual(str, "-i \"input.mp4\" -hide_banner -loglevel warning \"output.mp4\""); } + + [TestMethod] + public void Builder_BuildString_AudioCodec_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().AudioCodec(AudioCodec.Aac, AudioQuality.Normal)); + Assert.AreEqual(str, "-i \"input.mp4\" -c:a aac -b:a 128k \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_BitStream() { @@ -76,6 +106,14 @@ public void Builder_BuildString_BitStream() Assert.AreEqual(str, "-i \"input.mp4\" -bsf:a h264_mp4toannexb \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_BitStream_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().BitStreamFilter(Channel.Audio, Filter.H264_Mp4ToAnnexB)); + + Assert.AreEqual(str, "-i \"input.mp4\" -bsf:a h264_mp4toannexb \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Concat() { @@ -86,6 +124,20 @@ public void Builder_BuildString_Concat() Assert.AreEqual(str, "-i \"concat:1.mp4|2.mp4|3.mp4|4.mp4\" \"output.mp4\""); } + + [TestMethod] + public void Builder_BuildString_Concat_Fluent() + { + var container = new ArgumentContainer() + .Concat(concatFiles) + .Output("output.mp4"); + + + var str = builder.BuildArguments(container); + + Assert.AreEqual(str, "-i \"concat:1.mp4|2.mp4|3.mp4|4.mp4\" \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Copy_Audio() { @@ -94,6 +146,14 @@ public void Builder_BuildString_Copy_Audio() Assert.AreEqual(str, "-i \"input.mp4\" -c:a copy \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Copy_Audio_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Copy(Channel.Audio)); + + Assert.AreEqual(str, "-i \"input.mp4\" -c:a copy \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Copy_Video() { @@ -102,6 +162,15 @@ public void Builder_BuildString_Copy_Video() Assert.AreEqual(str, "-i \"input.mp4\" -c:v copy \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Copy_Video_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Copy(Channel.Video)); + + Assert.AreEqual(str, "-i \"input.mp4\" -c:v copy \"output.mp4\""); + } + + [TestMethod] public void Builder_BuildString_Copy_Both() { @@ -110,6 +179,14 @@ public void Builder_BuildString_Copy_Both() Assert.AreEqual(str, "-i \"input.mp4\" -c copy \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Copy_Both_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Copy(Channel.Both)); + + Assert.AreEqual(str, "-i \"input.mp4\" -c copy \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_CpuSpeed() { @@ -118,6 +195,14 @@ public void Builder_BuildString_CpuSpeed() Assert.AreEqual(str, "-i \"input.mp4\" -quality good -cpu-used 10 -deadline realtime \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_CpuSpeed_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().CpuSpeed(10)); + + Assert.AreEqual(str, "-i \"input.mp4\" -quality good -cpu-used 10 -deadline realtime \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_ForceFormat() { @@ -126,6 +211,14 @@ public void Builder_BuildString_ForceFormat() Assert.AreEqual(str, "-i \"input.mp4\" -f libx264 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_ForceFormat_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().ForceFormat(VideoCodec.LibX264)); + + Assert.AreEqual(str, "-i \"input.mp4\" -f libx264 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_FrameOutputCount() { @@ -134,6 +227,14 @@ public void Builder_BuildString_FrameOutputCount() Assert.AreEqual(str, "-i \"input.mp4\" -vframes 50 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_FrameOutputCount_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().FrameOutputCount(50)); + + Assert.AreEqual(str, "-i \"input.mp4\" -vframes 50 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_FrameRate() { @@ -142,6 +243,14 @@ public void Builder_BuildString_FrameRate() Assert.AreEqual(str, "-i \"input.mp4\" -r 50 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_FrameRate_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().FrameRate(50)); + + Assert.AreEqual(str, "-i \"input.mp4\" -r 50 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Loop() { @@ -150,6 +259,14 @@ public void Builder_BuildString_Loop() Assert.AreEqual(str, "-i \"input.mp4\" -loop 50 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Loop_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Loop(50)); + + Assert.AreEqual(str, "-i \"input.mp4\" -loop 50 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Seek() { @@ -158,6 +275,14 @@ public void Builder_BuildString_Seek() Assert.AreEqual(str, "-i \"input.mp4\" -ss 00:00:10 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Seek_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Seek(TimeSpan.FromSeconds(10))); + + Assert.AreEqual(str, "-i \"input.mp4\" -ss 00:00:10 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Shortest() { @@ -166,6 +291,14 @@ public void Builder_BuildString_Shortest() Assert.AreEqual(str, "-i \"input.mp4\" -shortest \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Shortest_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Shortest()); + + Assert.AreEqual(str, "-i \"input.mp4\" -shortest \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Size() { @@ -174,6 +307,14 @@ public void Builder_BuildString_Size() Assert.AreEqual(str, "-i \"input.mp4\" -s 1920x1080 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Size_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Size(1920, 1080)); + + Assert.AreEqual(str, "-i \"input.mp4\" -s 1920x1080 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Speed() { @@ -182,6 +323,14 @@ public void Builder_BuildString_Speed() Assert.AreEqual(str, "-i \"input.mp4\" -preset fast \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Speed_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Speed(Speed.Fast)); + + Assert.AreEqual(str, "-i \"input.mp4\" -preset fast \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_DrawtextFilter() { @@ -197,6 +346,27 @@ public void Builder_BuildString_DrawtextFilter() Assert.AreEqual("-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow': fontfile=/path/to/font.ttf: fontcolor=white: fontsize=24: box=1: boxcolor=black@0.5: boxborderw=5: x=(w-text_w)/2: y=(h-text_h)/2\" \"output.mp4\"", str); } + [TestMethod] + public void Builder_BuildString_DrawtextFilter_Fluent() + { + var container = new ArgumentContainer(). + DrawText((options) => + { + options.Text = "Stack Overflow"; + options.FontPath = "/path/to/font.ttf"; + options.AddParam("fontcolor", "white") + .AddParam("fontsize", "24") + .AddParam("box", "1") + .AddParam("boxcolor", "black@0.5") + .AddParam("boxborderw", "5") + .AddParam("x", "(w-text_w)/2") + .AddParam("y", "(h-text_h)/2"); + }); + var str = GetArgumentsString(container); + + Assert.AreEqual("-i \"input.mp4\" -vf drawtext=\"text='Stack Overflow': fontfile=/path/to/font.ttf: fontcolor=white: fontsize=24: box=1: boxcolor=black@0.5: boxborderw=5: x=(w-text_w)/2: y=(h-text_h)/2\" \"output.mp4\"", str); + } + [TestMethod] public void Builder_BuildString_StartNumber() { @@ -205,6 +375,15 @@ public void Builder_BuildString_StartNumber() Assert.AreEqual(str, "-i \"input.mp4\" -start_number 50 \"output.mp4\""); } + + [TestMethod] + public void Builder_BuildString_StartNumber_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().StartNumber(50)); + + Assert.AreEqual(str, "-i \"input.mp4\" -start_number 50 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Threads_1() { @@ -213,6 +392,14 @@ public void Builder_BuildString_Threads_1() Assert.AreEqual(str, "-i \"input.mp4\" -threads 50 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Threads_1_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Threads(50)); + + Assert.AreEqual(str, "-i \"input.mp4\" -threads 50 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Threads_2() { @@ -220,6 +407,14 @@ public void Builder_BuildString_Threads_2() Assert.AreEqual(str, $"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\""); } + + [TestMethod] + public void Builder_BuildString_Threads_2_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().MultiThreaded()); + + Assert.AreEqual(str, $"-i \"input.mp4\" -threads {Environment.ProcessorCount} \"output.mp4\""); + } [TestMethod] public void Builder_BuildString_Codec() @@ -229,6 +424,14 @@ public void Builder_BuildString_Codec() Assert.AreEqual(str, "-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Codec_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().VideoCodec(VideoCodec.LibX264)); + + Assert.AreEqual(str, "-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Codec_Override() { @@ -237,6 +440,15 @@ public void Builder_BuildString_Codec_Override() Assert.AreEqual(str, "-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p -y \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Codec_Override_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().VideoCodec(VideoCodec.LibX264).Override()); + + Assert.AreEqual(str, "-i \"input.mp4\" -c:v libx264 -pix_fmt yuv420p -y \"output.mp4\""); + } + + [TestMethod] public void Builder_BuildString_Duration() { @@ -245,6 +457,14 @@ public void Builder_BuildString_Duration() Assert.AreEqual(str, "-i \"input.mp4\" -t 00:00:20 \"output.mp4\""); } + [TestMethod] + public void Builder_BuildString_Duration_Fluent() + { + var str = GetArgumentsString(new ArgumentContainer().Duration(TimeSpan.FromSeconds(20))); + + Assert.AreEqual(str, "-i \"input.mp4\" -t 00:00:20 \"output.mp4\""); + } + [TestMethod] public void Builder_BuildString_Raw() { diff --git a/FFMpegCore/FFMPEG/Argument/ArgumentContainerFluentExtensions.cs b/FFMpegCore/FFMPEG/Argument/ArgumentContainerFluentExtensions.cs new file mode 100644 index 0000000..daf905c --- /dev/null +++ b/FFMpegCore/FFMPEG/Argument/ArgumentContainerFluentExtensions.cs @@ -0,0 +1,357 @@ +using FFMpegCore.FFMPEG.Enums; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; + +namespace FFMpegCore.FFMPEG.Argument.Fluent +{ + public static class ArgumentContainerFluentExtensions + { + public static ArgumentContainer AudioCodec(this ArgumentContainer container, AudioCodec codec) + { + container.Add(new AudioCodecArgument(codec)); + return container; + } + + public static ArgumentContainer AudioCodec(this ArgumentContainer container, AudioCodec codec, AudioQuality bitrate) + { + container.Add(new AudioCodecArgument(codec, bitrate)); + return container; + } + + public static ArgumentContainer AudioCodec(this ArgumentContainer container, AudioCodec codec, int bitrate) + { + container.Add(new AudioCodecArgument(codec, bitrate)); + return container; + } + + public static ArgumentContainer AudioSamplingRate(this ArgumentContainer container) + { + container.Add(new AudioSamplingRateArgument()); + return container; + } + + public static ArgumentContainer AudioSamplingRate(this ArgumentContainer container, int sampleRate) + { + container.Add(new AudioSamplingRateArgument(sampleRate)); + return container; + } + + public static ArgumentContainer BitStreamFilter(this ArgumentContainer container, Channel first, Filter second) + { + container.Add(new BitStreamFilterArgument(first, second)); + return container; + } + + public static ArgumentContainer Concat(this ArgumentContainer container, IEnumerable paths) + { + container.Add(new ConcatArgument(paths)); + return container; + } + + public static ArgumentContainer ConstantRateFactor(this ArgumentContainer container, int crf) + { + container.Add(new ConstantRateFactorArgument(crf)); + return container; + } + + public static ArgumentContainer Copy(this ArgumentContainer container) + { + container.Add(new CopyArgument()); + return container; + } + + public static ArgumentContainer Copy(this ArgumentContainer container, Channel value) + { + container.Add(new CopyArgument(value)); + return container; + } + + public static ArgumentContainer CpuSpeed(this ArgumentContainer container, int value) + { + container.Add(new CpuSpeedArgument(value)); + return container; + } + + public static ArgumentContainer DisableChannel(this ArgumentContainer container, Channel channel) + { + container.Add(new DisableChannelArgument(channel)); + return container; + } + + public class DrawTextOptions + { + public string Text { get; set; } + public string FontPath { get; set; } + public List<(string, string)> Params { get; private set; } + + public DrawTextOptions() + { + Params = new List<(string, string)>(); + } + + public DrawTextOptions AddParam(string key, string value) + { + Params.Add((key, value)); + return this; + } + } + + public static ArgumentContainer DrawText(this ArgumentContainer container, Action builder) + { + var argumentParams = new DrawTextOptions(); + builder.Invoke(argumentParams); + container.Add(new DrawTextArgument(argumentParams.Text, argumentParams.FontPath, argumentParams.Params.ToArray())); + return container; + } + + public static ArgumentContainer DrawText(this ArgumentContainer container, string text, string fontPath, params (string, string)[] optionalArguments) + { + container.Add(new DrawTextArgument(text, fontPath, optionalArguments)); + return container; + } + + public static ArgumentContainer Duration(this ArgumentContainer container, TimeSpan? duration) + { + container.Add(new DurationArgument(duration)); + return container; + } + + public static ArgumentContainer FastStart(this ArgumentContainer container) + { + container.Add(new FaststartArgument()); + return container; + } + + public static ArgumentContainer ForceFormat(this ArgumentContainer container, VideoCodec codec) + { + container.Add(new ForceFormatArgument(codec)); + return container; + } + + public static ArgumentContainer FrameOutputCount(this ArgumentContainer container, int count) + { + container.Add(new FrameOutputCountArgument(count)); + return container; + } + + public static ArgumentContainer FrameRate(this ArgumentContainer container, double framerate) + { + container.Add(new FrameRateArgument(framerate)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, string path) + { + container.Add(new InputArgument(path)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, IEnumerable paths) + { + container.Add(new InputArgument(paths.ToArray())); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, params string[] paths) + { + container.Add(new InputArgument(paths)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, VideoInfo path) + { + container.Add(new InputArgument(path)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, IEnumerable paths) + { + container.Add(new InputArgument(paths.ToArray())); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, params VideoInfo[] paths) + { + container.Add(new InputArgument(paths)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, FileInfo path) + { + container.Add(new InputArgument(path)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, IEnumerable paths) + { + container.Add(new InputArgument(paths.ToArray())); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, params FileInfo[] paths) + { + container.Add(new InputArgument(paths)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, Uri uri) + { + container.Add(new InputArgument(uri)); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, IEnumerable uris) + { + container.Add(new InputArgument(uris.ToArray())); + return container; + } + + public static ArgumentContainer Input(this ArgumentContainer container, params Uri[] uris) + { + container.Add(new InputArgument(uris)); + return container; + } + + public static ArgumentContainer Loop(this ArgumentContainer container, int times) + { + container.Add(new LoopArgument(times)); + return container; + } + + public static ArgumentContainer Output(this ArgumentContainer container, string path) + { + container.Add(new OutputArgument(path)); + return container; + } + + public static ArgumentContainer Output(this ArgumentContainer container, VideoInfo path) + { + container.Add(new OutputArgument(path)); + return container; + } + + public static ArgumentContainer Output(this ArgumentContainer container, FileInfo path) + { + container.Add(new OutputArgument(path)); + return container; + } + + public static ArgumentContainer Output(this ArgumentContainer container, Uri path) + { + container.Add(new OutputArgument(path)); + return container; + } + + public static ArgumentContainer Override(this ArgumentContainer container) + { + container.Add(new OverrideArgument()); + return container; + } + + public static ArgumentContainer RemoveMetadata(this ArgumentContainer container) + { + container.Add(new RemoveMetadataArgument()); + return container; + } + + public static ArgumentContainer Scale(this ArgumentContainer container, Size value) + { + container.Add(new ScaleArgument(value)); + return container; + } + + public static ArgumentContainer Scale(this ArgumentContainer container, VideoSize value) + { + container.Add(new ScaleArgument(value)); + return container; + } + + public static ArgumentContainer Scale(this ArgumentContainer container, int width, int height) + { + container.Add(new ScaleArgument(width, height)); + return container; + } + + public static ArgumentContainer Seek(this ArgumentContainer container, TimeSpan? value) + { + container.Add(new SeekArgument(value)); + return container; + } + + public static ArgumentContainer Shortest(this ArgumentContainer container) + { + container.Add(new ShortestArgument(true)); + return container; + } + + public static ArgumentContainer Size(this ArgumentContainer container, Size value) + { + container.Add(new SizeArgument(value)); + return container; + } + + public static ArgumentContainer Size(this ArgumentContainer container, VideoSize value) + { + container.Add(new SizeArgument(value)); + return container; + } + + public static ArgumentContainer Size(this ArgumentContainer container, int width, int height) + { + container.Add(new SizeArgument(width, height)); + return container; + } + + public static ArgumentContainer Speed(this ArgumentContainer container, Speed value) + { + container.Add(new SpeedArgument(value)); + return container; + } + + public static ArgumentContainer StartNumber(this ArgumentContainer container, int value) + { + container.Add(new StartNumberArgument(value)); + return container; + } + + public static ArgumentContainer Threads(this ArgumentContainer container, int value) + { + container.Add(new ThreadsArgument(value)); + return container; + } + + public static ArgumentContainer MultiThreaded(this ArgumentContainer container) + { + container.Add(new ThreadsArgument(true)); + return container; + } + + public static ArgumentContainer Transpose(this ArgumentContainer container, int transpose) + { + container.Add(new TransposeArgument(transpose)); + return container; + } + + public static ArgumentContainer VariableBitRate(this ArgumentContainer container, int vbr) + { + container.Add(new VariableBitRateArgument(vbr)); + return container; + } + + public static ArgumentContainer VideoCodec(this ArgumentContainer container, VideoCodec codec) + { + container.Add(new VideoCodecArgument(codec)); + return container; + } + + public static ArgumentContainer VideoCodec(this ArgumentContainer container, VideoCodec codec, int bitrate) + { + container.Add(new VideoCodecArgument(codec, bitrate)); + return container; + } + } +} diff --git a/README.md b/README.md index b58aa79..4a34689 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,16 @@ Custom video converting presets could be created with help of `ArgumentContainer var container = new ArgumentContainer(); container.Add(new VideoCodecArgument(VideoCodec.LibX264)); container.Add(new ScaleArgument(VideoSize.Hd)); +``` +or use Fluent API +```csharp +var container = new ArgumentContainer() + .VideoCodec(VideoCodec.LibX264) + .Scale(VideoSize.Hd); +``` + +```csharp var ffmpeg = new FFMpeg(); var result = ffmpeg.Convert(container, new FileInfo("input.mp4"), new FileInfo("output.mp4")); ```