diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 18a8c7d..daa3eda 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -192,6 +192,17 @@ public void Builder_BuildString_Transpose() Assert.AreEqual("-i \"input.mp4\" -vf \"transpose=2\" \"output.mp4\"", str); } + [TestMethod] + public void Builder_BuildString_Mirroring() + { + var str = FFMpegArguments.FromFileInput("input.mp4") + .OutputToFile("output.mp4", false, opt => opt + .WithVideoFilters(filterOptions => filterOptions + .Mirror(Mirroring.Horizontal))) + .Arguments; + Assert.AreEqual("-i \"input.mp4\" -vf \"hflip\" \"output.mp4\"", str); + } + [TestMethod] public void Builder_BuildString_TransposeScale() { diff --git a/FFMpegCore.sln b/FFMpegCore.sln index 27eab0a..7a27980 100644 --- a/FFMpegCore.sln +++ b/FFMpegCore.sln @@ -1,13 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.329 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore", "FFMpegCore\FFMpegCore.csproj", "{19DE2EC2-9955-4712-8096-C22EF6713E4F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore", "FFMpegCore\FFMpegCore.csproj", "{19DE2EC2-9955-4712-8096-C22EF6713E4F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Test", "FFMpegCore.Test\FFMpegCore.Test.csproj", "{F20C8353-72D9-454B-9F16-3624DBAD2328}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FFMpegCore.Examples", "FFMpegCore.Examples\FFMpegCore.Examples.csproj", "{3125CF91-FFBD-4E4E-8930-247116AFE772}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMpegCore.Examples", "FFMpegCore.Examples\FFMpegCore.Examples.csproj", "{3125CF91-FFBD-4E4E-8930-247116AFE772}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/FFMpegCore/FFMpeg/Arguments/SetMirroringArgument.cs b/FFMpegCore/FFMpeg/Arguments/SetMirroringArgument.cs new file mode 100644 index 0000000..f042f77 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/SetMirroringArgument.cs @@ -0,0 +1,32 @@ +using FFMpegCore.Enums; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FFMpegCore.Arguments +{ + public class SetMirroringArgument : IVideoFilterArgument + { + public SetMirroringArgument(Mirroring mirroring) + { + Mirroring = mirroring; + } + + public Mirroring Mirroring { get; set; } + + public string Key => string.Empty; + + public string Value + { + get + { + return Mirroring switch + { + Mirroring.Horizontal => "hflip", + Mirroring.Vertical => "vflip", + _ => throw new ArgumentOutOfRangeException(nameof(Mirroring)) + }; + } + } + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs b/FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs index f7fef93..fa4ae1e 100644 --- a/FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs +++ b/FFMpegCore/FFMpeg/Arguments/VideoFiltersArgument.cs @@ -17,12 +17,20 @@ public VideoFiltersArgument(VideoFilterOptions options) public string Text => GetText(); - public string GetText() + private string GetText() { if (!Options.Arguments.Any()) throw new FFMpegArgumentException("No video-filter arguments provided"); - return $"-vf \"{string.Join(", ", Options.Arguments.Where(arg => !string.IsNullOrEmpty(arg.Value)).Select(arg => $"{arg.Key}={arg.Value.Replace(",", "\\,")}"))}\""; + var arguments = Options.Arguments + .Where(arg => !string.IsNullOrEmpty(arg.Value)) + .Select(arg => + { + var escapedValue = arg.Value.Replace(",", "\\,"); + return string.IsNullOrEmpty(arg.Key) ? escapedValue : $"{arg.Key}={escapedValue}"; + }); + + return $"-vf \"{string.Join(", ", arguments)}\""; } } @@ -40,6 +48,7 @@ public class VideoFilterOptions public VideoFilterOptions Scale(int width, int height) => WithArgument(new ScaleArgument(width, height)); public VideoFilterOptions Scale(Size size) => WithArgument(new ScaleArgument(size)); public VideoFilterOptions Transpose(Transposition transposition) => WithArgument(new TransposeArgument(transposition)); + public VideoFilterOptions Mirror(Mirroring mirroring) => WithArgument(new SetMirroringArgument(mirroring)); public VideoFilterOptions DrawText(DrawTextOptions drawTextOptions) => WithArgument(new DrawTextArgument(drawTextOptions)); private VideoFilterOptions WithArgument(IVideoFilterArgument argument) diff --git a/FFMpegCore/FFMpeg/Enums/Mirroring.cs b/FFMpegCore/FFMpeg/Enums/Mirroring.cs new file mode 100644 index 0000000..5768163 --- /dev/null +++ b/FFMpegCore/FFMpeg/Enums/Mirroring.cs @@ -0,0 +1,8 @@ +namespace FFMpegCore.Enums +{ + public enum Mirroring + { + Vertical, + Horizontal + } +} diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index 47b41d8..bf9e682 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -9,11 +9,9 @@ 3.0.0.0 3.0.0.0 3.0.0.0 - - Fixes for RawVideoPipeSource hanging (thanks to max619) -- Added .OutputToUrl(..) method for outputting to url using supported protocol (thanks to TFleury) -- Improved timespan parsing (thanks to test-in-prod) + - Added support for mirroring video filter (thanks gorobvictor) 8 - 4.1.0 + 4.2.0 MIT Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev ffmpeg ffprobe convert video audio mediafile resize analyze muxing