Jelajahi Sumber

Merge pull request #9388 from Shadowghost/output-bitrate-channels-master

Bond-009 2 tahun lalu
induk
melakukan
2ce08eb184

+ 31 - 12
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -12,6 +12,7 @@ using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.PlaybackDtos;
 using Jellyfin.Api.Models.PlaybackDtos;
 using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Api.Models.StreamingDtos;
+using Jellyfin.Extensions;
 using Jellyfin.MediaEncoding.Hls.Playlist;
 using Jellyfin.MediaEncoding.Hls.Playlist;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -1687,14 +1688,25 @@ public class DynamicHlsController : BaseJellyfinApiController
 
 
             audioTranscodeParams += "-acodec " + audioCodec;
             audioTranscodeParams += "-acodec " + audioCodec;
 
 
-            if (state.OutputAudioBitrate.HasValue)
+            var audioBitrate = state.OutputAudioBitrate;
+            var audioChannels = state.OutputAudioChannels;
+
+            if (audioBitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(state.ActualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
             {
             {
-                audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture);
+                var vbrParam = _encodingHelper.GetAudioVbrModeParam(state.OutputAudioCodec, audioBitrate.Value / (audioChannels ?? 2));
+                if (_encodingOptions.EnableAudioVbr && vbrParam is not null)
+                {
+                    audioTranscodeParams += vbrParam;
+                }
+                else
+                {
+                    audioTranscodeParams += " -ab " + audioBitrate.Value.ToString(CultureInfo.InvariantCulture);
+                }
             }
             }
 
 
-            if (state.OutputAudioChannels.HasValue)
+            if (audioChannels.HasValue)
             {
             {
-                audioTranscodeParams += " -ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture);
+                audioTranscodeParams += " -ac " + audioChannels.Value.ToString(CultureInfo.InvariantCulture);
             }
             }
 
 
             if (state.OutputAudioSampleRate.HasValue)
             if (state.OutputAudioSampleRate.HasValue)
@@ -1708,11 +1720,11 @@ public class DynamicHlsController : BaseJellyfinApiController
 
 
         // dts, flac, opus and truehd are experimental in mp4 muxer
         // dts, flac, opus and truehd are experimental in mp4 muxer
         var strictArgs = string.Empty;
         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";
             strictArgs = " -strict -2";
         }
         }
@@ -1746,10 +1758,17 @@ public class DynamicHlsController : BaseJellyfinApiController
         }
         }
 
 
         var bitrate = state.OutputAudioBitrate;
         var bitrate = state.OutputAudioBitrate;
-
-        if (bitrate.HasValue)
+        if (bitrate.HasValue && !EncodingHelper.LosslessAudioCodecs.Contains(actualOutputAudioCodec, StringComparison.OrdinalIgnoreCase))
         {
         {
-            args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
+            var vbrParam = _encodingHelper.GetAudioVbrModeParam(actualOutputAudioCodec, bitrate.Value / (channels ?? 2));
+            if (_encodingOptions.EnableAudioVbr && vbrParam is not null)
+            {
+                args += vbrParam;
+            }
+            else
+            {
+                args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
+            }
         }
         }
 
 
         if (state.OutputAudioSampleRate.HasValue)
         if (state.OutputAudioSampleRate.HasValue)

+ 11 - 2
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -9,6 +9,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Api.Models.StreamingDtos;
+using Jellyfin.Extensions;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
@@ -223,9 +224,17 @@ public class DynamicHlsHelper
                     sdrVideoUrl += "&AllowVideoStreamCopy=false";
                     sdrVideoUrl += "&AllowVideoStreamCopy=false";
 
 
                     var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
                     var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
-                    var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
-                    var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
+                    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;
+                    }
 
 
+                    var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
                     var sdrPlaylist = AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
                     var sdrPlaylist = AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);
 
 
                     // Provide a workaround for the case issue between flac and fLaC.
                     // Provide a workaround for the case issue between flac and fLaC.

+ 11 - 5
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -181,12 +181,18 @@ public static class StreamingHelpers
                 : GetOutputFileExtension(state, mediaSource);
                 : GetOutputFileExtension(state, mediaSource);
         }
         }
 
 
-        state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
-
-        state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
-
-        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);
         state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
 
 
         if (state.VideoRequest is not null)
         if (state.VideoRequest is not null)

