Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ public FilterParameterAttribute(string name)
public Type Formatter { get; set; }

public Type Binding { get; set; }

public int Order { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ private static void FillFilterParameterAttributes(FilterSerializerData filterSer
IsDefault = filterPropertyIsDefault
});
}

if (filterSerializerData.Parameters.Any(f => f.Parameter.Order > 0))
{
filterSerializerData.Parameters = filterSerializerData.Parameters.OrderBy(f => f.Parameter.Order).ToList();
}
}

private static object GetFilterSerializationBindingValue(FilterParameterAttribute filterParameterAttribute, PropertyInfo propertyInfo, FilterBindingContext context, IFilter filter)
Expand Down
17 changes: 17 additions & 0 deletions Hudl.FFmpeg.Core/Formatters/SingleQuoteExpressionFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Hudl.FFmpeg.Interfaces;
using System.Text.RegularExpressions;

namespace Hudl.FFmpeg.Formatters
{
public class SingleQuoteExpressionFormatter : IFormatter
{
public string Format(object value)
{
var valueAsString = value.ToString();
var valueIsNumeric = Regex.IsMatch(valueAsString, @"^\d+$");
return (!valueIsNumeric)
? $"'{valueAsString}'"
: valueAsString;
}
}
}
14 changes: 14 additions & 0 deletions Hudl.FFmpeg.Core/Formatters/TimeSpanSecondsFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using Hudl.FFmpeg.Formatters.Utility;
using Hudl.FFmpeg.Interfaces;

namespace Hudl.FFmpeg.Formatters
{
public class TimeSpanSecondsFormatter : IFormatter
{
public string Format(object value)
{
return ((TimeSpan)value).TotalSeconds.ToString();
}
}
}
2 changes: 2 additions & 0 deletions Hudl.FFmpeg.Core/Hudl.FFmpeg.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@
<Compile Include="Formatters\MapSettingsFormatter.cs" />
<Compile Include="Formatters\RatioFractionalStringFormatter.cs" />
<Compile Include="Formatters\RatioStringFormatter.cs" />
<Compile Include="Formatters\SingleQuoteExpressionFormatter.cs" />
<Compile Include="Formatters\SingleQuoteFormatter.cs" />
<Compile Include="Formatters\SizeFormatter.cs" />
<Compile Include="Formatters\SizeHeightFormatter.cs" />
<Compile Include="Formatters\SizeWidthFormatter.cs" />
<Compile Include="Formatters\TimeSpanSecondsFormatter.cs" />
<Compile Include="Formatters\TimeSpanFormatter.cs" />
<Compile Include="Formatters\Utility\FormattingUtility.cs" />
<Compile Include="Interfaces\IValidator.cs" />
Expand Down
18 changes: 18 additions & 0 deletions Hudl.FFmpeg/Filters/AReverse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Hudl.FFmpeg.Attributes;
using Hudl.FFmpeg.Filters.Attributes;
using Hudl.FFmpeg.Filters.Interfaces;
using Hudl.FFmpeg.Resources.BaseTypes;

namespace Hudl.FFmpeg.Filters
{
/// <summary>
/// Reverses an audio clip. Note that it will buffer the entire clip,
/// so it's recommented to use trimming.
/// </summary>
[ForStream(Type = typeof(AudioStream))]
[Filter(Name ="areverse", MinInputs = 1, MaxInputs = 1)]
public class AReverse : IFilter
{
//This filter accepts no parameters
}
}
17 changes: 17 additions & 0 deletions Hudl.FFmpeg/Filters/AlphaExtract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Hudl.FFmpeg.Attributes;
using Hudl.FFmpeg.Filters.Attributes;
using Hudl.FFmpeg.Filters.Interfaces;
using Hudl.FFmpeg.Resources.BaseTypes;

namespace Hudl.FFmpeg.Filters
{
/// <summary>
/// Extracts the alpha component from the input as a grayscale video.
/// </summary>
[ForStream(Type = typeof(VideoStream))]
[Filter(Name ="alphaextract", MinInputs = 1, MaxInputs = 1)]
public class AlphaExtract : IFilter
{
//This filter accepts no parameters
}
}
18 changes: 18 additions & 0 deletions Hudl.FFmpeg/Filters/AlphaMerge.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Hudl.FFmpeg.Attributes;
using Hudl.FFmpeg.Filters.Attributes;
using Hudl.FFmpeg.Filters.Interfaces;
using Hudl.FFmpeg.Resources.BaseTypes;

