Sfoglia il codice sorgente

adjust bitrate limit for HLS audio codecs

nyanmisaka 4 anni fa
parent
commit
57e5b59b93

+ 1 - 1
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -222,7 +222,7 @@ namespace Jellyfin.Api.Helpers
 
                         EncodingHelper encodingHelper = new EncodingHelper(_mediaEncoder, _fileSystem, _subtitleEncoder, _configuration);
                         var sdrOutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0;
-                        var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest.AudioBitRate, state.AudioStream) ?? 0;
+                        var sdrOutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0;
                         var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate;
 
                         AppendPlaylist(builder, state, sdrVideoUrl, sdrTotalBitrate, subtitleGroup);

+ 35 - 14
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
 
             state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
 
-            state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, state.AudioStream);
+            state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
 
             state.OutputAudioCodec = streamingRequest.AudioCodec;
 
@@ -196,20 +196,41 @@ namespace Jellyfin.Api.Helpers
 
                 encodingHelper.TryStreamCopy(state);
 
-                if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
+                if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.OutputVideoBitrate.HasValue)
                 {
-                    var resolution = ResolutionNormalizer.Normalize(
-                        state.VideoStream?.BitRate,
-                        state.VideoStream?.Width,
-                        state.VideoStream?.Height,
-                        state.OutputVideoBitrate.Value,
-                        state.VideoStream?.Codec,
-                        state.OutputVideoCodec,
-                        state.VideoRequest.MaxWidth,
-                        state.VideoRequest.MaxHeight);
-
-                    state.VideoRequest.MaxWidth = resolution.MaxWidth;
-                    state.VideoRequest.MaxHeight = resolution.MaxHeight;
+                    var isVideoResolutionNotRequested = !state.VideoRequest.Width.HasValue
+                        && !state.VideoRequest.Height.HasValue
+                        && !state.VideoRequest.MaxWidth.HasValue
+                        && !state.VideoRequest.MaxHeight.HasValue;
+
+                    if (isVideoResolutionNotRequested
+                        && state.VideoRequest.VideoBitRate.HasValue
+                        && state.VideoStream.BitRate.HasValue
+                        && state.VideoRequest.VideoBitRate.Value >= state.VideoStream.BitRate.Value)
+                    {
+                        // Don't downscale the resolution if the width/height/MaxWidth/MaxHeight is not requested,
+                        // and the requested video bitrate is higher than source video bitrate.
+                        if (state.VideoStream.Width.HasValue || state.VideoStream.Height.HasValue)
+                        {
+                            state.VideoRequest.MaxWidth = state.VideoStream?.Width;
+                            state.VideoRequest.MaxHeight = state.VideoStream?.Height;
+                        }
+                    }
+                    else
+                    {
+                        var resolution = ResolutionNormalizer.Normalize(
+                            state.VideoStream?.BitRate,
+                            state.VideoStream?.Width,
+                            state.VideoStream?.Height,
+                            state.OutputVideoBitrate.Value,
+                            state.VideoStream?.Codec,
+                            state.OutputVideoCodec,
+                            state.VideoRequest.MaxWidth,
+                            state.VideoRequest.MaxHeight);
+
+                        state.VideoRequest.MaxWidth = resolution.MaxWidth;
+                        state.VideoRequest.MaxHeight = resolution.MaxHeight;
+                    }
                 }
             }
 

+ 31 - 19
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -1390,7 +1390,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                 || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
                 || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
             {
-                return .5;
+                return .6;
             }
 
             return 1;
@@ -1424,36 +1424,48 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
         {
-            if (audioStream == null)
-            {
-                return null;
-            }
-
-            if (request.AudioBitRate.HasValue)
-            {
-                // Don't encode any higher than this
-                return Math.Min(384000, request.AudioBitRate.Value);
-            }
-
-            // 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;
+            return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream);
         }
 
-        public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream)
+        public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream)
         {
             if (audioStream == null)
             {
                 return null;
             }
 
-            if (audioBitRate.HasValue)
+            if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec))
             {
-                // Don't encode any higher than this
                 return Math.Min(384000, audioBitRate.Value);
             }
 
+            if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec))
+            {
+                if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
+                {
+                    if ((audioStream.Channels ?? 0) >= 6)
+                    {
+                        return Math.Min(640000, audioBitRate.Value);
+                    }
+
+                    return Math.Min(384000, audioBitRate.Value);
+                }
+
+                if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
+                {
+                    if ((audioStream.Channels ?? 0) >= 6)
+                    {
+                        return Math.Min(3584000, audioBitRate.Value);
+                    }
+
+                    return Math.Min(1536000, audioBitRate.Value);
+                }
+            }
+
             // 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

+ 110 - 2
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -234,8 +234,8 @@ namespace MediaBrowser.MediaEncoding.Probing
 
             var channelsValue = channels.Value;
 
-            if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
             {
                 if (channelsValue <= 2)
                 {
@@ -248,6 +248,34 @@ namespace MediaBrowser.MediaEncoding.Probing
                 }
             }
 
+            if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
+            {
+                if (channelsValue <= 2)
+                {
+                    return 192000;
+                }
+
+                if (channelsValue >= 5)
+                {
+                    return 640000;
+                }
+            }
+
+            if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(codec, "alac", StringComparison.OrdinalIgnoreCase))
+            {
+                if (channelsValue <= 2)
+                {
+                    return 960000;
+                }
+
+                if (channelsValue >= 5)
+                {
+                    return 2880000;
+                }
+            }
+
             return null;
         }
 
@@ -774,6 +802,35 @@ namespace MediaBrowser.MediaEncoding.Probing
                 stream.BitRate = bitrate;
             }
 