+ 124 - 44
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -89,6 +89,16 @@ namespace MediaBrowser.Controller.MediaEncoding
             { "truehd", 6 },
             { "truehd", 6 },
         };
         };
 
 
+        public static readonly string[] LosslessAudioCodecs = new string[]
+        {
+            "alac",
+            "ape",
+            "flac",
+            "mlp",
+            "truehd",
+            "wavpack"
+        };
+
         public EncodingHelper(
         public EncodingHelper(
             IApplicationPaths appPaths,
             IApplicationPaths appPaths,
             IMediaEncoder mediaEncoder,
             IMediaEncoder mediaEncoder,
@@ -620,6 +630,11 @@ namespace MediaBrowser.Controller.MediaEncoding
                 return "flac";
                 return "flac";
             }
             }
 
 
+            if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase))
+            {
+                return "dca";
+            }
+
             return codec.ToLowerInvariant();
             return codec.ToLowerInvariant();
         }
         }
 
 
@@ -2050,9 +2065,9 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
                 }
             }
             }
 
 
-            // Video bitrate must fall within requested value
+            // Audio bitrate must fall within requested value
             if (request.AudioBitRate.HasValue
             if (request.AudioBitRate.HasValue
-                && audioStream.BitDepth.HasValue
+                && audioStream.BitRate.HasValue
                 && audioStream.BitRate.Value > request.AudioBitRate.Value)
                 && audioStream.BitRate.Value > request.AudioBitRate.Value)
             {
             {
                 return false;
                 return false;
@@ -2161,56 +2176,96 @@ namespace MediaBrowser.Controller.MediaEncoding
             return Convert.ToInt32(scaleFactor * bitrate);
             return Convert.ToInt32(scaleFactor * bitrate);
         }
         }
 
 
-        public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
+        public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream, int? outputAudioChannels)
         {
         {
-            return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
+            return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream, outputAudioChannels);
         }
         }
 
 
-        public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
+        public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream, int? outputAudioChannels)
         {
         {
             if (audioStream is null)
             if (audioStream is null)
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
+            var inputChannels = audioStream.Channels ?? 0;
+            var outputChannels = outputAudioChannels ?? 0;
+            var bitrate = audioBitRate ?? int.MaxValue;
+
+            if (string.IsNullOrEmpty(audioCodec)
+                || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
             {
             {
-                return Math.Min(384000, audioBitRate.Value);
+                return (inputChannels, outputChannels) switch
+                {
+                    (>= 6, >= 6 or 0) => Math.Min(640000, bitrate),
+                    (> 0, > 0) => Math.Min(outputChannels * 128000, bitrate),
+                    (> 0, _) => Math.Min(inputChannels * 128000, bitrate),
+                    (_, _) => Math.Min(384000, bitrate)
+                };
             }
             }
 
 
-            if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
+            if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase))
             {
             {
-                if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
+                return (inputChannels, outputChannels) switch
                 {
                 {
-                    if ((audioStream.Channels ?? 0) >= 6)
-                    {
-                        return Math.Min(640000, audioBitRate.Value);
-                    }
+                    (>= 6, >= 6 or 0) => Math.Min(768000, bitrate),
+                    (> 0, > 0) => Math.Min(outputChannels * 136000, bitrate),
+                    (> 0, _) => Math.Min(inputChannels * 136000, bitrate),
+                    (_, _) => Math.Min(672000, bitrate)
+                };
+            }
 
 
-                    return Math.Min(384000, audioBitRate.Value);
-                }
+            // 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 ?? 2);
+        }
 
 
-                if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
-                    || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
+        public string GetAudioVbrModeParam(string encoder, int bitratePerChannel)
+        {
+            if (string.Equals(encoder, "libfdk_aac", StringComparison.OrdinalIgnoreCase))
+            {
+                return " -vbr:a " + bitratePerChannel switch
                 {
                 {
-                    if ((audioStream.Channels ?? 0) >= 6)
-                    {
-                        return Math.Min(3584000, audioBitRate.Value);
-                    }
+                    < 32000 => "1",
+                    < 48000 => "2",
+                    < 64000 => "3",
+                    < 96000 => "4",
+                    _ => "5"
+                };
+            }
 
 
-                    return Math.Min(1536000, audioBitRate.Value);
-                }
+            if (string.Equals(encoder, "libmp3lame", StringComparison.OrdinalIgnoreCase))
+            {
+                return " -qscale:a " + bitratePerChannel switch
+                {
+                    < 48000 => "8",
+                    < 64000 => "6",
+                    < 88000 => "4",
+                    < 112000 => "2",
+                    _ => "0"
+                };
             }
             }
 
 
-            // Empty bitrate area is not allow on iOS
-            // Default audio bitrate to 128K if it is not being requested
-            // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
-            return 128000;
+            if (string.Equals(encoder, "libvorbis", StringComparison.OrdinalIgnoreCase))
+            {
+                return " -qscale:a " + bitratePerChannel switch
+                {
+                    < 40000 => "0",
+                    < 56000 => "2",
+                    < 80000 => "4",
+                    < 112000 => "6",
+                    _ => "8"
+                };
+            }
+
+            return null;
         }
         }
 
 
         public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions)
         public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions)
