From 8a764eccc7e0ca37d9df2e0da8694a8dbdb2bdcc Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Fri, 1 Mar 2024 07:08:16 -0600
Subject: [PATCH 1/7] Add MultiInputArgument
---
.../FFMpeg/Arguments/MultiInputArgument.cs | 47 +++++++++++++++++++
FFMpegCore/FFMpeg/FFMpegArguments.cs | 2 +
FFMpegCore/FFMpegCore.csproj | 4 +-
3 files changed, 52 insertions(+), 1 deletion(-)
create mode 100644 FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
diff --git a/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs b/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
new file mode 100644
index 0000000..e64d96a
--- /dev/null
+++ b/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
@@ -0,0 +1,47 @@
+namespace FFMpegCore.Arguments
+{
+ ///
+ /// Represents input parameters for multiple files
+ ///
+ public class MultiInputArgument : IInputArgument
+ {
+ public readonly bool VerifyExists;
+ public readonly string[] FilePaths;
+
+ public MultiInputArgument(bool verifyExists, params string[] filePaths)
+ {
+ VerifyExists = verifyExists;
+ FilePaths = filePaths;
+ }
+
+ public MultiInputArgument(string[] filePaths, bool verifyExists) : this(verifyExists, filePaths) { }
+
+ public void Pre()
+ {
+ if (VerifyExists)
+ {
+ var missingFiles = new List();
+ foreach (var filePath in FilePaths)
+ {
+ if (!File.Exists(filePath))
+ {
+ missingFiles.Add(filePath);
+ }
+ }
+
+ if (missingFiles.Any())
+ {
+ throw new FileNotFoundException($"The following input files were not found: {string.Join(", ", missingFiles)}");
+ }
+ }
+ }
+
+ public Task During(CancellationToken cancellationToken = default) => Task.CompletedTask;
+ public void Post() { }
+
+ ///
+ /// Generates a combined input argument text for all file paths
+ ///
+ public string Text => string.Join(" ", FilePaths.Select(filePath => $"-i \"{filePath}\""));
+ }
+}
diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs
index cfc6d9d..cf57b84 100644
--- a/FFMpegCore/FFMpeg/FFMpegArguments.cs
+++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs
@@ -21,6 +21,7 @@ private string GetText()
public static FFMpegArguments FromConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments);
+ public static FFMpegArguments FromFileInput(string[] filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public static FFMpegArguments FromUrlInput(Uri uri, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public static FFMpegArguments FromDeviceInput(string device, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputDeviceArgument(device), addArguments);
@@ -35,6 +36,7 @@ public FFMpegArguments WithGlobalOptions(Action configure
public FFMpegArguments AddConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new ConcatArgument(filePaths), addArguments);
public FFMpegArguments AddDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new DemuxConcatArgument(filePaths), addArguments);
public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new InputArgument(verifyExists, filePath), addArguments);
+ public FFMpegArguments AddFileInput(string[] filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
public FFMpegArguments AddFileInput(FileInfo fileInfo, Action? addArguments = null) => WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public FFMpegArguments AddUrlInput(Uri uri, Action? addArguments = null) => WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public FFMpegArguments AddDeviceInput(string device, Action? addArguments = null) => WithInput(new InputDeviceArgument(device), addArguments);
diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj
index ed3b71c..e6a45e9 100644
--- a/FFMpegCore/FFMpegCore.csproj
+++ b/FFMpegCore/FFMpegCore.csproj
@@ -3,7 +3,7 @@
true
A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications
- 5.1.0
+ 5.1.1
../nupkg
@@ -20,5 +20,7 @@
+
+
From 8f6d1aa8e959442a0116986d691dd7358fe476d0 Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Fri, 1 Mar 2024 07:13:55 -0600
Subject: [PATCH 2/7] Update MultiInput to IEnumerable
---
FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs b/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
index e64d96a..288c761 100644
--- a/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
+++ b/FFMpegCore/FFMpeg/Arguments/MultiInputArgument.cs
@@ -6,15 +6,15 @@
public class MultiInputArgument : IInputArgument
{
public readonly bool VerifyExists;
- public readonly string[] FilePaths;
+ public readonly IEnumerable FilePaths;
- public MultiInputArgument(bool verifyExists, params string[] filePaths)
+ public MultiInputArgument(bool verifyExists, IEnumerable filePaths)
{
VerifyExists = verifyExists;
FilePaths = filePaths;
}
- public MultiInputArgument(string[] filePaths, bool verifyExists) : this(verifyExists, filePaths) { }
+ public MultiInputArgument(IEnumerable filePaths, bool verifyExists) : this(verifyExists, filePaths) { }
public void Pre()
{
From d83650168121c2c5e6b98aa9977ada3cb31b076f Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Sun, 3 Mar 2024 11:42:52 -0600
Subject: [PATCH 3/7] Change string array to IEnumerable
---
FFMpegCore/FFMpeg/FFMpegArguments.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs
index cf57b84..08b9d19 100644
--- a/FFMpegCore/FFMpeg/FFMpegArguments.cs
+++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs
@@ -21,7 +21,7 @@ private string GetText()
public static FFMpegArguments FromConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new ConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => new FFMpegArguments().WithInput(new DemuxConcatArgument(filePaths), addArguments);
public static FFMpegArguments FromFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(verifyExists, filePath), addArguments);
- public static FFMpegArguments FromFileInput(string[] filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
+ public static FFMpegArguments FromFileInput(IEnumerable filePath, bool verifyExists = true, Action? addArguments = null) => new FFMpegArguments().WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
public static FFMpegArguments FromFileInput(FileInfo fileInfo, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public static FFMpegArguments FromUrlInput(Uri uri, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public static FFMpegArguments FromDeviceInput(string device, Action? addArguments = null) => new FFMpegArguments().WithInput(new InputDeviceArgument(device), addArguments);
From 622db9600c66f058085ee89b8487a4734dc02016 Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Sun, 3 Mar 2024 12:49:34 -0600
Subject: [PATCH 4/7] Add MultiInput Test
---
FFMpegCore.Test/ArgumentBuilderTest.cs | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs
index cf455c8..00b0c96 100644
--- a/FFMpegCore.Test/ArgumentBuilderTest.cs
+++ b/FFMpegCore.Test/ArgumentBuilderTest.cs
@@ -9,6 +9,7 @@ namespace FFMpegCore.Test
public class ArgumentBuilderTest
{
private readonly string[] _concatFiles = { "1.mp4", "2.mp4", "3.mp4", "4.mp4" };
+ private readonly string[] _multiFiles = { "1.mp3", "2.mp3", "3.mp3", "4.mp3" };
[TestMethod]
public void Builder_BuildString_IO_1()
@@ -611,5 +612,24 @@ public void Builder_BuildString_TeeOutput()
-i "input.mp4" -f tee "[movflags=faststart]output.mp4|[f=mpegts:select=\'0:v:0\']http://server/path"
""", str);
}
+ [TestMethod]
+ public void Builder_BuildString_MultiInput()
+ {
+ var audioStreams = string.Join("", _multiFiles.Select((item, index) => $"[{index}:0]"));
+ var mixFilter = $"{audioStreams}amix=inputs={_multiFiles.Length}:duration=longest:dropout_transition=1:normalize=0[final]";
+ var ffmpegArgs = $"-filter_complex \"{mixFilter}\" -map \"[final]\"";
+ var str = FFMpegArguments
+ .FromFileInput(_multiFiles)
+ .OutputToFile("output.mp3", overwrite: true, options => options
+ .WithCustomArgument(ffmpegArgs)
+ .WithAudioCodec(AudioCodec.LibMp3Lame) // Set the audio codec to MP3
+ .WithAudioBitrate(128) // Set the bitrate to 128kbps
+ .WithAudioSamplingRate(48000) // Set the sample rate to 48kHz
+ .WithoutMetadata() // Remove metadata
+ .WithCustomArgument("-ac 2 -write_xing 0 -id3v2_version 0")) // Force 2 Channels
+ .Arguments;
+
+ Assert.AreEqual($"-i \"1.mp3\" -i \"2.mp3\" -i \"3.mp3\" -i \"4.mp3\" -filter_complex \"[0:0][1:0][2:0][3:0]amix=inputs=4:duration=longest:dropout_transition=1:normalize=0[final]\" -map \"[final]\" -c:a libmp3lame -b:a 128k -ar 48000 -map_metadata -1 -ac 2 -write_xing 0 -id3v2_version 0 \"output.mp3\" -y", str);
+ }
}
}
From bb341e6dd7260196f13c69362aac3f6574b1cf95 Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Sun, 3 Mar 2024 13:02:50 -0600
Subject: [PATCH 5/7] Update one more instance of string[] to IEnumerable
---
FFMpegCore/FFMpeg/FFMpegArguments.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs
index 08b9d19..ddb1f72 100644
--- a/FFMpegCore/FFMpeg/FFMpegArguments.cs
+++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs
@@ -36,7 +36,7 @@ public FFMpegArguments WithGlobalOptions(Action configure
public FFMpegArguments AddConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new ConcatArgument(filePaths), addArguments);
public FFMpegArguments AddDemuxConcatInput(IEnumerable filePaths, Action? addArguments = null) => WithInput(new DemuxConcatArgument(filePaths), addArguments);
public FFMpegArguments AddFileInput(string filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new InputArgument(verifyExists, filePath), addArguments);
- public FFMpegArguments AddFileInput(string[] filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
+ public FFMpegArguments AddFileInput(IEnumerable filePath, bool verifyExists = true, Action? addArguments = null) => WithInput(new MultiInputArgument(verifyExists, filePath), addArguments);
public FFMpegArguments AddFileInput(FileInfo fileInfo, Action? addArguments = null) => WithInput(new InputArgument(fileInfo.FullName, false), addArguments);
public FFMpegArguments AddUrlInput(Uri uri, Action? addArguments = null) => WithInput(new InputArgument(uri.AbsoluteUri, false), addArguments);
public FFMpegArguments AddDeviceInput(string device, Action? addArguments = null) => WithInput(new InputDeviceArgument(device), addArguments);
From 31b117d1862fd80b7df928291986e2f693ff2c74 Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Sun, 3 Mar 2024 13:09:52 -0600
Subject: [PATCH 6/7] Remove whitespace
---
FFMpegCore.Test/ArgumentBuilderTest.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs
index 00b0c96..b6ae6dd 100644
--- a/FFMpegCore.Test/ArgumentBuilderTest.cs
+++ b/FFMpegCore.Test/ArgumentBuilderTest.cs
@@ -628,7 +628,6 @@ public void Builder_BuildString_MultiInput()
.WithoutMetadata() // Remove metadata
.WithCustomArgument("-ac 2 -write_xing 0 -id3v2_version 0")) // Force 2 Channels
.Arguments;
-
Assert.AreEqual($"-i \"1.mp3\" -i \"2.mp3\" -i \"3.mp3\" -i \"4.mp3\" -filter_complex \"[0:0][1:0][2:0][3:0]amix=inputs=4:duration=longest:dropout_transition=1:normalize=0[final]\" -map \"[final]\" -c:a libmp3lame -b:a 128k -ar 48000 -map_metadata -1 -ac 2 -write_xing 0 -id3v2_version 0 \"output.mp3\" -y", str);
}
}
From 94db493d1951b7a72698b972d6f345d6860ddac6 Mon Sep 17 00:00:00 2001
From: AddyMills <74471839+AddyMills@users.noreply.github.com>
Date: Sun, 3 Mar 2024 18:56:06 -0600
Subject: [PATCH 7/7] Add IEnumerable tests for inputs
---
FFMpegCore.Test/ArgumentBuilderTest.cs | 63 ++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs
index b6ae6dd..2b6c0d4 100644
--- a/FFMpegCore.Test/ArgumentBuilderTest.cs
+++ b/FFMpegCore.Test/ArgumentBuilderTest.cs
@@ -630,5 +630,68 @@ public void Builder_BuildString_MultiInput()
.Arguments;
Assert.AreEqual($"-i \"1.mp3\" -i \"2.mp3\" -i \"3.mp3\" -i \"4.mp3\" -filter_complex \"[0:0][1:0][2:0][3:0]amix=inputs=4:duration=longest:dropout_transition=1:normalize=0[final]\" -map \"[final]\" -c:a libmp3lame -b:a 128k -ar 48000 -map_metadata -1 -ac 2 -write_xing 0 -id3v2_version 0 \"output.mp3\" -y", str);
}
+ [TestMethod]
+ public void Pre_VerifyExists_AllFilesExist()
+ {
+ // Arrange
+ var filePaths = new List
+ {
+ Path.GetTempFileName(),
+ Path.GetTempFileName(),
+ Path.GetTempFileName()
+ };
+ var argument = new MultiInputArgument(true, filePaths);
+ try
+ {
+ // Act & Assert
+ argument.Pre(); // No exception should be thrown
+ }
+ finally
+ {
+ // Cleanup
+ foreach (var filePath in filePaths)
+ {
+ File.Delete(filePath);
+ }
+ }
+ }
+
+ [TestMethod]
+ public void Pre_VerifyExists_SomeFilesNotExist()
+ {
+ // Arrange
+ var filePaths = new List
+ {
+ Path.GetTempFileName(),
+ "file2.mp4",
+ "file3.mp4"
+ };
+ var argument = new MultiInputArgument(true, filePaths);
+ try
+ {
+ // Act & Assert
+ Assert.ThrowsException(() => argument.Pre());
+ }
+ finally
+ {
+ // Cleanup
+ File.Delete(filePaths[0]);
+ }
+ }
+
+ [TestMethod]
+ public void Pre_VerifyExists_NoFilesExist()
+ {
+ // Arrange
+ var filePaths = new List
+ {
+ "file1.mp4",
+ "file2.mp4",
+ "file3.mp4"
+ };
+ var argument = new MultiInputArgument(true, filePaths);
+ // Act & Assert
+ Assert.ThrowsException(() => argument.Pre());
+ }
}
}