mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-01-29 07:38:26 -06:00
FFMPEG Builder: Add Audio Track now will reuse and copy a stream if a matching one is found
This commit is contained in:
@@ -97,27 +97,32 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode
|
||||
|
||||
var audio = new FfmpegAudioStream();
|
||||
|
||||
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||
var bestAudio = Model.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x.Stream).ToLower().Contains("commentary") == false)
|
||||
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||
.OrderBy(x =>
|
||||
var bestAudio = GetBestAudioTrack(args, Model.AudioStreams.Select(x => x.Stream));
|
||||
if (bestAudio == null)
|
||||
{
|
||||
if (Language != string.Empty)
|
||||
{
|
||||
args.Logger?.ILog("Language: " + x.Stream.Language, x);
|
||||
if (string.IsNullOrEmpty(x.Stream.Language))
|
||||
return 50; // no language specified
|
||||
if (x.Stream.Language?.ToLower() != Language)
|
||||
return 100; // low priority not the desired language
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(x => x.Stream.Channels)
|
||||
.ThenBy(x => x.Index)
|
||||
.FirstOrDefault();
|
||||
audio.Stream = bestAudio.Stream;
|
||||
args.Logger.WLog("No source audio track found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.Stream.TypeIndex)));
|
||||
audio.Stream = bestAudio;
|
||||
|
||||
bool directCopy = false;
|
||||
if(bestAudio.Codec.ToLower() == this.Codec.ToLower())
|
||||
{
|
||||
if(this.Channels == 0 || this.Channels == bestAudio.Channels)
|
||||
{
|
||||
directCopy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (directCopy)
|
||||
{
|
||||
args.Logger?.ILog($"Source audio is already in appropriate format, just copying that track: {bestAudio.IndexString}, Channels: {bestAudio.Channels}, Codec: {bestAudio.Codec}");
|
||||
}
|
||||
else
|
||||
{
|
||||
audio.EncodingParameters.AddRange(GetNewAudioTrackParameters("0:a:" + (bestAudio.TypeIndex)));
|
||||
}
|
||||
if (Index > Model.AudioStreams.Count - 1)
|
||||
Model.AudioStreams.Add(audio);
|
||||
else
|
||||
@@ -126,6 +131,54 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode
|
||||
return 1;
|
||||
}
|
||||
|
||||
internal AudioStream GetBestAudioTrack(NodeParameters args, IEnumerable<AudioStream> streams)
|
||||
{
|
||||
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||
var bestAudio = streams.Where(x => System.Text.Json.JsonSerializer.Serialize(x).ToLower().Contains("commentary") == false)
|
||||
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||
.OrderBy(x =>
|
||||
{
|
||||
if (Language != string.Empty)
|
||||
{
|
||||
args.Logger?.ILog("Language: " + x.Language, x);
|
||||
if (string.IsNullOrEmpty(x.Language))
|
||||
return 50; // no language specified
|
||||
if (x.Language.ToLower() != Language)
|
||||
return 100; // low priority not the desired language
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(x => {
|
||||
if(this.Channels == 2)
|
||||
{
|
||||
if (x.Channels == 2)
|
||||
return 1_000_000_000;
|
||||
// compare codecs
|
||||
if (x.Codec?.ToLower() == this.Codec?.ToLower())
|
||||
return 1_000_000;
|
||||
}
|
||||
if(this.Channels == 1)
|
||||
{
|
||||
if (x.Channels == 1)
|
||||
return 1_000_000_000;
|
||||
if (x.Channels <= 2.1f)
|
||||
return 5_000_000;
|
||||
if (x.Codec?.ToLower() == this.Codec?.ToLower())
|
||||
return 1_000_000;
|
||||
}
|
||||
|
||||
// now we want best channels, but to prefer matching codec
|
||||
if (x.Codec?.ToLower() == this.Codec?.ToLower())
|
||||
{
|
||||
return 1_000 + x.Channels;
|
||||
}
|
||||
return x.Channels;
|
||||
})
|
||||
.ThenBy(x => x.Index)
|
||||
.FirstOrDefault();
|
||||
return bestAudio;
|
||||
}
|
||||
|
||||
|
||||
private string[] GetNewAudioTrackParameters(string source)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
#if(DEBUG)
|
||||
|
||||
using FileFlows.VideoNodes.FfmpegBuilderNodes;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using VideoNodes.Tests;
|
||||
|
||||
namespace FileFlows.VideoNodes.Tests.FfmpegBuilderTests;
|
||||
|
||||
[TestClass]
|
||||
public class FfmpegBuilder_AddAudioTests
|
||||
{
|
||||
VideoInfo vii;
|
||||
NodeParameters args;
|
||||
private void Prepare()
|
||||
{
|
||||
const string file = @"D:\videos\unprocessed\basic.mkv";
|
||||
var logger = new TestLogger();
|
||||
const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
|
||||
var vi = new VideoInfoHelper(ffmpeg, logger);
|
||||
vii = vi.Read(file);
|
||||
vii.AudioStreams = new List<AudioStream>
|
||||
{
|
||||
new AudioStream
|
||||
{
|
||||
Index = 2,
|
||||
IndexString = "0:a:0",
|
||||
Language = "en",
|
||||
Codec = "AC3",
|
||||
Channels = 5.1f
|
||||
},
|
||||
new AudioStream
|
||||
{
|
||||
Index = 3,
|
||||
IndexString = "0:a:1",
|
||||
Language = "en",
|
||||
Codec = "AAC",
|
||||
Channels = 2
|
||||
},
|
||||
new AudioStream
|
||||
{
|
||||
Index = 4,
|
||||
IndexString = "0:a:3",
|
||||
Language = "en",
|
||||
Codec = "AAC",
|
||||
Channels = 2
|
||||
},
|
||||
new AudioStream
|
||||
{
|
||||
Index = 5,
|
||||
IndexString = "0:a:4",
|
||||
Language = "en",
|
||||
Codec = "AAC",
|
||||
Channels = 5.1f
|
||||
}
|
||||
};
|
||||
args = new NodeParameters(file, logger, false, string.Empty);
|
||||
args.GetToolPathActual = (string tool) => ffmpeg;
|
||||
args.TempPath = @"D:\videos\temp";
|
||||
args.Parameters.Add("VideoInfo", vii);
|
||||
|
||||
|
||||
FfmpegBuilderStart ffStart = new();
|
||||
Assert.AreEqual(1, ffStart.Execute(args));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_AacStereo()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "aac";
|
||||
ffAddAudio.Channels = 2;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(3, best.Index);
|
||||
Assert.AreEqual("AAC", best.Codec);
|
||||
Assert.AreEqual(2f, best.Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_AacSameAsSource()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "aac";
|
||||
ffAddAudio.Channels = 0;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(5, best.Index);
|
||||
Assert.AreEqual("AAC", best.Codec);
|
||||
Assert.AreEqual(5.1f, best.Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_Ac3SameAsSource()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "ac3";
|
||||
ffAddAudio.Channels = 0;
|
||||
ffAddAudio.Index = 1;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(2, best.Index);
|
||||
Assert.AreEqual("AC3", best.Codec);
|
||||
Assert.AreEqual(5.1f, best.Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_DtsSame()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "dts";
|
||||
ffAddAudio.Channels = 0;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(2, best.Index);
|
||||
Assert.AreEqual("AC3", best.Codec);
|
||||
Assert.AreEqual(5.1f, best.Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_DtsStereo()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "dts";
|
||||
ffAddAudio.Channels = 2;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(3, best.Index);
|
||||
Assert.AreEqual("AAC", best.Codec);
|
||||
Assert.AreEqual(2f, best.Channels);
|
||||
}
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_DtsMono()
|
||||
{
|
||||
Prepare();
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "dts";
|
||||
ffAddAudio.Channels = 1;
|
||||
var best = ffAddAudio.GetBestAudioTrack(args, vii.AudioStreams);
|
||||
|
||||
Assert.IsNotNull(best);
|
||||
|
||||
Assert.AreEqual(3, best.Index);
|
||||
Assert.AreEqual("AAC", best.Codec);
|
||||
Assert.AreEqual(2f, best.Channels);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user