From 3d735e242af4f3606a2947cf75a99e8284f1615c Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 2 Mar 2023 20:57:59 +0100 Subject: [PATCH] Use source audio bitrate if requested codec is lossless --- .../Controllers/DynamicHlsController.cs | 16 +++---- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 24 ++++++++-- Jellyfin.Api/Helpers/StreamingHelpers.cs | 16 +++++-- .../MediaEncoding/EncodingHelper.cs | 46 +++++++++---------- 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index ba5d0e41cf..ffd5f6818f 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -13,6 +13,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; +using Jellyfin.Extensions; using Jellyfin.MediaEncoding.Hls.Playlist; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; @@ -1694,7 +1695,7 @@ namespace Jellyfin.Api.Controllers audioTranscodeParams += "-acodec " + audioCodec; - if (state.OutputAudioBitrate.HasValue) + if (state.OutputAudioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture); } @@ -1715,11 +1716,11 @@ namespace Jellyfin.Api.Controllers // dts, flac, opus and truehd are experimental in mp4 muxer var strictArgs = string.Empty; - - if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase)) + var actualOutputAudioCodec = state.ActualOutputAudioCodec; + if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase) + || string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase) + || string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase) + || string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase)) { strictArgs = " -strict -2"; } @@ -1748,8 +1749,7 @@ namespace Jellyfin.Api.Controllers } var bitrate = state.OutputAudioBitrate; - - if (bitrate.HasValue) + if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase)) { args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); } diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index bc3431e008..26402c002e 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; +using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -220,11 +221,26 @@ namespace Jellyfin.Api.Helpers var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(',', requestedVideoProfiles), "main"); sdrVideoUrl += "&AllowVideoStreamCopy=false"; - var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); - var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream, state.OutputAudioChannels) ?? 0; - var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; + var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); + var sdrOutputAudioBitrate = 0; + if (EncodingHelper.LosslessAudioCodecs.Contains(state.VideoRequest.AudioCodec, StringComparison.OrdinalIgnoreCase)) + { + sdrOutputAudioBitrate = state.AudioStream.BitRate ?? 0; + } + else + { + sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream, state.OutputAudioChannels) ?? 0; + } - AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); + var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; + var sdrPlaylist = AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup); + + // Provide a workaround for the case issue between flac and fLaC. + flacWaPlaylist = ApplyFlacCaseWorkaround(state, sdrPlaylist.ToString()); + if (!string.IsNullOrEmpty(flacWaPlaylist)) + { + builder.Append(flacWaPlaylist); + } // Restore the video codec state.OutputVideoCodec = "copy"; diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 77f8161d00..963931a24c 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -182,12 +182,18 @@ namespace Jellyfin.Api.Helpers : GetOutputFileExtension(state, mediaSource); } - state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.'); - - state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream, state.OutputAudioChannels); - - state.OutputAudioCodec = streamingRequest.AudioCodec; + var outputAudioCodec = streamingRequest.AudioCodec; + if (EncodingHelper.LosslessAudioCodecs.Contains(outputAudioCodec)) + { + state.OutputAudioBitrate = state.AudioStream.BitRate ?? 0; + } + else + { + state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream, state.OutputAudioChannels) ?? 0; + } + state.OutputAudioCodec = outputAudioCodec; + state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.'); state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec); if (state.VideoRequest != null) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6f3e9c6994..ebae38a4d9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -62,6 +62,16 @@ namespace MediaBrowser.Controller.MediaEncoding "Main10" }; + public static readonly string[] LosslessAudioCodecs = new string[] + { + "alac", + "ape", + "flac", + "mlp", + "truehd", + "wavpack" + }; + public EncodingHelper( IApplicationPaths appPaths, IMediaEncoder mediaEncoder, @@ -2104,17 +2114,6 @@ namespace MediaBrowser.Controller.MediaEncoding }; } - if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase)) - { - if (inputChannels >= 6) - { - return Math.Min(3584000, bitrate); - } - - return Math.Min(1536000, bitrate); - } - if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase)) { @@ -2127,20 +2126,10 @@ namespace MediaBrowser.Controller.MediaEncoding }; } - if (string.Equals(audioCodec, "truehd", StringComparison.OrdinalIgnoreCase)) - { - return (inputChannels, outputChannels) switch - { - (> 0, > 0) => Math.Min(outputChannels * 768000, bitrate), - (> 0, _) => Math.Min(inputChannels * 768000, bitrate), - (_, _) => Math.Min(768000, bitrate) - }; - } - // Empty bitrate area is not allow on iOS // Default audio bitrate to 128K per channel if we don't have codec specific defaults // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options - return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 1); + return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2); } public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) @@ -5562,7 +5551,7 @@ namespace MediaBrowser.Controller.MediaEncoding var bitrate = state.OutputAudioBitrate; - if (bitrate.HasValue) + if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase)) { args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); } @@ -5582,8 +5571,10 @@ namespace MediaBrowser.Controller.MediaEncoding var audioTranscodeParams = new List(); var bitrate = state.OutputAudioBitrate; + var channels = state.OutputAudioChannels; + var outputCodec = state.OutputAudioCodec; - if (bitrate.HasValue) + if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase)) { audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture)); } @@ -5593,7 +5584,12 @@ namespace MediaBrowser.Controller.MediaEncoding audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); } - if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(outputCodec)) + { + audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state)); + } + + if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase)) { // opus only supports specific sampling rates var sampleRate = state.OutputAudioSampleRate;