Pan filter implementation and testing

This commit is contained in:
alex6dj 2021-10-04 12:27:24 -04:00
parent 78a703fc93
commit 7a661b6ab3
4 changed files with 138 additions and 3 deletions

View file

@ -414,5 +414,38 @@ public void Builder_BuildString_ForcePixelFormat()
.OutputToFile("output.mp4", false, opt => opt.ForcePixelFormat("yuv444p")).Arguments;
Assert.AreEqual("-i \"input.mp4\" -pix_fmt yuv444p \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_PanAudioFilterChannelNumber()
{
var str = FFMpegArguments.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false,
opt => opt.WithAudioFilters(filterOptions => filterOptions.Pan(2, "c0=c1", "c1=c1")))
.Arguments;
Assert.AreEqual("-i \"input.mp4\" -af \"pan=2c|c0=c1|c1=c1\" \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_PanAudioFilterChannelLayout()
{
var str = FFMpegArguments.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false,
opt => opt.WithAudioFilters(filterOptions => filterOptions.Pan("stereo", "c0=c0", "c1=c1")))
.Arguments;
Assert.AreEqual("-i \"input.mp4\" -af \"pan=stereo|c0=c0|c1=c1\" \"output.mp4\"", str);
}
[TestMethod]
public void Builder_BuildString_PanAudioFilterChannelNoOutputDefinition()
{
var str = FFMpegArguments.FromFileInput("input.mp4")
.OutputToFile("output.mp4", false,
opt => opt.WithAudioFilters(filterOptions => filterOptions.Pan("stereo")))
.Arguments;
Assert.AreEqual("-i \"input.mp4\" -af \"pan=stereo\" \"output.mp4\"", str);
}
}
}

View file

@ -223,5 +223,63 @@ public void Audio_ToAAC_Args_Pipe_InvalidSampleRate()
.WithAudioCodec(AudioCodec.Aac))
.ProcessSynchronously());
}
[TestMethod, Timeout(10000)]
public void Audio_Pan_ToMono()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
var success = FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, true,
argumentOptions => argumentOptions
.WithAudioFilters(filter => filter.Pan(1, "c0 < 0.9 * c0 + 0.1 * c1")))
.ProcessSynchronously();
var mediaAnalysis = FFProbe.Analyse(outputFile);
Assert.IsTrue(success);
Assert.AreEqual(1, mediaAnalysis.AudioStreams.Count);
}
[TestMethod, Timeout(10000)]
public void Audio_Pan_ToMonoNoDefinitions()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
var success = FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, true,
argumentOptions => argumentOptions
.WithAudioFilters(filter => filter.Pan(1)))
.ProcessSynchronously();
var mediaAnalysis = FFProbe.Analyse(outputFile);
Assert.IsTrue(success);
Assert.AreEqual(1, mediaAnalysis.AudioStreams.Count);
}
[TestMethod, Timeout(10000)]
public void Audio_Pan_ToMonoChannelsToOutputDefinitionsMismatch()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
var ex = Assert.ThrowsException<ArgumentException>(() => FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, true,
argumentOptions => argumentOptions
.WithAudioFilters(filter => filter.Pan(1, "c0=c0", "c1=c1")))
.ProcessSynchronously());
}
[TestMethod, Timeout(10000)]
public void Audio_Pan_ToMonoChannelsLayoutToOutputDefinitionsMismatch()
{
using var outputFile = new TemporaryFile($"out{VideoType.Mp4.Extension}");
var ex = Assert.ThrowsException<FFMpegException>(() => FFMpegArguments.FromFileInput(TestResources.Mp4Video)
.OutputToFile(outputFile, true,
argumentOptions => argumentOptions
.WithAudioFilters(filter => filter.Pan("mono", "c0=c0", "c1=c1")))
.ProcessSynchronously());
}
}
}

View file

@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using FFMpegCore.Enums;
using FFMpegCore.Exceptions;
namespace FFMpegCore.Arguments
@ -43,7 +41,10 @@ public interface IAudioFilterArgument
public class AudioFilterOptions
{
public List<IAudioFilterArgument> Arguments { get; } = new List<IAudioFilterArgument>();
public AudioFilterOptions Pan(string channelLayout, params string[] outputDefinitions) => WithArgument(new PanArgument(channelLayout, outputDefinitions));
public AudioFilterOptions Pan(int channels, params string[] outputDefinitions) => WithArgument(new PanArgument(channels, outputDefinitions));
private AudioFilterOptions WithArgument(IAudioFilterArgument argument)
{
Arguments.Add(argument);

View file

@ -0,0 +1,43 @@
using System;
using System.Linq;
namespace FFMpegCore.Arguments
{
/// <summary>
/// Represents scale parameter
/// </summary>
public class PanArgument : IAudioFilterArgument
{
public readonly string ChannelLayout;
private readonly string[] _outputDefinitions;
public PanArgument(string channelLayout, params string[] outputDefinitions)
{
if (string.IsNullOrWhiteSpace(channelLayout))
{
throw new ArgumentException("The channel layout must be set" ,nameof(channelLayout));
}
ChannelLayout = channelLayout;
_outputDefinitions = outputDefinitions;
}
public PanArgument(int channels, params string[] outputDefinitions)
{
if (channels <= 0) throw new ArgumentOutOfRangeException(nameof(channels));
if (outputDefinitions.Length > channels)
throw new ArgumentException("The number of output definitions must be equal or lower than number of channels", nameof(outputDefinitions));
ChannelLayout = $"{channels}c";
_outputDefinitions = outputDefinitions;
}
public string Key { get; } = "pan";
public string Value =>
string.Join("|", Enumerable.Empty<string>().Append(ChannelLayout).Concat(_outputDefinitions));
}
}