@@ -5670,14 +5725,22 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             var inputChannels = audioStream is null ? 6 : audioStream.Channels ?? 6;
             var inputChannels = audioStream is null ? 6 : audioStream.Channels ?? 6;
+            var shiftAudioCodecs = new List<string>();
             if (inputChannels >= 6)
             if (inputChannels >= 6)
             {
             {
-                return;
+                // DTS and TrueHD are not supported by HLS
+                // Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
+                shiftAudioCodecs.Add("dca");
+                shiftAudioCodecs.Add("truehd");
+            }
+            else
+            {
+                // Transcoding to 2ch ac3 or eac3 almost always causes a playback failure
+                // Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used
+                shiftAudioCodecs.Add("ac3");
+                shiftAudioCodecs.Add("eac3");
             }
             }
 
 
-            // Transcoding to 2ch ac3 almost always causes a playback failure
-            // Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used
-            var shiftAudioCodecs = new[] { "ac3", "eac3" };
             if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase)))
             if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase)))
             {
             {
                 return;
                 return;
@@ -5919,10 +5982,17 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             var bitrate = state.OutputAudioBitrate;
             var bitrate = state.OutputAudioBitrate;
-
-            if (bitrate.HasValue)
+            if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase))
             {
             {
-                args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
+                var vbrParam = GetAudioVbrModeParam(codec, bitrate.Value / (channels ?? 2));
+                if (encodingOptions.EnableAudioVbr && vbrParam is not null)
+                {
+                    args += vbrParam;
+                }
+                else
+                {
+                    args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
+                }
             }
             }
 
 
             if (state.OutputAudioSampleRate.HasValue)
             if (state.OutputAudioSampleRate.HasValue)
@@ -5940,23 +6010,33 @@ namespace MediaBrowser.Controller.MediaEncoding
             var audioTranscodeParams = new List<string>();
             var audioTranscodeParams = new List<string>();
 
 
             var bitrate = state.OutputAudioBitrate;
             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));
+                var vbrParam = GetAudioVbrModeParam(outputCodec, bitrate.Value / (channels ?? 2));
+                if (encodingOptions.EnableAudioVbr && vbrParam is not null)
+                {
+                    audioTranscodeParams.Add(vbrParam);
+                }
+                else
+                {
+                    audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture));
+                }
             }
             }
 
 
-            if (state.OutputAudioChannels.HasValue)
+            if (channels.HasValue)
             {
             {
                 audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
                 audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
             }
             }
 
 
-            if (!string.IsNullOrEmpty(state.OutputAudioCodec))
+            if (!string.IsNullOrEmpty(outputCodec))
             {
             {
                 audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state));
                 audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state));
             }
             }
 
 