namespace Hudl.FFmpeg.Filters
{
/// <summary>
/// Adds or replaces the alpha component of the primary input with
/// the grayscale value of a second input.
/// </summary>
[ForStream(Type = typeof(VideoStream))]
[Filter(Name ="alphamerge", MinInputs = 2, MaxInputs = 2)]
public class AlphaMerge : IFilter
{
//This filter accepts no parameters
}
}
13 changes: 10 additions & 3 deletions Hudl.FFmpeg/Filters/Blend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,32 @@ public Blend()
{
Mode = BlendVideoModeType.and;
Option = BlendVideoOptionType.all_expr;
RepeatLast = true;
}
public Blend(string expression)
: this()
{
Expression = expression;
}

[FilterParameter]
[FilterParameter(Order = 1)]
public BlendVideoOptionType? Option { get; set; }

[FilterParameter(Default = BlendVideoModeType.and)]
[FilterParameter(Order = 2, Default = BlendVideoModeType.and)]
public BlendVideoModeType? Mode { get; set; }

/// <summary>
/// the blend expression details can be found at http://ffmpeg.org/ffmpeg-all.html#blend.
/// </summary>
[FilterParameter(Formatter = typeof(SingleQuoteFormatter))]
[FilterParameter(Order = 3, Formatter = typeof(SingleQuoteFormatter))]
public string Expression { get; set; }

[FilterParameter(Order = 4, Name = "repeatlast", Default = true, Formatter = typeof(BoolToInt32Formatter))]
public bool RepeatLast { get; set; }

[FilterParameter(Order = 5, Name = "shortest", Default = false, Formatter = typeof(BoolToInt32Formatter))]
public bool Shortest { get; set; }

public virtual MetadataInfoTreeContainer EditInfo(MetadataInfoTreeContainer infoToUpdate, List<MetadataInfoTreeContainer> suppliedInfo)
{
infoToUpdate.VideoStream.VideoMetadata.Duration = suppliedInfo.Min(r => r.VideoStream.VideoMetadata.Duration);
Expand Down
2 changes: 1 addition & 1 deletion Hudl.FFmpeg/Filters/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class Color : IFilter, IMetadataManipulation
[FilterParameter(Name = "r", Default = 25)]
public int? FrameRate { get; set; }

[FilterParameter(Name = "d", Formatter = typeof(TimeSpanFormatter))]
[FilterParameter(Name = "d", Formatter = typeof(TimeSpanSecondsFormatter))]
[Validate(typeof(TimeSpanGreterThanZeroValidator))]
public TimeSpan? Duration { get; set; }

Expand Down
4 changes: 2 additions & 2 deletions Hudl.FFmpeg/Filters/Pad.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ public Pad(string expression)
public int? Height { get; set; }

[FilterParameter(Name = "x")]
[Validate(LogicalOperators.GreaterThan, 0)]
[Validate(LogicalOperators.GreaterThanOrEqual, 0)]
public int? X { get; set; }

[FilterParameter(Name = "y")]
[Validate(LogicalOperators.GreaterThan, 0)]
[Validate(LogicalOperators.GreaterThanOrEqual, 0)]
public int? Y { get; set; }

[FilterParameter(Name = "color")]
Expand Down
18 changes: 18 additions & 0 deletions Hudl.FFmpeg/Filters/Reverse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Hudl.FFmpeg.Attributes;
using Hudl.FFmpeg.Filters.Attributes;
using Hudl.FFmpeg.Filters.Interfaces;
using Hudl.FFmpeg.Resources.BaseTypes;

namespace Hudl.FFmpeg.Filters
{
/// <summary>
/// Reverses a video clip. Note that it will buffer the entire clip,
/// so it's recommented to use trimming.
/// </summary>
[ForStream(Type = typeof(VideoStream))]
[Filter(Name ="reverse", MinInputs = 1, MaxInputs = 1)]
public class Reverse : IFilter
{
//This filter accepts no parameters
}
}
63 changes: 63 additions & 0 deletions Hudl.FFmpeg/Filters/ZoomPan.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Hudl.FFmpeg.Attributes;
using Hudl.FFmpeg.Common;
using Hudl.FFmpeg.Enums;
using Hudl.FFmpeg.Filters.Attributes;
using Hudl.FFmpeg.Filters.Interfaces;
using Hudl.FFmpeg.Formatters;
using Hudl.FFmpeg.Metadata;
using Hudl.FFmpeg.Resources.BaseTypes;
using System.Collections.Generic;
using System.Drawing;

namespace Hudl.FFmpeg.Filters
{
/// <summary>
/// Applies Zoom & Pan effect.
/// </summary>
[ForStream(Type = typeof(VideoStream))]
[Filter(Name ="zoompan", MinInputs = 1, MaxInputs = 1)]
public class ZoomPan : IFilter
{
public ZoomPan()
{
}

public ZoomPan(string zoom, string x, string y, string d, double? fps, Size? size)
{
Zoom = zoom;
X = x;
Y = y;
D = d;
S = size;
Fps = fps;
}

public ZoomPan(string zoom, string x, string y, string d, double? fps, ScalePresetType? preset)
: this(zoom, x, y, d, fps, (Size?)null)
{
Size sizeValue = default(Size);
if (preset.HasValue && Helpers.ScalingPresets.TryGetValue(preset.Value, out sizeValue))
{
S = sizeValue;
}
}

[FilterParameter(Name = "zoom", Formatter = typeof(SingleQuoteExpressionFormatter))]
public string Zoom { get; set; }

[FilterParameter(Name = "x", Formatter = typeof(SingleQuoteExpressionFormatter))]
public string X { get; set; }

[FilterParameter(Name = "y", Formatter = typeof(SingleQuoteExpressionFormatter))]
public string Y { get; set; }

[FilterParameter(Name = "d")]
public string D { get; set; }

[FilterParameter(Name = "s", Formatter = typeof(SizeFormatter))]
public Size? S { get; set; }

[FilterParameter(Name = "fps")]
public double? Fps { get; set; }
}
}
5 changes: 5 additions & 0 deletions Hudl.FFmpeg/Hudl.Ffmpeg.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@
<Compile Include="Enums\FpsRoundType.cs" />
<Compile Include="Enums\AvoidNegativeTimestampsType.cs" />
<Compile Include="Filters\AEvalSrc.cs" />
<Compile Include="Filters\AlphaMerge.cs" />
<Compile Include="Filters\Color.cs" />
<Compile Include="Filters\APad.cs" />
<Compile Include="Filters\AReverse.cs" />
<Compile Include="Filters\AlphaExtract.cs" />
<Compile Include="Filters\ZoomPan.cs" />
<Compile Include="Filters\Reverse.cs" />
<Compile Include="Filters\Rotate.cs" />
<Compile Include="Filters\NullSrc.cs" />
<Compile Include="Filters\ANullSrc.cs" />
Expand Down
70 changes: 69 additions & 1 deletion Hudl.Ffmpeg.Tests/Filter/FilterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,14 @@ public void Blend_Verify()
filter.Expression = "A*(if(gte(T,{0}),1,T/{0}))+B*(1-(if(gte(T,{0}),1,T/{0})))";
filterValue.Append("blend=all_expr='A*(if(gte(T,{0}),1,T/{0}))+B*(1-(if(gte(T,{0}),1,T/{0})))'");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.RepeatLast = false;
filterValue.Append(":repeatlast=0");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.Shortest = true;
filterValue.Append(":shortest=1");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));
}

