From b53cfebac119fd6c539a9e2ac3ed35f74effb5ea Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Thu, 14 Jul 2022 10:52:25 -0500 Subject: [PATCH] fix bug with unsupported aac channel layouts (#893) * fix bug with unsupported aac channel layouts * update dependencies --- CHANGELOG.md | 1 + .../FFmpegPlaybackSettingsCalculator.cs | 13 ++-- ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs | 72 ++++++++++++++++++- .../Option/AudioChannelsOutputOption.cs | 31 ++++++-- ErsatzTV.FFmpeg/PipelineBuilder.cs | 11 ++- .../ErsatzTV.Infrastructure.csproj | 6 +- ErsatzTV/ErsatzTV.csproj | 8 +-- 7 files changed, 120 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b2db1f78..7a0328655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Fixed - Fix subtitle stream selection when subtitle language is different than audio language +- Fix bug with unsupported AAC channel layouts ## [0.6.3-beta] - 2022-07-04 ### Fixed diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index c9f71f556..15c9761c1 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -148,14 +148,11 @@ public class FFmpegPlaybackSettingsCalculator result.AudioBitrate = ffmpegProfile.AudioBitrate; result.AudioBufferSize = ffmpegProfile.AudioBufferSize; - audioStream.IfSome( - stream => - { - if (stream.Channels != ffmpegProfile.AudioChannels) - { - result.AudioChannels = ffmpegProfile.AudioChannels; - } - }); + foreach (MediaStream _ in audioStream) + { + // this can be optimized out later, depending on the audio codec + result.AudioChannels = ffmpegProfile.AudioChannels; + } result.AudioSampleRate = ffmpegProfile.AudioSampleRate; result.AudioDuration = outPoint - inPoint; diff --git a/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs b/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs index 7434ed41f..cd54b9b9d 100644 --- a/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs +++ b/ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs @@ -86,7 +86,77 @@ public class PipelineGeneratorTests string command = PrintCommand(videoInputFile, audioInputFile, None, None, result); command.Should().Be( - "-threads 1 -nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:a aac -ac 2 -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); + "-threads 1 -nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:a aac -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); + } + + [Test] + public void Aac_6_Channel_Should_Specify_Audio_Channels() + { + var videoInputFile = new VideoInputFile( + "/tmp/whatever.mkv", + new List + { new(0, VideoFormat.H264, new PixelFormatYuv420P(), new FrameSize(1920, 1080), "24", false) }); + + var audioInputFile = new AudioInputFile( + "/tmp/whatever.mkv", + new List { new(1, AudioFormat.Aac, 6) }, + new AudioState( + AudioFormat.Aac, + 6, + 320, + 640, + 48, + Option.None, + false)); + + var desiredState = new FrameState( + true, + false, + VideoFormat.Hevc, + new PixelFormatYuv420P(), + new FrameSize(1920, 1080), + new FrameSize(1920, 1080), + Option.None, + 2000, + 4000, + 90_000, + false); + + var ffmpegState = new FFmpegState( + false, + HardwareAccelerationMode.None, + HardwareAccelerationMode.None, + Option.None, + Option.None, + TimeSpan.FromSeconds(1), + Option.None, + false, + Option.None, + Option.None, + Option.None, + OutputFormatKind.MpegTs, + Option.None, + Option.None, + 0, + Option.None); + + var builder = new PipelineBuilder( + new DefaultHardwareCapabilities(), + videoInputFile, + audioInputFile, + None, + None, + "", + "", + _logger); + FFmpegPipeline result = builder.Build(ffmpegState, desiredState); + + result.PipelineSteps.Should().HaveCountGreaterThan(0); + result.PipelineSteps.Should().Contain(ps => ps is EncoderLibx265); + + string command = PrintCommand(videoInputFile, audioInputFile, None, None, result); + command.Should().Be( + "-threads 1 -nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:a aac -ac 6 -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); } [Test] diff --git a/ErsatzTV.FFmpeg/Option/AudioChannelsOutputOption.cs b/ErsatzTV.FFmpeg/Option/AudioChannelsOutputOption.cs index 70e2a0697..558e9f1a0 100644 --- a/ErsatzTV.FFmpeg/Option/AudioChannelsOutputOption.cs +++ b/ErsatzTV.FFmpeg/Option/AudioChannelsOutputOption.cs @@ -1,10 +1,33 @@ -namespace ErsatzTV.FFmpeg.Option; +using ErsatzTV.FFmpeg.Format; + +namespace ErsatzTV.FFmpeg.Option; public class AudioChannelsOutputOption : OutputOption { - private readonly int _channels; + private readonly Option _audioFormat; + private readonly int _desiredChannels; + private readonly int _sourceChannels; - public AudioChannelsOutputOption(int channels) => _channels = channels; + public AudioChannelsOutputOption(Option audioFormat, int sourceChannels, int desiredChannels) + { + _audioFormat = audioFormat; + _sourceChannels = sourceChannels; + _desiredChannels = desiredChannels; + } - public override IList OutputOptions => new List { "-ac", _channels.ToString() }; + public override IList OutputOptions + { + get + { + if (_sourceChannels != _desiredChannels || _audioFormat == Some(AudioFormat.Aac) && _desiredChannels > 2) + { + return new List + { + "-ac", _desiredChannels.ToString() + }; + } + + return Array.Empty(); + } + } } diff --git a/ErsatzTV.FFmpeg/PipelineBuilder.cs b/ErsatzTV.FFmpeg/PipelineBuilder.cs index f25996f1f..d2f606f51 100644 --- a/ErsatzTV.FFmpeg/PipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/PipelineBuilder.cs @@ -538,9 +538,16 @@ public class PipelineBuilder _pipelineSteps.Add(step); } - foreach (int desiredAudioChannels in audioInputFile.DesiredState.AudioChannels) + foreach (AudioStream audioStream in audioInputFile.AudioStreams.HeadOrNone()) { - _pipelineSteps.Add(new AudioChannelsOutputOption(desiredAudioChannels)); + foreach (int desiredAudioChannels in audioInputFile.DesiredState.AudioChannels) + { + _pipelineSteps.Add( + new AudioChannelsOutputOption( + audioInputFile.DesiredState.AudioFormat, + audioStream.Channels, + desiredAudioChannels)); + } } foreach (int desiredBitrate in audioInputFile.DesiredState.AudioBitrate) diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index ea1bd4a7e..05c4ffed3 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -14,12 +14,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 245d4a687..9169a68d7 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -57,14 +57,14 @@ - + - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive