FFMPEG Builder: Add Audio Track now will reuse and copy a stream if a matching one is found

This commit is contained in:
John Andrews
2022-04-25 17:07:42 +12:00
parent c75ac43da5
commit 00745eae04
2 changed files with 241 additions and 19 deletions

View File

@@ -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)
{

View File

@@ -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