diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs index 441ce13b..a43647c3 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioAddTrack.cs @@ -2,23 +2,37 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes; +/// +/// FFmpeg Builder: Add Audio Track +/// public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode { + /// + /// Gets the icon for this flow element + /// public override string Icon => "fas fa-volume-off"; - + /// + /// Gets the help URL for this flow element + /// public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/add-audio-track"; - + /// + /// Gets or sets the index to insert this track + /// [NumberInt(1)] [Range(0, 100)] [DefaultValue(1)] public int Index { get; set; } - - + /// + /// Gets or sets the codec to to use + /// [DefaultValue("aac")] [Select(nameof(CodecOptions), 1)] public string Codec { get; set; } private static List _CodecOptions; + /// + /// Gets the codec options + /// public static List CodecOptions { get @@ -37,12 +51,17 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode return _CodecOptions; } } - + /// + /// Gets or sets the audio channels for the new track + /// [DefaultValue(2f)] [Select(nameof(ChannelsOptions), 2)] public float Channels { get; set; } private static List _ChannelsOptions; + /// + /// Gets the channel options + /// public static List ChannelsOptions { get @@ -61,11 +80,16 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode return _ChannelsOptions; } } - + /// + /// Gets or sets the bitrate + /// [Select(nameof(BitrateOptions), 3)] public int Bitrate { get; set; } private static List _BitrateOptions; + /// + /// Gets the background bitrate options + /// public static List BitrateOptions { get @@ -84,18 +108,29 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode return _BitrateOptions; } } - + /// + /// Gets or sets the language of the nee track + /// [DefaultValue("eng")] [TextVariable(4)] public string Language { get; set; } - + /// + /// Gets or sets if the title of the new track should be removed + /// [Boolean(5)] public bool RemoveTitle { get; set; } - + /// + /// Gets or sets the title of the new track + /// [TextVariable(6)] [ConditionEquals(nameof(RemoveTitle), false)] public string NewTitle { get; set; } - + + /// + /// Executes the flow element + /// + /// the node parameters + /// the output node to execute next public override int Execute(NodeParameters args) { if (string.IsNullOrEmpty(Codec) || Codec == "ORIGINAL") @@ -113,6 +148,7 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode } audio.Stream = bestAudio; + audio.Channels = audio.Stream.Channels; bool directCopy = false; if(bestAudio.Codec.ToLower() == this.Codec.ToLower()) @@ -129,7 +165,9 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode } else { - audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.TypeIndex), Codec, Channels, Bitrate)); + audio.EncodingParameters.AddRange(GetNewAudioTrackParameters(Codec, Channels, Bitrate)); + if (this.Channels > 0) + audio.Channels = this.Channels; } if (RemoveTitle) @@ -145,6 +183,12 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode return 1; } + /// + /// Gets the best audio track + /// + /// the node parameters + /// the possible audio streams + /// the best stream internal AudioStream GetBestAudioTrack(NodeParameters args, IEnumerable streams) { Regex? rgxLanguage = null; @@ -202,8 +246,14 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode return bestAudio; } - - internal static string[] GetNewAudioTrackParameters(string source, string codec, float channels, int bitrate) + /// + /// Gets hte new audio track parameters + /// + /// the codec of the new track + /// the channels of the new track + /// the bitrate of the new track + /// the new track parameters + internal static string[] GetNewAudioTrackParameters(string codec, float channels, int bitrate) { if (codec == "opus") codec = "libopus"; diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioConvert.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioConvert.cs index d1a43f5f..3732cdb8 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioConvert.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioConvert.cs @@ -136,7 +136,7 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode if (codecSame && channelsSame && bitrateSame) return false; - stream.EncodingParameters.AddRange(FfmpegBuilderAudioAddTrack.GetNewAudioTrackParameters("0:a:" + (stream.Stream.TypeIndex), Codec, Channels, Bitrate)); + stream.EncodingParameters.AddRange(FfmpegBuilderAudioAddTrack.GetNewAudioTrackParameters(Codec, Channels, Bitrate)); return true; } } diff --git a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs index 86c3de00..9bd836c9 100644 --- a/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs +++ b/VideoNodes/FfmpegBuilderNodes/Audio/FfmpegBuilderAudioTrackReorder.cs @@ -3,29 +3,49 @@ using System.Text; namespace FileFlows.VideoNodes.FfmpegBuilderNodes; +/// +/// FFmpeg Builder: Audio Track Reorder +/// public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode { + /// + /// Gets the number of output nodes + /// public override int Outputs => 2; - + /// + /// Gets the icon + /// public override string Icon => "fas fa-sort-alpha-down"; - + /// + /// Gets the help URL + /// public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/track-reorder"; - - + /// + /// Gets or sets the stream type + /// [Select(nameof(StreamTypeOptions), 1)] public string StreamType { get; set; } - + /// + /// Gets or sets the languages + /// [StringArray(2)] public List Languages { get; set; } - + /// + /// Gets or sets the ordered tracks + /// [StringArray(3)] public List OrderedTracks { get; set; } - + /// + /// Gets or sets the channels + /// [StringArray(4)] [ConditionEquals(nameof(StreamType), "Subtitle", inverse: true)] public List Channels { get; set; } private static List _StreamTypeOptions; + /// + /// Gets or sets the stream type options + /// public static List StreamTypeOptions { get @@ -42,6 +62,11 @@ public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode } } + /// + /// Executes the flow element + /// + /// the node parameters + /// the next output node public override int Execute(NodeParameters args) { OrderedTracks = OrderedTracks?.Select(x => x.ToLower())?.ToList() ?? new(); @@ -92,6 +117,12 @@ public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode } } + /// + /// Reorders the tracks + /// + /// the inputs to reorder + /// the type to reorder + /// the reordered tracks public List Reorder(List input) where T : FfmpegStream { Languages ??= new List(); @@ -124,7 +155,8 @@ public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode { langIndex = Languages.IndexOf(audioStream.Stream.Language?.ToLower() ?? String.Empty); codecIndex = OrderedTracks.IndexOf(audioStream.Stream.Codec?.ToLower() ?? String.Empty); - channelIndex = actualChannels.IndexOf(audioStream.Stream.Channels); + float channels = audioStream.Channels > 0 ? audioStream.Channels : audioStream.Stream.Channels; + channelIndex = actualChannels.IndexOf(channels); } else if (x is FfmpegSubtitleStream subStream) { @@ -152,6 +184,14 @@ public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode return data; } + + /// + /// Tests if two lists are the same + /// + /// the original list + /// the reordered list + /// the type of items + /// true if the lists are the same, otherwise false public bool AreSame(List original, List reordered) where T: FfmpegStream { for (int i = 0; i < reordered.Count; i++) diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs index b8b34c7f..b1b687b4 100644 --- a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegAudioStream.cs @@ -4,6 +4,13 @@ { public AudioStream Stream { get; set; } public override bool HasChange => EncodingParameters.Any() || Filter.Any(); + + /// + /// Gets or sets the channels for this stream + /// Note: changing this will not magically change the channels for processing, you must change manually + /// down-mix or up-mix then update this channel count, this is intended for sorting only + /// + public float Channels { get; set; } private List _EncodingParameters = new List(); public List EncodingParameters diff --git a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs index 81b509f0..805eb434 100644 --- a/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs +++ b/VideoNodes/FfmpegBuilderNodes/Models/FfmpegModel.cs @@ -90,6 +90,7 @@ Title = item.stream.Title, Language = item.stream.Language, Stream = item.stream, + Channels = item.stream.Channels }); } foreach (var item in info.SubtitleStreams.Select((stream, index) => (stream, index))) diff --git a/VideoNodes/Tests/FfmpegBuilderTests/FFmpegBuilder_TrackReorderTests.cs b/VideoNodes/Tests/FfmpegBuilderTests/FFmpegBuilder_TrackReorderTests.cs new file mode 100644 index 00000000..bcdd6f3b --- /dev/null +++ b/VideoNodes/Tests/FfmpegBuilderTests/FFmpegBuilder_TrackReorderTests.cs @@ -0,0 +1,86 @@ +#if(DEBUG) + +using FileFlows.VideoNodes.FfmpegBuilderNodes; +using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace FileFlows.VideoNodes.Tests.FfmpegBuilderTests; + +/// +/// Tests for track reorders +/// +[TestClass] +public class FFmpegBuilder_TrackReorderTests +{ + /// + /// Basic test + /// + [TestMethod] + public void Basic() + { + var element = new FfmpegBuilderAudioTrackReorder(); + List original = new() + { + new() { Index = 1, Channels = 2, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 2, Channels = 0, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 3, Channels = 5.1f, Stream = new() { Language = "fr", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 4, Channels = 5.1f, Stream = new() { Language = "en", Codec = "aac", Channels = 2 } }, + }; + element.Channels = new() { "5.1" }; + var reordered = element.Reorder(original); + Assert.IsFalse(element.AreSame(original, reordered)); + Assert.AreEqual(2, reordered[0].Index); + Assert.AreEqual(3, reordered[1].Index); + Assert.AreEqual(4, reordered[2].Index); + Assert.AreEqual(1, reordered[3].Index); + } + + /// + /// Basic test 2 + /// + [TestMethod] + public void Basic_2() + { + var element = new FfmpegBuilderAudioTrackReorder(); + List original = new() + { + new() { Index = 1, Channels = 2, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 2, Channels = 0, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 3, Channels = 5.1f, Stream = new() { Language = "fr", Codec = "ac3", Channels = 7.1f } }, + new() { Index = 4, Channels = 5.1f, Stream = new() { Language = "en", Codec = "aac", Channels = 2 } }, + }; + element.Channels = new() { "5.1" }; + var reordered = element.Reorder(original); + Assert.IsFalse(element.AreSame(original, reordered)); + Assert.AreEqual(2, reordered[0].Index); + Assert.AreEqual(3, reordered[1].Index); + Assert.AreEqual(4, reordered[2].Index); + Assert.AreEqual(1, reordered[3].Index); + } + + /// + /// Basic test 3 + /// + [TestMethod] + public void Basic_3() + { + var element = new FfmpegBuilderAudioTrackReorder(); + List original = new() + { + new() { Index = 1, Channels = 2, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 2, Channels = 0, Stream = new() { Language = "en", Codec = "ac3", Channels = 5.1f } }, + new() { Index = 3, Channels = 5.1f, Stream = new() { Language = "fr", Codec = "ac3", Channels = 7.1f } }, + new() { Index = 4, Channels = 5.1f, Stream = new() { Language = "en", Codec = "aac", Channels = 2 } }, + }; + element.Channels = new() { "5.1" }; + element.OrderedTracks = new() { "aac", "ac3" }; + var reordered = element.Reorder(original); + Assert.IsFalse(element.AreSame(original, reordered)); + Assert.AreEqual(4, reordered[0].Index); + Assert.AreEqual(2, reordered[1].Index); + Assert.AreEqual(3, reordered[2].Index); + Assert.AreEqual(1, reordered[3].Index); + } +} + +#endif \ No newline at end of file