+            // Extract bitrate info from tag "BPS" if possible.
+            if (!stream.BitRate.HasValue
+                && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
+            {
+                var bps = GetBPSFromTags(streamInfo);
+                if (bps != null && bps > 0)
+                {
+                    stream.BitRate = bps;
+                }
+            }
+
+            // Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible.
+            if (!stream.BitRate.HasValue
+                && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)))
+            {
+                var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo);
+                var bytes = GetNumberOfBytesFromTags(streamInfo);
+                if (durationInSeconds != null && bytes != null)
+                {
+                    var bps = Convert.ToInt32(bytes * 8 / durationInSeconds);
+                    if (bps > 0)
+                    {
+                        stream.BitRate = bps;
+                    }
+                }
+            }
+
             var disposition = streamInfo.Disposition;
             if (disposition != null)
             {
@@ -963,6 +1020,57 @@ namespace MediaBrowser.MediaEncoding.Probing
             }
         }
 
+        private int? GetBPSFromTags(MediaStreamInfo streamInfo)
+        {
+            if (streamInfo != null && streamInfo.Tags != null)
+            {
+                var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS");
+                if (!string.IsNullOrEmpty(bps))
+                {
+                    if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps))
+                    {
+                        return parsedBps;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private double? GetRuntimeSecondsFromTags(MediaStreamInfo streamInfo)
+        {
+            if (streamInfo != null && streamInfo.Tags != null)
+            {
+                var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION");
+                if (!string.IsNullOrEmpty(duration))
+                {
+                    if (TimeSpan.TryParse(duration, out var parsedDuration))
+                    {
+                        return parsedDuration.TotalSeconds;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private long? GetNumberOfBytesFromTags(MediaStreamInfo streamInfo)
+        {
+            if (streamInfo != null && streamInfo.Tags != null)
+            {
+                var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES");
+                if (!string.IsNullOrEmpty(numberOfBytes))
+                {
+                    if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes))
+                    {
+                        return parsedBytes;
+                    }
+                }
+            }
+
+            return null;
+        }
+
         private void SetSize(InternalMediaInfoResult data, MediaInfo info)
         {
             if (data.Format != null)

+ 4 - 4
MediaBrowser.Model/Dlna/ResolutionNormalizer.cs

@@ -79,11 +79,11 @@ namespace MediaBrowser.Model.Dlna
 
         private static double GetVideoBitrateScaleFactor(string codec)
         {
-            if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
+                || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
             {
-                return .5;
+                return .6;
             }
 
             return 1;

+ 64 - 8
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -872,11 +872,34 @@ namespace MediaBrowser.Model.Dlna
             return playlistItem;
         }
 
-        private static int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
+        private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels)
         {
-            if ((audioStream.Channels ?? 0) >= 6)
+            if (!string.IsNullOrEmpty(audioCodec))
             {
-                return 384000;
+                // Default to a higher bitrate for stream copy
+                if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
+                {
+                    if ((audioChannels ?? 0) < 2)
+                    {
+                        return 128000;
+                    }
+
+                    return (audioChannels ?? 0) >= 6 ? 640000 : 384000;
+                }
+
+                if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase))
+                {
+                    if ((audioChannels ?? 0) < 2)
+                    {
+                        return 768000;
+                    }
+
+                    return (audioChannels ?? 0) >= 6 ? 3584000 : 1536000;
+                }
             }
 
             return 192000;
@@ -897,14 +920,27 @@ namespace MediaBrowser.Model.Dlna
             }
             else
             {
-                if (targetAudioChannels.HasValue && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
+                if (targetAudioChannels.HasValue
+                    && audioStream.Channels.HasValue
+                    && audioStream.Channels.Value > targetAudioChannels.Value)
                 {
-                    // Reduce the bitrate if we're downmixing
-                    defaultBitrate = targetAudioChannels.Value < 2 ? 128000 : 192000;
+                    // Reduce the bitrate if we're downmixing.
+                    defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
+                }
+                else if (targetAudioChannels.HasValue
+                        && audioStream.Channels.HasValue
+                        && audioStream.Channels.Value <= targetAudioChannels.Value
+                        && !string.IsNullOrEmpty(audioStream.Codec)
+                        && targetAudioCodecs != null
+                        && targetAudioCodecs.Length > 0
+                        && !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase)))
+                {
+                    // Shift the bitrate if we're transcoding to a different audio codec.
+                    defaultBitrate = GetDefaultAudioBitrate(targetAudioCodec, audioStream.Channels.Value);
                 }
                 else
                 {
-                    defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
+                    defaultBitrate = audioStream.BitRate ?? GetDefaultAudioBitrate(targetAudioCodec, targetAudioChannels);
                 }
 
                 // Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
@@ -938,8 +974,28 @@ namespace MediaBrowser.Model.Dlna
             {
                 return 448000;
             }
+            else if (totalBitrate <= 4000000)
+            {
+                return 640000;
+            }
+            else if (totalBitrate <= 5000000)
+            {
+                return 768000;
+            }
+            else if (totalBitrate <= 10000000)
+            {
+                return 1536000;
+            }
+            else if (totalBitrate <= 15000000)
+            {
+                return 2304000;
+            }
+            else if (totalBitrate <= 20000000)
+            {
+                return 3584000;
+            }
 
-            return 640000;
+            return 7168000;
         }
 
         private (PlayMethod?, List<TranscodeReason>) GetVideoDirectPlayProfile(

+ 1 - 1
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -787,7 +787,7 @@ namespace MediaBrowser.Model.Dlna
 
         public int? GetTargetAudioChannels(string codec)
         {
-            var defaultValue = GlobalMaxAudioChannels;
+            var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
 
             var value = GetOption(codec, "audiochannels");
             if (string.IsNullOrEmpty(value))