-            if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
+            if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase))
             {
             {
                 // opus only supports specific sampling rates
                 // opus only supports specific sampling rates
                 var sampleRate = state.OutputAudioSampleRate;
                 var sampleRate = state.OutputAudioSampleRate;

+ 4 - 1
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs

@@ -25,11 +25,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
             "mpeg2video",
             "mpeg2video",
             "mpeg4",
             "mpeg4",
             "msmpeg4",
             "msmpeg4",
-            "dts",
+            "dca",
             "ac3",
             "ac3",
             "aac",
             "aac",
             "mp3",
             "mp3",
             "flac",
             "flac",
+            "truehd",
             "h264_qsv",
             "h264_qsv",
             "hevc_qsv",
             "hevc_qsv",
             "mpeg2_qsv",
             "mpeg2_qsv",
@@ -59,10 +60,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
             "aac_at",
             "aac_at",
             "libfdk_aac",
             "libfdk_aac",
             "ac3",
             "ac3",
+            "dca",
             "libmp3lame",
             "libmp3lame",
             "libopus",
             "libopus",
             "libvorbis",
             "libvorbis",
             "flac",
             "flac",
+            "truehd",
             "srt",
             "srt",
             "h264_amf",
             "h264_amf",
             "hevc_amf",
             "hevc_amf",

+ 6 - 0
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -14,6 +14,7 @@ public class EncodingOptions
     public EncodingOptions()
     public EncodingOptions()
     {
     {
         EnableFallbackFont = false;
         EnableFallbackFont = false;
+        EnableAudioVbr = false;
         DownMixAudioBoost = 2;
         DownMixAudioBoost = 2;
         DownMixStereoAlgorithm = DownMixStereoAlgorithms.None;
         DownMixStereoAlgorithm = DownMixStereoAlgorithms.None;
         MaxMuxingQueueSize = 2048;
         MaxMuxingQueueSize = 2048;
@@ -71,6 +72,11 @@ public class EncodingOptions
     /// </summary>
     /// </summary>
     public bool EnableFallbackFont { get; set; }
     public bool EnableFallbackFont { get; set; }
 
 
+    /// <summary>
+    /// Gets or sets a value indicating whether audio VBR is enabled.
+    /// </summary>
+    public bool EnableAudioVbr { get; set; }
+
     /// <summary>
     /// <summary>
     /// Gets or sets the audio boost applied when downmixing audio.
     /// Gets or sets the audio boost applied when downmixing audio.
     /// </summary>
     /// </summary>

+ 24 - 0
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -23,6 +23,9 @@ namespace MediaBrowser.Model.Dlna
 
 
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly ITranscoderSupport _transcoderSupport;
         private readonly ITranscoderSupport _transcoderSupport;
+        private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc" };
+        private static readonly string[] _supportedHlsAudioCodecsTs = new string[] { "aac", "ac3", "eac3", "mp3" };
+        private static readonly string[] _supportedHlsAudioCodecsMp4 = new string[] { "aac", "ac3", "eac3", "mp3", "alac", "flac", "opus", "dca", "truehd" };
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="StreamBuilder"/> class.
         /// Initializes a new instance of the <see cref="StreamBuilder"/> class.
@@ -801,6 +804,13 @@ namespace MediaBrowser.Model.Dlna
         {
         {
             // Prefer matching video codecs
             // Prefer matching video codecs
             var videoCodecs = ContainerProfile.SplitValue(videoCodec);
             var videoCodecs = ContainerProfile.SplitValue(videoCodec);
+
+            // Enforce HLS video codec restrictions
+            if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+            {
+                videoCodecs = videoCodecs.Where(codec => _supportedHlsVideoCodecs.Contains(codec)).ToArray();
+            }
+
             var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
             var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
             if (directVideoCodec is not null)
             if (directVideoCodec is not null)
             {
             {
@@ -836,6 +846,20 @@ namespace MediaBrowser.Model.Dlna
 
 
             // Prefer matching audio codecs, could do better here
             // Prefer matching audio codecs, could do better here
             var audioCodecs = ContainerProfile.SplitValue(audioCodec);
             var audioCodecs = ContainerProfile.SplitValue(audioCodec);
+
+            // Enforce HLS audio codec restrictions
+            if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+            {
+                if (string.Equals(playlistItem.Container, "mp4", StringComparison.OrdinalIgnoreCase))
+                {
+                    audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsMp4.Contains(codec)).ToArray();
+                }
+                else
+                {
+                    audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsTs.Contains(codec)).ToArray();
+                }
+            }
+
             var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
             var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
             playlistItem.AudioCodecs = audioCodecs;
             playlistItem.AudioCodecs = audioCodecs;
             if (directAudioStream is not null)
             if (directAudioStream is not null)