[Fact]
Expand Down Expand Up @@ -692,7 +700,7 @@ public void Color_Verify()
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.Duration = TimeSpan.FromSeconds(3.222);
filterValue.Append(":d=00:00:03.222");
filterValue.Append(":d=3.222");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.SampleAspectRatio = Ratio.Create(1, 1);
Expand All @@ -702,6 +710,66 @@ public void Color_Verify()

}

[Fact]
public void ZoomPan_Verify()
{
var filter = FilterFactory.CreateEmpty<Hudl.FFmpeg.Filters.ZoomPan>();
var filterValue = new StringBuilder(100);
Assert.DoesNotThrow(() => FilterSerializer.Serialize(filter));

filter.Zoom = "2";
filterValue.Append("zoompan=zoom=2");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.X = "100";
filterValue.Append(":x=100");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.Y = "200";
filterValue.Append(":y=200");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.D = "1";
filterValue.Append(":d=1");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.S = new Size(1280, 720);
filterValue.Append(":s=1280x720");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

filter.Fps = 30;
filterValue.Append(":fps=30");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter));

var filter2 = FilterFactory.CreateEmpty<Hudl.FFmpeg.Filters.ZoomPan>();
filterValue = new StringBuilder(100);
Assert.DoesNotThrow(() => FilterSerializer.Serialize(filter2));

filter2.Zoom = "in_w";
filterValue.Append("zoompan=zoom='in_w'");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));

filter2.X = "in_w+2";
filterValue.Append(":x='in_w+2'");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));

filter2.Y = "out_w+3";
filterValue.Append(":y='out_w+3'");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));

filter2.D = "1";
filterValue.Append(":d=1");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));

filter2.S = new Size(1280, 720);
filterValue.Append(":s=1280x720");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));

filter2.Fps = 30;
filterValue.Append(":fps=30");
Assert.Equal(filterValue.ToString(), FilterSerializer.Serialize(filter2));
}

private class FilterFactory
{
internal static TFilter CreateEmpty<TFilter>()
Expand Down