From 832b36562aef04b2c0f6d012920fcf80e0dd080c Mon Sep 17 00:00:00 2001 From: Dominik Date: Mon, 14 Nov 2022 10:11:25 +0100 Subject: Add new options for segment deletion --- MediaBrowser.Model/Configuration/EncodingOptions.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f4cd2f006..c23d4e642 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -11,7 +11,9 @@ namespace MediaBrowser.Model.Configuration DownMixAudioBoost = 2; MaxMuxingQueueSize = 2048; EnableThrottling = false; + EnableSegmentDeletion = false; ThrottleDelaySeconds = 180; + SegmentKeepSeconds = 200; EncodingThreadCount = -1; // This is a DRM device that is almost guaranteed to be there on every intel platform, // plus it's the default one in ffmpeg if you don't specify anything @@ -57,8 +59,12 @@ namespace MediaBrowser.Model.Configuration public bool EnableThrottling { get; set; } + public bool EnableSegmentDeletion { get; set; } + public int ThrottleDelaySeconds { get; set; } + public int SegmentKeepSeconds { get; set; } + public string HardwareAccelerationType { get; set; } /// -- cgit v1.2.3 From 09a1d6786a0cad593cc862d62351a0c5e748497f Mon Sep 17 00:00:00 2001 From: Dominik Date: Tue, 15 Nov 2022 16:06:24 +0100 Subject: Increase default SegmentKeepSeconds --- MediaBrowser.Model/Configuration/EncodingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index c23d4e642..008050602 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Model.Configuration EnableThrottling = false; EnableSegmentDeletion = false; ThrottleDelaySeconds = 180; - SegmentKeepSeconds = 200; + SegmentKeepSeconds = 360; EncodingThreadCount = -1; // This is a DRM device that is almost guaranteed to be there on every intel platform, // plus it's the default one in ffmpeg if you don't specify anything -- cgit v1.2.3 From aa99aaebc4e37ca1e16c11f72dd4a57038200179 Mon Sep 17 00:00:00 2001 From: knackebrot Date: Mon, 7 Nov 2022 00:15:04 +0100 Subject: Add audio vbr calculation --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 29 ++++++++-- .../MediaEncoding/EncodingHelper.cs | 64 +++++++++++++++++++++- .../Configuration/EncodingOptions.cs | 6 ++ 3 files changed, 91 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 4d8b4de24..7b1830761 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1685,14 +1685,25 @@ public class DynamicHlsController : BaseJellyfinApiController audioTranscodeParams += "-acodec " + audioCodec; - if (state.OutputAudioBitrate.HasValue) + var audioBitrate = state.OutputAudioBitrate; + var audioChannels = state.OutputAudioChannels; + + if (audioBitrate.HasValue) { - audioTranscodeParams += " -ab " + state.OutputAudioBitrate.Value.ToString(CultureInfo.InvariantCulture); + string vbrParam; + if (_encodingOptions.EnableAudioVbr && (vbrParam = _encodingHelper.GetAudioVbrModeParam(state.OutputAudioCodec, audioBitrate.Value / audioChannels ?? 2)) != 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) @@ -1747,7 +1758,15 @@ public class DynamicHlsController : BaseJellyfinApiController if (bitrate.HasValue) { - args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + string vbrParam; + if (_encodingOptions.EnableAudioVbr && (vbrParam = _encodingHelper.GetAudioVbrModeParam(state.OutputAudioCodec, bitrate.Value / channels ?? 2)) != null) + { + args += vbrParam; + } + else + { + args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + } } if (state.OutputAudioSampleRate.HasValue) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 648358b59..551160934 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2160,6 +2160,47 @@ namespace MediaBrowser.Controller.MediaEncoding return 128000; } + public string GetAudioVbrModeParam(string encoder, int bitratePerChannel) + { + if (encoder == "libfdk_aac") + { + return " -vbr:a " + bitratePerChannel switch + { + < 32000 => "1", + < 48000 => "2", + < 64000 => "3", + < 96000 => "4", + _ => "5" + }; + } + + if (encoder == "libmp3lame") + { + return " -qscale:a " + bitratePerChannel switch + { + < 48000 => "8", + < 64000 => "6", + < 88000 => "4", + < 112000 => "2", + _ => "0" + }; + } + + if (encoder == "libvorbis") + { + return " -qscale:a " + bitratePerChannel switch + { + < 40000 => "0", + < 56000 => "2", + < 80000 => "4", + < 112000 => "6", + _ => "8" + }; + } + + return null; + } + public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) { var channels = state.OutputAudioChannels; @@ -5801,7 +5842,15 @@ namespace MediaBrowser.Controller.MediaEncoding if (bitrate.HasValue) { - args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + string vbrParam; + if (encodingOptions.EnableAudioVbr && (vbrParam = GetAudioVbrModeParam(codec, bitrate.Value / channels ?? 2)) != null) + { + args += vbrParam; + } + else + { + args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + } } if (state.OutputAudioSampleRate.HasValue) @@ -5819,13 +5868,22 @@ namespace MediaBrowser.Controller.MediaEncoding var audioTranscodeParams = new List(); var bitrate = state.OutputAudioBitrate; + var channels = state.OutputAudioChannels; if (bitrate.HasValue) { - audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture)); + string vbrParam; + if (encodingOptions.EnableAudioVbr && (vbrParam = GetAudioVbrModeParam(state.OutputAudioCodec, bitrate.Value / channels ?? 2)) != 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)); } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 0ff95a2e1..b43e0f024 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -14,6 +14,7 @@ public class EncodingOptions public EncodingOptions() { EnableFallbackFont = false; + EnableAudioVbr = false; DownMixAudioBoost = 2; DownMixStereoAlgorithm = DownMixStereoAlgorithms.None; MaxMuxingQueueSize = 2048; @@ -70,6 +71,11 @@ public class EncodingOptions /// public bool EnableFallbackFont { get; set; } + /// + /// Gets or sets a value indicating whether audio VBR is enabled. + /// + public bool EnableAudioVbr { get; set; } + /// /// Gets or sets the audio boost applied when downmixing audio. /// -- cgit v1.2.3 From 638bda629ba2fdceb58b0732f9c2cf4684edc115 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 9 Mar 2023 19:35:54 -0500 Subject: Backport pull request #9391 from jellyfin/release-10.8.z Fix H.264 baseline hwaccel and enable enhanced Nvdec by default Original-merge: 22a8283a9e3425da0496c28e6737dfadf9c67b33 Merged-by: Bond-009 Backported-by: crobibero --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 17 +++++++++++++---- MediaBrowser.Model/Configuration/EncodingOptions.cs | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index f07ac0751..0f3912e28 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4909,6 +4909,10 @@ namespace MediaBrowser.Controller.MediaEncoding // Set the av1 codec explicitly to trigger hw accelerator, otherwise libdav1d will be used. var isAv1 = string.Equals(videoCodec, "av1", StringComparison.OrdinalIgnoreCase); + // Allow profile mismatch if decoding H.264 baseline with d3d11va and vaapi hwaccels. + var profileMismatch = string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.VideoStream?.Profile, "baseline", StringComparison.OrdinalIgnoreCase); + if (bitDepth == 10 && isCodecAvailable) { if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase) @@ -4933,14 +4937,16 @@ namespace MediaBrowser.Controller.MediaEncoding { if (isVaapiSupported && isCodecAvailable) { - return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); + return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + + (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); } if (isD3d11Supported && isCodecAvailable) { // set -threads 3 to intel d3d11va decoder explicitly. Lower threads may result in dead lock. // on newer devices such as Xe, the larger the init_pool_size, the longer the initialization time for opencl to derive from d3d11. - return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + " -threads 3" + (isAv1 ? " -c:v av1" : string.Empty); + return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + + (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + " -threads 3" + (isAv1 ? " -c:v av1" : string.Empty); } } else @@ -4975,7 +4981,8 @@ namespace MediaBrowser.Controller.MediaEncoding { if (isD3d11Supported && isCodecAvailable) { - return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); + return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty) + + (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); } } @@ -4984,9 +4991,11 @@ namespace MediaBrowser.Controller.MediaEncoding && isVaapiSupported && isCodecAvailable) { - return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); + return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty) + + (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty); } + // Apple videotoolbox if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase) && isVideotoolboxSupported && isCodecAvailable) diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 0ff95a2e1..f348d8417 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -39,7 +39,8 @@ public class EncodingOptions DeinterlaceMethod = "yadif"; EnableDecodingColorDepth10Hevc = true; EnableDecodingColorDepth10Vp9 = true; - EnableEnhancedNvdecDecoder = false; + // Enhanced Nvdec or system native decoder is required for DoVi to SDR tone-mapping. + EnableEnhancedNvdecDecoder = true; PreferSystemNativeHwDecoder = true; EnableIntelLowPowerH264HwEncoder = false; EnableIntelLowPowerHevcHwEncoder = false; -- cgit v1.2.3 From 4c8d3827658342b630472cc4dc1601ae56ffe39c Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 13 Jun 2023 10:53:35 -0400 Subject: Backport pull request #9642 from jellyfin/release-10.8.z Fix the brightness of VPP tonemap and add the tonemap mode Original-merge: d5a8419bc52ba06c070012849ba166dd2fbff8b0 Merged-by: Cody Robibero Backported-by: Bond_009 --- .../MediaEncoding/EncodingHelper.cs | 23 +++++++++++++++++----- .../Configuration/EncodingOptions.cs | 16 +++++++-------- 2 files changed, 26 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 906a04a61..b6fa49fcd 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -45,6 +45,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegImplictHwaccel = new Version(6, 0); private readonly Version _minFFmpegHwaUnsafeOutput = new Version(6, 0); + private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3); private static readonly string[] _videoProfilesH264 = new[] { @@ -2929,7 +2930,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - public static string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat) + public string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat) { if (string.IsNullOrEmpty(hwTonemapSuffix)) { @@ -2941,7 +2942,8 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase)) { - args = "tonemap_vaapi=format={0}:p=bt709:t=bt709:m=bt709,procamp_vaapi=b={1}:c={2}:extra_hw_frames=16"; + args = "procamp_vaapi=b={2}:c={3}," + args + ":extra_hw_frames=32"; + return string.Format( CultureInfo.InvariantCulture, args, @@ -2972,14 +2974,24 @@ namespace MediaBrowser.Controller.MediaEncoding { args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}"; + if (string.Equals(options.TonemappingMode, "max", StringComparison.OrdinalIgnoreCase) + || string.Equals(options.TonemappingMode, "rgb", StringComparison.OrdinalIgnoreCase)) + { + if (_mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode) + { + args += ":tonemap_mode={5}"; + } + } + if (options.TonemappingParam != 0) { - args += ":param={5}"; + args += ":param={6}"; } - if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase) + || string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase)) { - args += ":range={6}"; + args += ":range={7}"; } } @@ -2991,6 +3003,7 @@ namespace MediaBrowser.Controller.MediaEncoding algorithm, options.TonemappingPeak, options.TonemappingDesat, + options.TonemappingMode, options.TonemappingParam, options.TonemappingRange); } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f9f63f751..ac2f1e71a 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -27,13 +27,13 @@ public class EncodingOptions EnableTonemapping = false; EnableVppTonemapping = false; TonemappingAlgorithm = "bt2390"; + TonemappingMode = "auto"; TonemappingRange = "auto"; TonemappingDesat = 0; - TonemappingThreshold = 0.8; TonemappingPeak = 100; TonemappingParam = 0; - VppTonemappingBrightness = 0; - VppTonemappingContrast = 1.2; + VppTonemappingBrightness = 16; + VppTonemappingContrast = 1; H264Crf = 23; H265Crf = 28; DeinterlaceDoubleRate = false; @@ -137,6 +137,11 @@ public class EncodingOptions /// public string TonemappingAlgorithm { get; set; } + /// + /// Gets or sets the tone-mapping mode. + /// + public string TonemappingMode { get; set; } + /// /// Gets or sets the tone-mapping range. /// @@ -147,11 +152,6 @@ public class EncodingOptions /// public double TonemappingDesat { get; set; } - /// - /// Gets or sets the tone-mapping threshold. - /// - public double TonemappingThreshold { get; set; } - /// /// Gets or sets the tone-mapping peak. /// -- cgit v1.2.3 From 0dffe64489f826b91410bddc32ad2ceb1c5cc0d0 Mon Sep 17 00:00:00 2001 From: Dominik Date: Thu, 15 Jun 2023 19:55:11 +0200 Subject: Add baseUrlParam back in and fix indentation --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 9 +++++++++ MediaBrowser.Model/Configuration/EncodingOptions.cs | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 86be68004..9f2088e36 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1632,6 +1632,15 @@ public class DynamicHlsController : BaseJellyfinApiController ? _encodingOptions.MaxMuxingQueueSize.ToString(CultureInfo.InvariantCulture) : "128"; + var baseUrlParam = string.Empty; + if (isEventPlaylist) + { + baseUrlParam = string.Format( + CultureInfo.InvariantCulture, + " -hls_base_url \"hls/{0}/\"", + Path.GetFileNameWithoutExtension(outputPath)); + } + var hlsArguments = GetHlsArguments(isEventPlaylist, state.SegmentLength); return string.Format( diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index b5b6e86c8..f11a43122 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -103,12 +103,12 @@ public class EncodingOptions /// Gets or sets the delay after which throttling happens. /// public int ThrottleDelaySeconds { get; set; } - + /// /// Gets or sets a value indicating whether segment deletion is enabled. /// public bool EnableSegmentDeletion { get; set; } - + /// /// Gets or sets seconds for which segments should be kept before being deleted. /// -- cgit v1.2.3 From dbe44a591cbc19859161cd6cd7f63dab91110594 Mon Sep 17 00:00:00 2001 From: Dominik Date: Thu, 15 Jun 2023 21:38:45 +0200 Subject: Pick safer default value for segments to keep --- MediaBrowser.Model/Configuration/EncodingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f11a43122..a53be0fee 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -21,7 +21,7 @@ public class EncodingOptions EnableThrottling = false; ThrottleDelaySeconds = 180; EnableSegmentDeletion = false; - SegmentKeepSeconds = 360; + SegmentKeepSeconds = 720; EncodingThreadCount = -1; // This is a DRM device that is almost guaranteed to be there on every intel platform, // plus it's the default one in ffmpeg if you don't specify anything -- cgit v1.2.3 From be01aeecd9d211243c6d67935c593aeb93219260 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 20 Jun 2023 03:49:26 +0800 Subject: Add AV1 hardware and software encoding Signed-off-by: nyanmisaka --- .../MediaEncoding/EncodingHelper.cs | 187 ++++++++++++++++++--- .../Encoder/EncoderValidator.cs | 5 + .../Configuration/EncodingOptions.cs | 6 + 3 files changed, 171 insertions(+), 27 deletions(-) (limited to 'MediaBrowser.Model/Configuration/EncodingOptions.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b155d674d..d5049ca6b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -46,6 +46,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegImplictHwaccel = new Version(6, 0); private readonly Version _minFFmpegHwaUnsafeOutput = new Version(6, 0); private readonly Version _minFFmpegOclCuTonemapMode = new Version(5, 1, 3); + private readonly Version _minFFmpegSvtAv1Params = new Version(5, 1); private static readonly string[] _videoProfilesH264 = new[] { @@ -65,6 +66,13 @@ namespace MediaBrowser.Controller.MediaEncoding "Main10" }; + private static readonly string[] _videoProfilesAv1 = new[] + { + "Main", + "High", + "Professional", + }; + private static readonly HashSet _mp4ContainerNames = new(StringComparer.OrdinalIgnoreCase) { "mp4", @@ -113,12 +121,15 @@ namespace MediaBrowser.Controller.MediaEncoding } public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) - => GetH264OrH265Encoder("libx264", "h264", state, encodingOptions); + => GetH26xOrAv1Encoder("libx264", "h264", state, encodingOptions); public string GetH265Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) - => GetH264OrH265Encoder("libx265", "hevc", state, encodingOptions); + => GetH26xOrAv1Encoder("libx265", "hevc", state, encodingOptions); + + public string GetAv1Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) + => GetH26xOrAv1Encoder("libsvtav1", "av1", state, encodingOptions); - private string GetH264OrH265Encoder(string defaultEncoder, string hwEncoder, EncodingJobInfo state, EncodingOptions encodingOptions) + private string GetH26xOrAv1Encoder(string defaultEncoder, string hwEncoder, EncodingJobInfo state, EncodingOptions encodingOptions) { // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully @@ -266,6 +277,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { + if (string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase)) + { + return GetAv1Encoder(state, encodingOptions); + } + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { @@ -565,6 +581,11 @@ namespace MediaBrowser.Controller.MediaEncoding return Array.FindIndex(_videoProfilesH265, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase)); } + if (string.Equals("av1", videoCodec, StringComparison.OrdinalIgnoreCase)) + { + return Array.FindIndex(_videoProfilesAv1, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase)); + } + return -1; } @@ -1204,6 +1225,11 @@ namespace MediaBrowser.Controller.MediaEncoding return FormattableString.Invariant($" -b:v {bitrate}"); } + if (string.Equals(videoCodec, "libsvtav1", StringComparison.OrdinalIgnoreCase)) + { + return FormattableString.Invariant($" -b:v {bitrate} -bufsize {bufsize}"); + } + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) { @@ -1211,14 +1237,16 @@ namespace MediaBrowser.Controller.MediaEncoding } if (string.Equals(videoCodec, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoCodec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoCodec, "hevc_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoCodec, "av1_amf", StringComparison.OrdinalIgnoreCase)) { // Override the too high default qmin 18 in transcoding preset return FormattableString.Invariant($" -rc cbr -qmin 0 -qmax 32 -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); } if (string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoCodec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoCodec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoCodec, "av1_vaapi", StringComparison.OrdinalIgnoreCase)) { // VBR in i965 driver may result in pixelated output. if (_mediaEncoder.IsVaapiDeviceInteli965) @@ -1236,14 +1264,23 @@ namespace MediaBrowser.Controller.MediaEncoding { if (double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel)) { - if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputVideoCodec, "h265", 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) + { + return "15"; + } + } + 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 >= 150) + if (requestLevel < 0 || requestLevel >= 150) { return "150"; } @@ -1253,7 +1290,7 @@ namespace MediaBrowser.Controller.MediaEncoding // 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 >= 51) + if (requestLevel < 0 || requestLevel >= 51) { return "51"; } @@ -1391,14 +1428,18 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "av1_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "av1_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "av1_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "libsvtav1", StringComparison.OrdinalIgnoreCase)) { args += gopArg; } else if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "av1_vaapi", StringComparison.OrdinalIgnoreCase)) { args += keyFrameArg; @@ -1534,18 +1575,60 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -crf " + defaultCrf; } } + else if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase)) + { + // Default to use the recommended preset 10. + // Omit presets < 5, which are too slow for on the fly encoding. + // https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md + param += encodingOptions.EncoderPreset switch + { + "veryslow" => " -preset 5", + "slower" => " -preset 6", + "slow" => " -preset 7", + "medium" => " -preset 8", + "fast" => " -preset 9", + "faster" => " -preset 10", + "veryfast" => " -preset 11", + "superfast" => " -preset 12", + "ultrafast" => " -preset 13", + _ => " -preset 10" + }; + } + else if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "av1_vaapi", StringComparison.OrdinalIgnoreCase)) + { + // -compression_level is not reliable on AMD. + if (_mediaEncoder.IsVaapiDeviceInteliHD) + { + param += encodingOptions.EncoderPreset switch + { + "veryslow" => " -compression_level 1", + "slower" => " -compression_level 2", + "slow" => " -compression_level 3", + "medium" => " -compression_level 4", + "fast" => " -compression_level 5", + "faster" => " -compression_level 6", + "veryfast" => " -compression_level 7", + "superfast" => " -compression_level 7", + "ultrafast" => " -compression_level 7", + _ => string.Empty + }; + } + } else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) // h264 (h264_qsv) - || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_qsv) + || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) // hevc (hevc_qsv) + || string.Equals(videoEncoder, "av1_qsv", StringComparison.OrdinalIgnoreCase)) // av1 (av1_qsv) { - string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; + string[] valid_presets = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; - if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparison.OrdinalIgnoreCase)) + if (valid_presets.Contains(encodingOptions.EncoderPreset, StringComparison.OrdinalIgnoreCase)) { param += " -preset " + encodingOptions.EncoderPreset; } else { - param += " -preset 7"; + param += " -preset veryfast"; } // Only h264_qsv has look_ahead option @@ -1555,7 +1638,8 @@ namespace MediaBrowser.Controller.MediaEncoding } } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) - || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc) + || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc) + || string.Equals(videoEncoder, "av1_nvenc", StringComparison.OrdinalIgnoreCase)) // av1 (av1_nvenc) { switch (encodingOptions.EncoderPreset) { @@ -1595,7 +1679,8 @@ namespace MediaBrowser.Controller.MediaEncoding } } else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // h264 (h264_amf) - || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_amf) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) // hevc (hevc_amf) + || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase)) // av1 (av1_amf) { switch (encodingOptions.EncoderPreset) { @@ -1622,9 +1707,15 @@ namespace MediaBrowser.Controller.MediaEncoding break; } + if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase)) + { + param += " -header_insertion_mode gop"; + } + if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { - param += " -header_insertion_mode gop -gops_per_idr 1"; + param += " -gops_per_idr 1"; } } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // vp8 @@ -1755,6 +1846,14 @@ namespace MediaBrowser.Controller.MediaEncoding profile = "high"; } + // We only need Main profile of AV1 encoders. + if (videoEncoder.Contains("av1", StringComparison.OrdinalIgnoreCase) + && (profile.Contains("high", StringComparison.OrdinalIgnoreCase) + || profile.Contains("professional", StringComparison.OrdinalIgnoreCase))) + { + profile = "main"; + } + // h264_vaapi does not support Baseline profile, force Constrained Baseline in this case, // which is compatible (and ugly). if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) @@ -1822,19 +1921,41 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -level " + (hevcLevel / 3); } } + else if (string.Equals(videoEncoder, "av1_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase)) + { + // libsvtav1 and av1_qsv use -level 60 instead of -level 16 + // https://aomedia.org/av1/specification/annex-a/ + if (int.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out int av1Level)) + { + var x = 2 + (av1Level >> 2); + var y = av1Level & 3; + var res = (x * 10) + y; + param += " -level " + res; + } + } else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase)) { param += " -level " + level; } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) + || string.Equals(videoEncoder, "av1_nvenc", StringComparison.OrdinalIgnoreCase)) { // level option may cause NVENC to fail. // NVENC cannot adjust the given level, just throw an error. + } + else if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "av1_vaapi", StringComparison.OrdinalIgnoreCase)) + { // level option may cause corrupted frames on AMD VAAPI. + if (_mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965) + { + param += " -level " + level; + } } else if (!string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) { @@ -1856,6 +1977,12 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -x265-params:0 no-info=1"; } + if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase) + && _mediaEncoder.EncoderVersion >= _minFFmpegSvtAv1Params) + { + param += " -svtav1-params:0 rc=1:tune=0:film-grain=0:enable-overlays=1:enable-tf=0"; + } + return param; } @@ -5810,19 +5937,25 @@ namespace MediaBrowser.Controller.MediaEncoding private void ShiftVideoCodecsIfNeeded(List videoCodecs, EncodingOptions encodingOptions) { - // Shift hevc/h265 to the end of list if hevc encoding is not allowed. - if (encodingOptions.AllowHevcEncoding) + // No need to shift if there is only one supported video codec. + if (videoCodecs.Count < 2) { return; } - // No need to shift if there is only one supported video codec. - if (videoCodecs.Count < 2) + // Shift codecs to the end of list if it's not allowed. + var shiftVideoCodecs = new List(); + if (!encodingOptions.AllowHevcEncoding) { - return; + shiftVideoCodecs.Add("hevc"); + shiftVideoCodecs.Add("h265"); + } + + if (!encodingOptions.AllowAv1Encoding) + { + shiftVideoCodecs.Add("av1"); } - var shiftVideoCodecs = new[] { "hevc", "h265" }; if (videoCodecs.All(i => shiftVideoCodecs.Contains(i, StringComparison.OrdinalIgnoreCase))) { return; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index d3843796f..e1a0e8d67 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -52,6 +52,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { "libx264", "libx265", + "libsvtav1", "mpeg4", "msmpeg4", "libvpx", @@ -69,12 +70,16 @@ namespace MediaBrowser.MediaEncoding.Encoder "srt", "h264_amf", "hevc_amf", + "av1_amf", "h264_qsv", "hevc_qsv", + "av1_qsv", "h264_nvenc", "hevc_nvenc", + "av1_nvenc", "h264_vaapi", "hevc_vaapi", + "av1_vaapi", "h264_v4l2m2m", "h264_videotoolbox", "hevc_videotoolbox" diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index a53be0fee..3f0e98ec8 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -49,6 +49,7 @@ public class EncodingOptions EnableIntelLowPowerHevcHwEncoder = false; EnableHardwareEncoding = true; AllowHevcEncoding = false; + AllowAv1Encoding = false; EnableSubtitleExtraction = true; AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" }; HardwareDecodingCodecs = new string[] { "h264", "vc1" }; @@ -249,6 +250,11 @@ public class EncodingOptions /// public bool AllowHevcEncoding { get; set; } + /// + /// Gets or sets a value indicating whether AV1 encoding is enabled. + /// + public bool AllowAv1Encoding { get; set; } + /// /// Gets or sets a value indicating whether subtitle extraction is enabled. /// -- cgit v1.2.3