diff options
| -rw-r--r-- | Jellyfin.Api/Controllers/AudioController.cs | 4 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/DynamicHlsController.cs | 14 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/VideosController.cs | 4 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 71 |
5 files changed, 51 insertions, 46 deletions
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index b6b3e8fe95..590bd05da4 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -112,7 +112,7 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -276,7 +276,7 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 2044710978..c13da3ac7b 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -187,7 +187,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -427,7 +427,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -601,7 +601,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -767,7 +767,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -937,7 +937,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -1114,7 +1114,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -1297,7 +1297,7 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index afae756e48..7854edc5ac 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -334,7 +334,7 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, @@ -572,7 +572,7 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? audioChannels, [FromQuery] int? maxAudioChannels, [FromQuery] string? profile, - [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegex)] string? level, + [FromQuery] [RegularExpression(EncodingHelper.LevelValidationRegexStr)] string? level, [FromQuery] float? framerate, [FromQuery] float? maxFramerate, [FromQuery] bool? copyTimestamps, diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 047d4ed867..bae2756303 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -17,9 +17,7 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.Net.Http.Headers; namespace Jellyfin.Api.Helpers; @@ -487,7 +485,7 @@ public static class StreamingHelpers request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture); break; case 15: - if (videoRequest is not null) + if (videoRequest is not null && EncodingHelper.LevelValidationRegex().IsMatch(val)) { videoRequest.Level = val; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 559f763ada..9f7e35d1ea 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -41,10 +41,10 @@ namespace MediaBrowser.Controller.MediaEncoding public const string ContainerValidationRegexStr = @"^[a-zA-Z0-9\-\._,|]{0,40}$"; /// <summary> - /// The level validation regex. + /// The level validation regex string. /// This regular expression matches strings representing a double. /// </summary> - public const string LevelValidationRegex = @"-?[0-9]+(?:\.[0-9]+)?"; + public const string LevelValidationRegexStr = @"-?[0-9]+(?:\.[0-9]+)?"; private const string _defaultMjpegEncoder = "mjpeg"; @@ -185,9 +185,16 @@ namespace MediaBrowser.Controller.MediaEncoding /// periods, underscores, commas, and vertical bars, with a length between 0 and 40 characters. /// This should matches all common valid codecs. /// </summary> - [GeneratedRegex(@"^[a-zA-Z0-9\-\._,|]{0,40}$")] + [GeneratedRegex(ContainerValidationRegexStr)] public static partial Regex ContainerValidationRegex(); + /// <summary> + /// The level validation regex string. + /// This regular expression matches strings representing a double. + /// </summary> + [GeneratedRegex(LevelValidationRegexStr)] + public static partial Regex LevelValidationRegex(); + [GeneratedRegex(@"\s+")] private static partial Regex WhiteSpaceRegex(); @@ -1797,38 +1804,40 @@ namespace MediaBrowser.Controller.MediaEncoding public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level) { - if (double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel)) + if (!double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel)) + { + return null; + } + + if (string.Equals(state.ActualOutputVideoCodec, "av1", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(state.ActualOutputVideoCodec, "av1", StringComparison.OrdinalIgnoreCase)) + // Transcode to level 5.3 (15) and lower for maximum compatibility. + // https://en.wikipedia.org/wiki/AV1#Levels + if (requestLevel < 0 || requestLevel >= 15) { - // Transcode to level 5.3 (15) and lower for maximum compatibility. - // https://en.wikipedia.org/wiki/AV1#Levels - if (requestLevel < 0 || requestLevel >= 15) - { - return "15"; - } + return "15"; } - else if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) + } + else if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) + { + // Transcode to level 5.0 and lower for maximum compatibility. + // Level 5.0 is suitable for up to 4k 30fps hevc encoding, otherwise let the encoder to handle it. + // https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels + // MaxLumaSampleRate = 3840*2160*30 = 248832000 < 267386880. + if (requestLevel < 0 || requestLevel >= 150) { - // Transcode to level 5.0 and lower for maximum compatibility. - // Level 5.0 is suitable for up to 4k 30fps hevc encoding, otherwise let the encoder to handle it. - // https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels - // MaxLumaSampleRate = 3840*2160*30 = 248832000 < 267386880. - if (requestLevel < 0 || requestLevel >= 150) - { - return "150"; - } + return "150"; } - else if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + } + else if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + // Transcode to level 5.1 and lower for maximum compatibility. + // h264 4k 30fps requires at least level 5.1 otherwise it will break on safari fmp4. + // https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels + if (requestLevel < 0 || requestLevel >= 51) { - // Transcode to level 5.1 and lower for maximum compatibility. - // h264 4k 30fps requires at least level 5.1 otherwise it will break on safari fmp4. - // https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels - if (requestLevel < 0 || requestLevel >= 51) - { - return "51"; - } + return "51"; } } @@ -2218,12 +2227,10 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var level = state.GetRequestedLevel(targetVideoCodec); + var level = NormalizeTranscodingLevel(state, state.GetRequestedLevel(targetVideoCodec)); if (!string.IsNullOrEmpty(level)) { - level = NormalizeTranscodingLevel(state, level); - // libx264, QSV, AMF can adjust the given level to match the output. if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) |
