From 5a8a19e07b0de471d4c0762d1692a29b06d5bfd6 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 8 Sep 2024 01:16:23 +0800 Subject: Add MediaStream.ReferenceFrameRate for problematic video files (#12603) Co-authored-by: Nyanmisaka --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 24cd141dc..220b5d57b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1534,7 +1534,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (maxrate.HasValue && state.VideoStream is not null) { - var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate; + var contentRate = state.VideoStream.ReferenceFrameRate; if (contentRate.HasValue && contentRate.Value > maxrate.Value) { @@ -2218,7 +2218,7 @@ namespace MediaBrowser.Controller.MediaEncoding var requestedFramerate = request.MaxFramerate ?? request.Framerate; if (requestedFramerate.HasValue) { - var videoFrameRate = videoStream.AverageFrameRate ?? videoStream.RealFrameRate; + var videoFrameRate = videoStream.ReferenceFrameRate; if (!videoFrameRate.HasValue || videoFrameRate.Value > requestedFramerate.Value) { @@ -3234,7 +3234,7 @@ namespace MediaBrowser.Controller.MediaEncoding public static string GetSwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options) { - var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.AverageFrameRate <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.ReferenceFrameRate <= 30; return string.Format( CultureInfo.InvariantCulture, "{0}={1}:-1:0", @@ -3244,7 +3244,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix) { - var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase)) { var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase) @@ -3598,7 +3598,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwEncoder = !isNvencEncoder; var isCuInCuOut = isNvDecoder && isNvencEncoder; - var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintH2645 = doDeintH264 || doDeintHevc; -- cgit v1.2.3 From 5d4f71eb9a2095a9fc42f9098bad23a870a50307 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 8 Sep 2024 01:19:26 +0800 Subject: Enable tone-mapping and HLS remuxing for DoVi Profile 10 in AV1 (#12604) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 22 +++++++++++++++------- .../MediaEncoding/EncodingHelper.cs | 3 +-- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 662e2acbc..b9ef189e9 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1797,10 +1797,11 @@ public class DynamicHlsController : BaseJellyfinApiController var args = "-codec:v:0 " + codec; - if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) + var isActualOutputVideoCodecAv1 = string.Equals(state.ActualOutputVideoCodec, "av1", StringComparison.OrdinalIgnoreCase); + var isActualOutputVideoCodecHevc = string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase); + + if (isActualOutputVideoCodecHevc || isActualOutputVideoCodecAv1) { var requestedRange = state.GetRequestedRangeTypes(state.ActualOutputVideoCodec); var requestHasDOVI = requestedRange.Contains(VideoRangeType.DOVI.ToString(), StringComparison.OrdinalIgnoreCase); @@ -1814,10 +1815,17 @@ public class DynamicHlsController : BaseJellyfinApiController || (state.VideoStream.VideoRangeType == VideoRangeType.DOVIWithHLG && requestHasDOVIWithHLG) || (state.VideoStream.VideoRangeType == VideoRangeType.DOVIWithSDR && requestHasDOVIWithSDR))) { - // Prefer dvh1 to dvhe - args += " -tag:v:0 dvh1 -strict -2"; + if (isActualOutputVideoCodecHevc) + { + // Prefer dvh1 to dvhe + args += " -tag:v:0 dvh1 -strict -2"; + } + else if (isActualOutputVideoCodecAv1) + { + args += " -tag:v:0 dav1 -strict -2"; + } } - else + else if (isActualOutputVideoCodecHevc) { // Prefer hvc1 to hev1 args += " -tag:v:0 hvc1"; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 220b5d57b..52faf22f2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -315,8 +315,7 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - if (string.Equals(state.VideoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) - && state.VideoStream.VideoRange == VideoRange.HDR + if (state.VideoStream.VideoRange == VideoRange.HDR && state.VideoStream.VideoRangeType == VideoRangeType.DOVI) { // Only native SW decoder and HW accelerator can parse dovi rpu. -- cgit v1.2.3 From df00909b85db0c312879df404049dd98508781d2 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 8 Sep 2024 01:21:14 +0800 Subject: Backport #12562 and #12521 (#12602) Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com> Co-authored-by: Nyanmisaka --- .../MediaEncoding/EncodingHelper.cs | 21 ++++++++++++++++++++- MediaBrowser.Model/Dlna/StreamBuilder.cs | 12 +++++------- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 52faf22f2..d6ad7e2b3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1914,7 +1914,26 @@ namespace MediaBrowser.Controller.MediaEncoding } var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault() ?? string.Empty; - profile = WhiteSpaceRegex().Replace(profile, string.Empty); + profile = WhiteSpaceRegex().Replace(profile, string.Empty).ToLowerInvariant(); + + var videoProfiles = Array.Empty(); + if (string.Equals("h264", targetVideoCodec, StringComparison.OrdinalIgnoreCase)) + { + videoProfiles = _videoProfilesH264; + } + else if (string.Equals("hevc", targetVideoCodec, StringComparison.OrdinalIgnoreCase)) + { + videoProfiles = _videoProfilesH265; + } + else if (string.Equals("av1", targetVideoCodec, StringComparison.OrdinalIgnoreCase)) + { + videoProfiles = _videoProfilesAv1; + } + + if (!videoProfiles.Contains(profile, StringComparison.OrdinalIgnoreCase)) + { + profile = string.Empty; + } // We only transcode to HEVC 8-bit for now, force Main Profile. if (profile.Contains("main10", StringComparison.OrdinalIgnoreCase) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index cd18fea12..ad00149e0 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -925,7 +925,7 @@ namespace MediaBrowser.Model.Dlna { audioStream = directAudioStream; playlistItem.AudioStreamIndex = audioStream.Index; - playlistItem.AudioCodecs = new[] { audioStream.Codec }; + playlistItem.AudioCodecs = audioCodecs = new[] { audioStream.Codec }; // Copy matching audio codec options playlistItem.AudioSampleRate = audioStream.SampleRate; @@ -966,15 +966,14 @@ namespace MediaBrowser.Model.Dlna var appliedVideoConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.Video && - i.ContainsAnyCodec(videoStream?.Codec, container, useSubContainer) && + i.ContainsAnyCodec(videoCodecs, container, useSubContainer) && i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))) // Reverse codec profiles for backward compatibility - first codec profile has higher priority .Reverse(); foreach (var i in appliedVideoConditions) { - var transcodingVideoCodecs = ContainerProfile.SplitValue(videoCodec); - foreach (var transcodingVideoCodec in transcodingVideoCodecs) + foreach (var transcodingVideoCodec in videoCodecs) { if (i.ContainsAnyCodec(transcodingVideoCodec, container, useSubContainer)) { @@ -999,15 +998,14 @@ namespace MediaBrowser.Model.Dlna var appliedAudioConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.VideoAudio && - i.ContainsAnyCodec(audioStream?.Codec, container) && + i.ContainsAnyCodec(audioCodecs, container) && i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))) // Reverse codec profiles for backward compatibility - first codec profile has higher priority .Reverse(); foreach (var codecProfile in appliedAudioConditions) { - var transcodingAudioCodecs = ContainerProfile.SplitValue(audioCodec); - foreach (var transcodingAudioCodec in transcodingAudioCodecs) + foreach (var transcodingAudioCodec in audioCodecs) { if (codecProfile.ContainsAnyCodec(transcodingAudioCodec, container)) { -- cgit v1.2.3 From 0d85af019c6ebc855ab2d8e689abe72b995225b4 Mon Sep 17 00:00:00 2001 From: Tim Eisele Date: Mon, 9 Sep 2024 16:43:37 +0200 Subject: Use enums for encoding options (#12561) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +- Jellyfin.Api/Controllers/VideosController.cs | 2 +- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../CreateNetworkConfiguration.cs | 1 - .../PreStartupRoutines/MigrateEncodingOptions.cs | 245 +++++++++ .../MigrateMusicBrainzTimeout.cs | 10 +- .../MigrateNetworkConfiguration.cs | 85 +-- .../MediaEncoding/EncodingHelper.cs | 594 +++++++++------------ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 17 +- .../Transcoding/TranscodeManager.cs | 7 +- .../Configuration/EncodingOptions.cs | 26 +- MediaBrowser.Model/Entities/DeinterlaceMethod.cs | 19 + MediaBrowser.Model/Entities/EncoderPreset.cs | 64 +++ .../Entities/HardwareAccelerationType.cs | 49 ++ .../Entities/TonemappingAlgorithm.cs | 49 ++ MediaBrowser.Model/Entities/TonemappingMode.cs | 34 ++ MediaBrowser.Model/Entities/TonemappingRange.cs | 24 + MediaBrowser.Model/Session/HardwareEncodingType.cs | 43 -- MediaBrowser.Model/Session/TranscodingInfo.cs | 78 ++- .../Playlist/DynamicHlsPlaylistGenerator.cs | 4 +- 20 files changed, 883 insertions(+), 475 deletions(-) create mode 100644 Jellyfin.Server/Migrations/PreStartupRoutines/MigrateEncodingOptions.cs create mode 100644 MediaBrowser.Model/Entities/DeinterlaceMethod.cs create mode 100644 MediaBrowser.Model/Entities/EncoderPreset.cs create mode 100644 MediaBrowser.Model/Entities/HardwareAccelerationType.cs create mode 100644 MediaBrowser.Model/Entities/TonemappingAlgorithm.cs create mode 100644 MediaBrowser.Model/Entities/TonemappingMode.cs create mode 100644 MediaBrowser.Model/Entities/TonemappingRange.cs delete mode 100644 MediaBrowser.Model/Session/HardwareEncodingType.cs (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index b9ef189e9..db1d86698 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -40,8 +40,8 @@ namespace Jellyfin.Api.Controllers; [Authorize] public class DynamicHlsController : BaseJellyfinApiController { - private const string DefaultVodEncoderPreset = "veryfast"; - private const string DefaultEventEncoderPreset = "superfast"; + private const EncoderPreset DefaultVodEncoderPreset = EncoderPreset.veryfast; + private const EncoderPreset DefaultEventEncoderPreset = EncoderPreset.superfast; private const TranscodingJobType TranscodingJobType = MediaBrowser.Controller.MediaEncoding.TranscodingJobType.Hls; private readonly Version _minFFmpegFlacInMp4 = new Version(6, 0); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index effe7b021..8348fd937 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -482,7 +482,7 @@ public class VideosController : BaseJellyfinApiController // Need to start ffmpeg (because media can't be returned directly) var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, "superfast"); + var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, EncoderPreset.superfast); return await FileStreamResponseHelpers.GetTranscodedFile( state, isHeadRequest, diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 8682f28e0..9d4441ac3 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -23,7 +23,8 @@ namespace Jellyfin.Server.Migrations { typeof(PreStartupRoutines.CreateNetworkConfiguration), typeof(PreStartupRoutines.MigrateMusicBrainzTimeout), - typeof(PreStartupRoutines.MigrateNetworkConfiguration) + typeof(PreStartupRoutines.MigrateNetworkConfiguration), + typeof(PreStartupRoutines.MigrateEncodingOptions) }; /// diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs index 139a6ec64..8462d0a8c 100644 --- a/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/CreateNetworkConfiguration.cs @@ -132,5 +132,4 @@ public class CreateNetworkConfiguration : IMigrationRoutine public string[] KnownProxies { get; set; } = Array.Empty(); } -#pragma warning restore } diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateEncodingOptions.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateEncodingOptions.cs new file mode 100644 index 000000000..61f5620dc --- /dev/null +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateEncodingOptions.cs @@ -0,0 +1,245 @@ +using System; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using Emby.Server.Implementations; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.PreStartupRoutines; + +/// +public class MigrateEncodingOptions : IMigrationRoutine +{ + private readonly ServerApplicationPaths _applicationPaths; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// An instance of . + /// An instance of the interface. + public MigrateEncodingOptions(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory) + { + _applicationPaths = applicationPaths; + _logger = loggerFactory.CreateLogger(); + } + + /// + public Guid Id => Guid.Parse("A8E61960-7726-4450-8F3D-82C12DAABBCB"); + + /// + public string Name => nameof(MigrateEncodingOptions); + + /// + public bool PerformOnNewInstall => false; + + /// + public void Perform() + { + string path = Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "encoding.xml"); + var oldSerializer = new XmlSerializer(typeof(OldEncodingOptions), new XmlRootAttribute("EncodingOptions")); + OldEncodingOptions? oldConfig = null; + + try + { + using var xmlReader = XmlReader.Create(path); + oldConfig = (OldEncodingOptions?)oldSerializer.Deserialize(xmlReader); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Migrate EncodingOptions deserialize Invalid Operation error"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Migrate EncodingOptions deserialize error"); + } + + if (oldConfig is null) + { + return; + } + + var hardwareAccelerationType = HardwareAccelerationType.none; + if (Enum.TryParse(oldConfig.HardwareAccelerationType, true, out var parsedHardwareAccelerationType)) + { + hardwareAccelerationType = parsedHardwareAccelerationType; + } + + var tonemappingAlgorithm = TonemappingAlgorithm.none; + if (Enum.TryParse(oldConfig.TonemappingAlgorithm, true, out var parsedTonemappingAlgorithm)) + { + tonemappingAlgorithm = parsedTonemappingAlgorithm; + } + + var tonemappingMode = TonemappingMode.auto; + if (Enum.TryParse(oldConfig.TonemappingMode, true, out var parsedTonemappingMode)) + { + tonemappingMode = parsedTonemappingMode; + } + + var tonemappingRange = TonemappingRange.auto; + if (Enum.TryParse(oldConfig.TonemappingRange, true, out var parsedTonemappingRange)) + { + tonemappingRange = parsedTonemappingRange; + } + + var encoderPreset = EncoderPreset.superfast; + if (Enum.TryParse(oldConfig.TonemappingRange, true, out var parsedEncoderPreset)) + { + encoderPreset = parsedEncoderPreset; + } + + var deinterlaceMethod = DeinterlaceMethod.yadif; + if (Enum.TryParse(oldConfig.TonemappingRange, true, out var parsedDeinterlaceMethod)) + { + deinterlaceMethod = parsedDeinterlaceMethod; + } + + var encodingOptions = new EncodingOptions() + { + EncodingThreadCount = oldConfig.EncodingThreadCount, + TranscodingTempPath = oldConfig.TranscodingTempPath, + FallbackFontPath = oldConfig.FallbackFontPath, + EnableFallbackFont = oldConfig.EnableFallbackFont, + EnableAudioVbr = oldConfig.EnableAudioVbr, + DownMixAudioBoost = oldConfig.DownMixAudioBoost, + DownMixStereoAlgorithm = oldConfig.DownMixStereoAlgorithm, + MaxMuxingQueueSize = oldConfig.MaxMuxingQueueSize, + EnableThrottling = oldConfig.EnableThrottling, + ThrottleDelaySeconds = oldConfig.ThrottleDelaySeconds, + EnableSegmentDeletion = oldConfig.EnableSegmentDeletion, + SegmentKeepSeconds = oldConfig.SegmentKeepSeconds, + HardwareAccelerationType = hardwareAccelerationType, + EncoderAppPath = oldConfig.EncoderAppPath, + EncoderAppPathDisplay = oldConfig.EncoderAppPathDisplay, + VaapiDevice = oldConfig.VaapiDevice, + EnableTonemapping = oldConfig.EnableTonemapping, + EnableVppTonemapping = oldConfig.EnableVppTonemapping, + EnableVideoToolboxTonemapping = oldConfig.EnableVideoToolboxTonemapping, + TonemappingAlgorithm = tonemappingAlgorithm, + TonemappingMode = tonemappingMode, + TonemappingRange = tonemappingRange, + TonemappingDesat = oldConfig.TonemappingDesat, + TonemappingPeak = oldConfig.TonemappingPeak, + TonemappingParam = oldConfig.TonemappingParam, + VppTonemappingBrightness = oldConfig.VppTonemappingBrightness, + VppTonemappingContrast = oldConfig.VppTonemappingContrast, + H264Crf = oldConfig.H264Crf, + H265Crf = oldConfig.H265Crf, + EncoderPreset = encoderPreset, + DeinterlaceDoubleRate = oldConfig.DeinterlaceDoubleRate, + DeinterlaceMethod = deinterlaceMethod, + EnableDecodingColorDepth10Hevc = oldConfig.EnableDecodingColorDepth10Hevc, + EnableDecodingColorDepth10Vp9 = oldConfig.EnableDecodingColorDepth10Vp9, + EnableEnhancedNvdecDecoder = oldConfig.EnableEnhancedNvdecDecoder, + PreferSystemNativeHwDecoder = oldConfig.PreferSystemNativeHwDecoder, + EnableIntelLowPowerH264HwEncoder = oldConfig.EnableIntelLowPowerH264HwEncoder, + EnableIntelLowPowerHevcHwEncoder = oldConfig.EnableIntelLowPowerHevcHwEncoder, + EnableHardwareEncoding = oldConfig.EnableHardwareEncoding, + AllowHevcEncoding = oldConfig.AllowHevcEncoding, + AllowAv1Encoding = oldConfig.AllowAv1Encoding, + EnableSubtitleExtraction = oldConfig.EnableSubtitleExtraction, + HardwareDecodingCodecs = oldConfig.HardwareDecodingCodecs, + AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = oldConfig.AllowOnDemandMetadataBasedKeyframeExtractionForExtensions + }; + + var newSerializer = new XmlSerializer(typeof(EncodingOptions)); + var xmlWriterSettings = new XmlWriterSettings { Indent = true }; + using var xmlWriter = XmlWriter.Create(path, xmlWriterSettings); + newSerializer.Serialize(xmlWriter, encodingOptions); + } + +#pragma warning disable + public sealed class OldEncodingOptions + { + public int EncodingThreadCount { get; set; } + + public string TranscodingTempPath { get; set; } + + public string FallbackFontPath { get; set; } + + public bool EnableFallbackFont { get; set; } + + public bool EnableAudioVbr { get; set; } + + public double DownMixAudioBoost { get; set; } + + public DownMixStereoAlgorithms DownMixStereoAlgorithm { get; set; } + + public int MaxMuxingQueueSize { get; set; } + + public bool EnableThrottling { get; set; } + + public int ThrottleDelaySeconds { get; set; } + + public bool EnableSegmentDeletion { get; set; } + + public int SegmentKeepSeconds { get; set; } + + public string HardwareAccelerationType { get; set; } + + public string EncoderAppPath { get; set; } + + public string EncoderAppPathDisplay { get; set; } + + public string VaapiDevice { get; set; } + + public bool EnableTonemapping { get; set; } + + public bool EnableVppTonemapping { get; set; } + + public bool EnableVideoToolboxTonemapping { get; set; } + + public string TonemappingAlgorithm { get; set; } + + public string TonemappingMode { get; set; } + + public string TonemappingRange { get; set; } + + public double TonemappingDesat { get; set; } + + public double TonemappingPeak { get; set; } + + public double TonemappingParam { get; set; } + + public double VppTonemappingBrightness { get; set; } + + public double VppTonemappingContrast { get; set; } + + public int H264Crf { get; set; } + + public int H265Crf { get; set; } + + public string EncoderPreset { get; set; } + + public bool DeinterlaceDoubleRate { get; set; } + + public string DeinterlaceMethod { get; set; } + + public bool EnableDecodingColorDepth10Hevc { get; set; } + + public bool EnableDecodingColorDepth10Vp9 { get; set; } + + public bool EnableEnhancedNvdecDecoder { get; set; } + + public bool PreferSystemNativeHwDecoder { get; set; } + + public bool EnableIntelLowPowerH264HwEncoder { get; set; } + + public bool EnableIntelLowPowerHevcHwEncoder { get; set; } + + public bool EnableHardwareEncoding { get; set; } + + public bool AllowHevcEncoding { get; set; } + + public bool AllowAv1Encoding { get; set; } + + public bool EnableSubtitleExtraction { get; set; } + + public string[] HardwareDecodingCodecs { get; set; } + + public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; } + } +} diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs index 0544fe561..580282a5f 100644 --- a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs @@ -48,9 +48,11 @@ public class MigrateMusicBrainzTimeout : IMigrationRoutine if (oldPluginConfiguration is not null) { - var newPluginConfiguration = new PluginConfiguration(); - newPluginConfiguration.Server = oldPluginConfiguration.Server; - newPluginConfiguration.ReplaceArtistName = oldPluginConfiguration.ReplaceArtistName; + var newPluginConfiguration = new PluginConfiguration + { + Server = oldPluginConfiguration.Server, + ReplaceArtistName = oldPluginConfiguration.ReplaceArtistName + }; var newRateLimit = oldPluginConfiguration.RateLimit / 1000.0; newPluginConfiguration.RateLimit = newRateLimit < 1.0 ? 1.0 : newRateLimit; WriteNew(path, newPluginConfiguration); @@ -93,6 +95,4 @@ public class MigrateMusicBrainzTimeout : IMigrationRoutine public bool ReplaceArtistName { get; set; } } -#pragma warning restore - } diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs index d92c00991..49960f430 100644 --- a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs +++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateNetworkConfiguration.cs @@ -55,49 +55,53 @@ public class MigrateNetworkConfiguration : IMigrationRoutine _logger.LogError(ex, "Migrate NetworkConfiguration deserialize error"); } - if (oldNetworkConfiguration is not null) + if (oldNetworkConfiguration is null) { - // Migrate network config values to new config schema - var networkConfiguration = new NetworkConfiguration(); - networkConfiguration.AutoDiscovery = oldNetworkConfiguration.AutoDiscovery; - networkConfiguration.BaseUrl = oldNetworkConfiguration.BaseUrl; - networkConfiguration.CertificatePassword = oldNetworkConfiguration.CertificatePassword; - networkConfiguration.CertificatePath = oldNetworkConfiguration.CertificatePath; - networkConfiguration.EnableHttps = oldNetworkConfiguration.EnableHttps; - networkConfiguration.EnableIPv4 = oldNetworkConfiguration.EnableIPV4; - networkConfiguration.EnableIPv6 = oldNetworkConfiguration.EnableIPV6; - networkConfiguration.EnablePublishedServerUriByRequest = oldNetworkConfiguration.EnablePublishedServerUriByRequest; - networkConfiguration.EnableRemoteAccess = oldNetworkConfiguration.EnableRemoteAccess; - networkConfiguration.EnableUPnP = oldNetworkConfiguration.EnableUPnP; - networkConfiguration.IgnoreVirtualInterfaces = oldNetworkConfiguration.IgnoreVirtualInterfaces; - networkConfiguration.InternalHttpPort = oldNetworkConfiguration.HttpServerPortNumber; - networkConfiguration.InternalHttpsPort = oldNetworkConfiguration.HttpsPortNumber; - networkConfiguration.IsRemoteIPFilterBlacklist = oldNetworkConfiguration.IsRemoteIPFilterBlacklist; - networkConfiguration.KnownProxies = oldNetworkConfiguration.KnownProxies; - networkConfiguration.LocalNetworkAddresses = oldNetworkConfiguration.LocalNetworkAddresses; - networkConfiguration.LocalNetworkSubnets = oldNetworkConfiguration.LocalNetworkSubnets; - networkConfiguration.PublicHttpPort = oldNetworkConfiguration.PublicPort; - networkConfiguration.PublicHttpsPort = oldNetworkConfiguration.PublicHttpsPort; - networkConfiguration.PublishedServerUriBySubnet = oldNetworkConfiguration.PublishedServerUriBySubnet; - networkConfiguration.RemoteIPFilter = oldNetworkConfiguration.RemoteIPFilter; - networkConfiguration.RequireHttps = oldNetworkConfiguration.RequireHttps; - - // Migrate old virtual interface name schema - var oldVirtualInterfaceNames = oldNetworkConfiguration.VirtualInterfaceNames; - if (oldVirtualInterfaceNames.Equals("vEthernet*", StringComparison.OrdinalIgnoreCase)) - { - networkConfiguration.VirtualInterfaceNames = new string[] { "veth" }; - } - else - { - networkConfiguration.VirtualInterfaceNames = oldVirtualInterfaceNames.Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase).Split(','); - } + return; + } - var networkConfigSerializer = new XmlSerializer(typeof(NetworkConfiguration)); - var xmlWriterSettings = new XmlWriterSettings { Indent = true }; - using var xmlWriter = XmlWriter.Create(path, xmlWriterSettings); - networkConfigSerializer.Serialize(xmlWriter, networkConfiguration); + // Migrate network config values to new config schema + var networkConfiguration = new NetworkConfiguration + { + AutoDiscovery = oldNetworkConfiguration.AutoDiscovery, + BaseUrl = oldNetworkConfiguration.BaseUrl, + CertificatePassword = oldNetworkConfiguration.CertificatePassword, + CertificatePath = oldNetworkConfiguration.CertificatePath, + EnableHttps = oldNetworkConfiguration.EnableHttps, + EnableIPv4 = oldNetworkConfiguration.EnableIPV4, + EnableIPv6 = oldNetworkConfiguration.EnableIPV6, + EnablePublishedServerUriByRequest = oldNetworkConfiguration.EnablePublishedServerUriByRequest, + EnableRemoteAccess = oldNetworkConfiguration.EnableRemoteAccess, + EnableUPnP = oldNetworkConfiguration.EnableUPnP, + IgnoreVirtualInterfaces = oldNetworkConfiguration.IgnoreVirtualInterfaces, + InternalHttpPort = oldNetworkConfiguration.HttpServerPortNumber, + InternalHttpsPort = oldNetworkConfiguration.HttpsPortNumber, + IsRemoteIPFilterBlacklist = oldNetworkConfiguration.IsRemoteIPFilterBlacklist, + KnownProxies = oldNetworkConfiguration.KnownProxies, + LocalNetworkAddresses = oldNetworkConfiguration.LocalNetworkAddresses, + LocalNetworkSubnets = oldNetworkConfiguration.LocalNetworkSubnets, + PublicHttpPort = oldNetworkConfiguration.PublicPort, + PublicHttpsPort = oldNetworkConfiguration.PublicHttpsPort, + PublishedServerUriBySubnet = oldNetworkConfiguration.PublishedServerUriBySubnet, + RemoteIPFilter = oldNetworkConfiguration.RemoteIPFilter, + RequireHttps = oldNetworkConfiguration.RequireHttps + }; + + // Migrate old virtual interface name schema + var oldVirtualInterfaceNames = oldNetworkConfiguration.VirtualInterfaceNames; + if (oldVirtualInterfaceNames.Equals("vEthernet*", StringComparison.OrdinalIgnoreCase)) + { + networkConfiguration.VirtualInterfaceNames = new string[] { "veth" }; } + else + { + networkConfiguration.VirtualInterfaceNames = oldVirtualInterfaceNames.Replace("*", string.Empty, StringComparison.OrdinalIgnoreCase).Split(','); + } + + var networkConfigSerializer = new XmlSerializer(typeof(NetworkConfiguration)); + var xmlWriterSettings = new XmlWriterSettings { Indent = true }; + using var xmlWriter = XmlWriter.Create(path, xmlWriterSettings); + networkConfigSerializer.Serialize(xmlWriter, networkConfiguration); } #pragma warning disable @@ -204,5 +208,4 @@ public class MigrateNetworkConfiguration : IMigrationRoutine public bool EnablePublishedServerUriByRequest { get; set; } = false; } -#pragma warning restore } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index d6ad7e2b3..fdc56652a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.MediaEncoding /// public const string ValidationRegex = @"^[a-zA-Z0-9\-\._,|]{0,40}$"; + private const string _defaultMjpegEncoder = "mjpeg"; + private const string QsvAlias = "qs"; private const string VaapiAlias = "va"; private const string D3d11vaAlias = "dx11"; @@ -72,8 +74,8 @@ namespace MediaBrowser.Controller.MediaEncoding private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); - private static readonly string[] _videoProfilesH264 = new[] - { + private static readonly string[] _videoProfilesH264 = + [ "ConstrainedBaseline", "Baseline", "Extended", @@ -82,20 +84,20 @@ namespace MediaBrowser.Controller.MediaEncoding "ProgressiveHigh", "ConstrainedHigh", "High10" - }; + ]; - private static readonly string[] _videoProfilesH265 = new[] - { + private static readonly string[] _videoProfilesH265 = + [ "Main", "Main10" - }; + ]; - private static readonly string[] _videoProfilesAv1 = new[] - { + private static readonly string[] _videoProfilesAv1 = + [ "Main", "High", "Professional", - }; + ]; private static readonly HashSet _mp4ContainerNames = new(StringComparer.OrdinalIgnoreCase) { @@ -107,8 +109,8 @@ namespace MediaBrowser.Controller.MediaEncoding "m4v", }; - private static readonly string[] _legacyTonemapModes = new[] { "max", "rgb" }; - private static readonly string[] _advancedTonemapModes = new[] { "lum", "itp" }; + private static readonly TonemappingMode[] _legacyTonemapModes = [TonemappingMode.max, TonemappingMode.rgb]; + private static readonly TonemappingMode[] _advancedTonemapModes = [TonemappingMode.lum, TonemappingMode.itp]; // Set max transcoding channels for encoders that can't handle more than a set amount of channels // AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels @@ -123,23 +125,22 @@ namespace MediaBrowser.Controller.MediaEncoding { "truehd", 6 }, }; - private static readonly string _defaultMjpegEncoder = "mjpeg"; - private static readonly Dictionary _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary _mjpegCodecMap = new() { - { "vaapi", _defaultMjpegEncoder + "_vaapi" }, - { "qsv", _defaultMjpegEncoder + "_qsv" }, - { "videotoolbox", _defaultMjpegEncoder + "_videotoolbox" } + { HardwareAccelerationType.vaapi, _defaultMjpegEncoder + "_vaapi" }, + { HardwareAccelerationType.qsv, _defaultMjpegEncoder + "_qsv" }, + { HardwareAccelerationType.videotoolbox, _defaultMjpegEncoder + "_videotoolbox" } }; - public static readonly string[] LosslessAudioCodecs = new string[] - { + public static readonly string[] LosslessAudioCodecs = + [ "alac", "ape", "flac", "mlp", "truehd", "wavpack" - }; + ]; public EncodingHelper( IApplicationPaths appPaths, @@ -176,18 +177,18 @@ namespace MediaBrowser.Controller.MediaEncoding { var hwType = encodingOptions.HardwareAccelerationType; - var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + var codecMap = new Dictionary() { - { "amf", hwEncoder + "_amf" }, - { "nvenc", hwEncoder + "_nvenc" }, - { "qsv", hwEncoder + "_qsv" }, - { "vaapi", hwEncoder + "_vaapi" }, - { "videotoolbox", hwEncoder + "_videotoolbox" }, - { "v4l2m2m", hwEncoder + "_v4l2m2m" }, - { "rkmpp", hwEncoder + "_rkmpp" }, + { HardwareAccelerationType.amf, hwEncoder + "_amf" }, + { HardwareAccelerationType.nvenc, hwEncoder + "_nvenc" }, + { HardwareAccelerationType.qsv, hwEncoder + "_qsv" }, + { HardwareAccelerationType.vaapi, hwEncoder + "_vaapi" }, + { HardwareAccelerationType.videotoolbox, hwEncoder + "_videotoolbox" }, + { HardwareAccelerationType.v4l2m2m, hwEncoder + "_v4l2m2m" }, + { HardwareAccelerationType.rkmpp, hwEncoder + "_rkmpp" }, }; - if (!string.IsNullOrEmpty(hwType) + if (hwType != HardwareAccelerationType.none && encodingOptions.EnableHardwareEncoding && codecMap.TryGetValue(hwType, out var preferredEncoder) && _mediaEncoder.SupportsEncoder(preferredEncoder)) @@ -205,7 +206,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var hwType = encodingOptions.HardwareAccelerationType; - if (!string.IsNullOrEmpty(hwType) + if (hwType != HardwareAccelerationType.none && encodingOptions.EnableHardwareEncoding && _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder) && _mediaEncoder.SupportsEncoder(preferredEncoder)) @@ -360,7 +361,7 @@ namespace MediaBrowser.Controller.MediaEncoding // prefer 'tonemap_vaapi' over 'vpp_qsv' on Linux for supporting Gen9/KBLx. // 'vpp_qsv' requires VPL, which is only supported on Gen12/TGLx and newer. if (OperatingSystem.IsWindows() - && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) + && options.HardwareAccelerationType == HardwareAccelerationType.qsv && _mediaEncoder.EncoderVersion < _minFFmpegQsvVppTonemapOption) { return false; @@ -970,7 +971,7 @@ namespace MediaBrowser.Controller.MediaEncoding var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty; var isHwTonemapAvailable = IsHwTonemapAvailable(state, options); - if (string.Equals(optHwaccelType, "vaapi", StringComparison.OrdinalIgnoreCase)) + if (optHwaccelType == HardwareAccelerationType.vaapi) { if (!isLinux || !_mediaEncoder.SupportsHwaccel("vaapi")) { @@ -1044,7 +1045,7 @@ namespace MediaBrowser.Controller.MediaEncoding args.Append(filterDevArgs); } - else if (string.Equals(optHwaccelType, "qsv", StringComparison.OrdinalIgnoreCase)) + else if (optHwaccelType == HardwareAccelerationType.qsv) { if ((!isLinux && !isWindows) || !_mediaEncoder.SupportsHwaccel("qsv")) { @@ -1079,7 +1080,7 @@ namespace MediaBrowser.Controller.MediaEncoding args.Append(filterDevArgs); } - else if (string.Equals(optHwaccelType, "nvenc", StringComparison.OrdinalIgnoreCase)) + else if (optHwaccelType == HardwareAccelerationType.nvenc) { if ((!isLinux && !isWindows) || !IsCudaFullSupported()) { @@ -1098,7 +1099,7 @@ namespace MediaBrowser.Controller.MediaEncoding args.Append(GetCudaDeviceArgs(0, CudaAlias)) .Append(GetFilterHwDeviceArgs(CudaAlias)); } - else if (string.Equals(optHwaccelType, "amf", StringComparison.OrdinalIgnoreCase)) + else if (optHwaccelType == HardwareAccelerationType.amf) { if (!isWindows || !_mediaEncoder.SupportsHwaccel("d3d11va")) { @@ -1123,7 +1124,7 @@ namespace MediaBrowser.Controller.MediaEncoding args.Append(filterDevArgs); } - else if (string.Equals(optHwaccelType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + else if (optHwaccelType == HardwareAccelerationType.videotoolbox) { if (!isMacOS || !_mediaEncoder.SupportsHwaccel("videotoolbox")) { @@ -1140,7 +1141,7 @@ namespace MediaBrowser.Controller.MediaEncoding // videotoolbox hw filter does not require device selection args.Append(GetVideoToolboxDeviceArgs(VideotoolboxAlias)); } - else if (string.Equals(optHwaccelType, "rkmpp", StringComparison.OrdinalIgnoreCase)) + else if (optHwaccelType == HardwareAccelerationType.rkmpp) { if (!isLinux || !_mediaEncoder.SupportsHwaccel("rkmpp")) { @@ -1413,6 +1414,149 @@ namespace MediaBrowser.Controller.MediaEncoding return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); } + private string GetEncoderParam(EncoderPreset? preset, EncoderPreset defaultPreset, EncodingOptions encodingOptions, string videoEncoder, bool isLibX265) + { + var param = string.Empty; + var encoderPreset = preset ?? defaultPreset; + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265) + { + param += " -preset " + encoderPreset.ToString().ToLowerInvariant(); + + int encodeCrf = encodingOptions.H264Crf; + if (isLibX265) + { + encodeCrf = encodingOptions.H265Crf; + } + + if (encodeCrf >= 0 && encodeCrf <= 51) + { + param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture); + } + else + { + string defaultCrf = "23"; + if (isLibX265) + { + defaultCrf = "28"; + } + + 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 += encoderPreset switch + { + EncoderPreset.veryslow => " -preset 5", + EncoderPreset.slower => " -preset 6", + EncoderPreset.slow => " -preset 7", + EncoderPreset.medium => " -preset 8", + EncoderPreset.fast => " -preset 9", + EncoderPreset.faster => " -preset 10", + EncoderPreset.veryfast => " -preset 11", + EncoderPreset.superfast => " -preset 12", + EncoderPreset.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 += encoderPreset switch + { + EncoderPreset.veryslow => " -compression_level 1", + EncoderPreset.slower => " -compression_level 2", + EncoderPreset.slow => " -compression_level 3", + EncoderPreset.medium => " -compression_level 4", + EncoderPreset.fast => " -compression_level 5", + EncoderPreset.faster => " -compression_level 6", + EncoderPreset.veryfast => " -compression_level 7", + EncoderPreset.superfast => " -compression_level 7", + EncoderPreset.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, "av1_qsv", StringComparison.OrdinalIgnoreCase)) // av1 (av1_qsv) + { + EncoderPreset[] valid_presets = [EncoderPreset.veryslow, EncoderPreset.slower, EncoderPreset.slow, EncoderPreset.medium, EncoderPreset.fast, EncoderPreset.faster, EncoderPreset.veryfast]; + + if (valid_presets.Contains(encoderPreset)) + { + param += " -preset " + encodingOptions.EncoderPreset; + } + else + { + param += " -preset " + EncoderPreset.veryfast.ToString().ToLowerInvariant(); + } + } + 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, "av1_nvenc", StringComparison.OrdinalIgnoreCase) // av1 (av1_nvenc) + ) + { + param += encoderPreset switch + { + EncoderPreset.veryslow => " -preset p7", + EncoderPreset.slower => " -preset p6", + EncoderPreset.slow => " -preset p5", + EncoderPreset.medium => " -preset p4", + EncoderPreset.fast => " -preset p3", + EncoderPreset.faster => " -preset p2", + _ => " -preset p1" + }; + } + 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, "av1_amf", StringComparison.OrdinalIgnoreCase) // av1 (av1_amf) + ) + { + param += encoderPreset switch + { + EncoderPreset.veryslow => " -quality quality", + EncoderPreset.slower => " -quality quality", + EncoderPreset.slow => " -quality quality", + EncoderPreset.medium => " -quality balanced", + _ => " -quality speed" + }; + + 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 += " -gops_per_idr 1"; + } + } + else if (string.Equals(videoEncoder, "h264_videotoolbox", StringComparison.OrdinalIgnoreCase) // h264 (h264_videotoolbox) + || string.Equals(videoEncoder, "hevc_videotoolbox", StringComparison.OrdinalIgnoreCase) // hevc (hevc_videotoolbox) + ) + { + param += encoderPreset switch + { + EncoderPreset.veryslow => " -prio_speed 0", + EncoderPreset.slower => " -prio_speed 0", + EncoderPreset.slow => " -prio_speed 0", + EncoderPreset.medium => " -prio_speed 0", + _ => " -prio_speed 1" + }; + } + + return param; + } + public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level) { if (double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel)) @@ -1625,7 +1769,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// Encoding options. /// Default present to use for encoding. /// Video bitrate. - public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset) + public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, EncoderPreset defaultPreset) { var param = string.Empty; @@ -1640,7 +1784,9 @@ namespace MediaBrowser.Controller.MediaEncoding // https://github.com/intel/media-driver/issues/1456 var enableWaFori915Hang = false; - if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + var hardwareAccelerationType = encodingOptions.HardwareAccelerationType; + + if (hardwareAccelerationType == HardwareAccelerationType.vaapi) { var isIntelVaapiDriver = _mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965; @@ -1653,7 +1799,7 @@ namespace MediaBrowser.Controller.MediaEncoding intelLowPowerHwEncoding = encodingOptions.EnableIntelLowPowerHevcHwEncoder && isIntelVaapiDriver; } } - else if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + else if (hardwareAccelerationType == HardwareAccelerationType.qsv) { if (OperatingSystem.IsLinux()) { @@ -1700,204 +1846,10 @@ namespace MediaBrowser.Controller.MediaEncoding param += " -async_depth 1"; } - var isVc1 = string.Equals(state.VideoStream?.Codec, "vc1", StringComparison.OrdinalIgnoreCase); var isLibX265 = string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase); + var encodingPreset = encodingOptions.EncoderPreset; - if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265) - { - if (!string.IsNullOrEmpty(encodingOptions.EncoderPreset)) - { - param += " -preset " + encodingOptions.EncoderPreset; - } - else - { - param += " -preset " + defaultPreset; - } - - int encodeCrf = encodingOptions.H264Crf; - if (isLibX265) - { - encodeCrf = encodingOptions.H265Crf; - } - - if (encodeCrf >= 0 && encodeCrf <= 51) - { - param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture); - } - else - { - string defaultCrf = "23"; - if (isLibX265) - { - defaultCrf = "28"; - } - - 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, "av1_qsv", StringComparison.OrdinalIgnoreCase)) // av1 (av1_qsv) - { - string[] valid_presets = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" }; - - if (valid_presets.Contains(encodingOptions.EncoderPreset, StringComparison.OrdinalIgnoreCase)) - { - param += " -preset " + encodingOptions.EncoderPreset; - } - else - { - param += " -preset veryfast"; - } - } - 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, "av1_nvenc", StringComparison.OrdinalIgnoreCase)) // av1 (av1_nvenc) - { - switch (encodingOptions.EncoderPreset) - { - case "veryslow": - param += " -preset p7"; - break; - - case "slower": - param += " -preset p6"; - break; - - case "slow": - param += " -preset p5"; - break; - - case "medium": - param += " -preset p4"; - break; - - case "fast": - param += " -preset p3"; - break; - - case "faster": - param += " -preset p2"; - break; - - case "veryfast": - case "superfast": - case "ultrafast": - param += " -preset p1"; - break; - - default: - param += " -preset p1"; - break; - } - } - 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, "av1_amf", StringComparison.OrdinalIgnoreCase)) // av1 (av1_amf) - { - switch (encodingOptions.EncoderPreset) - { - case "veryslow": - case "slower": - case "slow": - param += " -quality quality"; - break; - - case "medium": - param += " -quality balanced"; - break; - - case "fast": - case "faster": - case "veryfast": - case "superfast": - case "ultrafast": - param += " -quality speed"; - break; - - default: - param += " -quality speed"; - 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 += " -gops_per_idr 1"; - } - } - else if (string.Equals(videoEncoder, "h264_videotoolbox", StringComparison.OrdinalIgnoreCase) // h264 (h264_videotoolbox) - || string.Equals(videoEncoder, "hevc_videotoolbox", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_videotoolbox) - { - switch (encodingOptions.EncoderPreset) - { - case "veryslow": - case "slower": - case "slow": - case "medium": - param += " -prio_speed 0"; - break; - - case "fast": - case "faster": - case "veryfast": - case "superfast": - case "ultrafast": - param += " -prio_speed 1"; - break; - - default: - param += " -prio_speed 1"; - break; - } - } - + param += GetEncoderParam(encodingPreset, defaultPreset, encodingOptions, videoEncoder, isLibX265); param += GetVideoBitrateParam(state, videoEncoder); var framerate = GetFramerateParam(state); @@ -3256,7 +3208,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, "{0}={1}:-1:0", - string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase) ? "bwdif" : "yadif", + options.DeinterlaceMethod.ToString().ToLowerInvariant(), doubleRateDeint ? "1" : "0"); } @@ -3265,8 +3217,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase)) { - var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase) - && _mediaEncoder.SupportsFilter("bwdif_cuda"); + var useBwdif = options.DeinterlaceMethod == DeinterlaceMethod.bwdif && _mediaEncoder.SupportsFilter("bwdif_cuda"); return string.Format( CultureInfo.InvariantCulture, @@ -3307,7 +3258,10 @@ namespace MediaBrowser.Controller.MediaEncoding } var args = string.Empty; - var algorithm = options.TonemappingAlgorithm; + var algorithm = options.TonemappingAlgorithm.ToString().ToLowerInvariant(); + var mode = options.TonemappingMode.ToString().ToLowerInvariant(); + var range = options.TonemappingRange; + var rangeString = range.ToString().ToLowerInvariant(); if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase)) { @@ -3342,10 +3296,10 @@ namespace MediaBrowser.Controller.MediaEncoding args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}"; var useLegacyTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode - && _legacyTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase); + && _legacyTonemapModes.Contains(options.TonemappingMode); var useAdvancedTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegAdvancedTonemapMode - && _advancedTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase); + && _advancedTonemapModes.Contains(options.TonemappingMode); if (useLegacyTonemapModes || useAdvancedTonemapModes) { @@ -3357,8 +3311,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += ":param={6}"; } - if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase) - || string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase)) + if (range == TonemappingRange.tv || range == TonemappingRange.pc) { args += ":range={7}"; } @@ -3372,9 +3325,9 @@ namespace MediaBrowser.Controller.MediaEncoding algorithm, options.TonemappingPeak, options.TonemappingDesat, - options.TonemappingMode, + mode, options.TonemappingParam, - options.TonemappingRange); + rangeString); } public string GetLibplaceboFilter( @@ -3409,24 +3362,24 @@ namespace MediaBrowser.Controller.MediaEncoding if (doTonemap) { var algorithm = options.TonemappingAlgorithm; + var algorithmString = "clip"; var mode = options.TonemappingMode; var range = options.TonemappingRange; - if (string.Equals(algorithm, "bt2390", StringComparison.OrdinalIgnoreCase)) + if (algorithm == TonemappingAlgorithm.bt2390) { - algorithm = "bt.2390"; + algorithmString = "bt.2390"; } - else if (string.Equals(algorithm, "none", StringComparison.OrdinalIgnoreCase)) + else if (algorithm != TonemappingAlgorithm.none) { - algorithm = "clip"; + algorithmString = algorithm.ToString().ToLowerInvariant(); } tonemapArg = ":tonemapping=" + algorithm + ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709"; - if (string.Equals(range, "tv", StringComparison.OrdinalIgnoreCase) - || string.Equals(range, "pc", StringComparison.OrdinalIgnoreCase)) + if (range == TonemappingRange.tv || range == TonemappingRange.pc) { - tonemapArg += ":range=" + range; + tonemapArg += ":range=" + range.ToString().ToLowerInvariant(); } } @@ -3530,8 +3483,8 @@ namespace MediaBrowser.Controller.MediaEncoding tonemapArgs += $":param={options.TonemappingParam}"; } - if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase) - || string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase)) + var range = options.TonemappingRange; + if (range == TonemappingRange.tv || range == TonemappingRange.pc) { tonemapArgs += $":range={options.TonemappingRange}"; } @@ -3575,7 +3528,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.nvenc) { return (null, null, null); } @@ -3777,7 +3730,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.amf) { return (null, null, null); } @@ -3993,7 +3946,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.qsv) { return (null, null, null); } @@ -4543,7 +4496,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.vaapi) { return (null, null, null); } @@ -5247,7 +5200,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox) { return (null, null, null); } @@ -5436,7 +5389,7 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string vidEncoder) { - if (!string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)) + if (options.HardwareAccelerationType != HardwareAccelerationType.rkmpp) { return (null, null, null); } @@ -5696,38 +5649,20 @@ namespace MediaBrowser.Controller.MediaEncoding List subFilters; List overlayFilters; - if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + (mainFilters, subFilters, overlayFilters) = options.HardwareAccelerationType switch { - (mainFilters, subFilters, overlayFilters) = GetVaapiVidFilterChain(state, options, outputVideoCodec); - } - else if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) - { - (mainFilters, subFilters, overlayFilters) = GetIntelVidFilterChain(state, options, outputVideoCodec); - } - else if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) - { - (mainFilters, subFilters, overlayFilters) = GetNvidiaVidFilterChain(state, options, outputVideoCodec); - } - else if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) - { - (mainFilters, subFilters, overlayFilters) = GetAmdVidFilterChain(state, options, outputVideoCodec); - } - else if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) - { - (mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec); - } - else if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)) - { - (mainFilters, subFilters, overlayFilters) = GetRkmppVidFilterChain(state, options, outputVideoCodec); - } - else - { - (mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec); - } + HardwareAccelerationType.vaapi => GetVaapiVidFilterChain(state, options, outputVideoCodec), + HardwareAccelerationType.amf => GetAmdVidFilterChain(state, options, outputVideoCodec), + HardwareAccelerationType.qsv => GetIntelVidFilterChain(state, options, outputVideoCodec), + HardwareAccelerationType.nvenc => GetNvidiaVidFilterChain(state, options, outputVideoCodec), + HardwareAccelerationType.videotoolbox => GetAppleVidFilterChain(state, options, outputVideoCodec), + HardwareAccelerationType.rkmpp => GetRkmppVidFilterChain(state, options, outputVideoCodec), + _ => GetSwVidFilterChain(state, options, outputVideoCodec), + }; - mainFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter)); - subFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter)); - overlayFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter)); + mainFilters?.RemoveAll(string.IsNullOrEmpty); + subFilters?.RemoveAll(string.IsNullOrEmpty); + overlayFilters?.RemoveAll(string.IsNullOrEmpty); var framerate = GetFramerateParam(state); if (framerate.HasValue) @@ -5907,7 +5842,9 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(options.HardwareAccelerationType)) + var hardwareAccelerationType = options.HardwareAccelerationType; + + if (!string.IsNullOrEmpty(videoStream.Codec) && hardwareAccelerationType != HardwareAccelerationType.none) { var bitDepth = GetVideoColorBitDepth(state); @@ -5919,10 +5856,10 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase))) { // RKMPP has H.264 Hi10P decoder - bool hasHardwareHi10P = string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase); + bool hasHardwareHi10P = hardwareAccelerationType == HardwareAccelerationType.rkmpp; // VideoToolbox on Apple Silicon has H.264 Hi10P mode enabled after macOS 14.6 - if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + if (hardwareAccelerationType == HardwareAccelerationType.videotoolbox) { var ver = Environment.OSVersion.Version; var arch = RuntimeInformation.OSArchitecture; @@ -5939,34 +5876,20 @@ namespace MediaBrowser.Controller.MediaEncoding } } - if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) - { - return GetQsvHwVidDecoder(state, options, videoStream, bitDepth); - } - - if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) + var decoder = hardwareAccelerationType switch { - return GetNvdecVidDecoder(state, options, videoStream, bitDepth); - } - - if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) - { - return GetAmfVidDecoder(state, options, videoStream, bitDepth); - } - - if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) - { - return GetVaapiVidDecoder(state, options, videoStream, bitDepth); - } - - if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) - { - return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth); - } + HardwareAccelerationType.vaapi => GetVaapiVidDecoder(state, options, videoStream, bitDepth), + HardwareAccelerationType.amf => GetAmfVidDecoder(state, options, videoStream, bitDepth), + HardwareAccelerationType.qsv => GetQsvHwVidDecoder(state, options, videoStream, bitDepth), + HardwareAccelerationType.nvenc => GetNvdecVidDecoder(state, options, videoStream, bitDepth), + HardwareAccelerationType.videotoolbox => GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth), + HardwareAccelerationType.rkmpp => GetRkmppVidDecoder(state, options, videoStream, bitDepth), + _ => string.Empty + }; - if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(decoder)) { - return GetRkmppVidDecoder(state, options, videoStream, bitDepth); + return decoder; } } @@ -5981,7 +5904,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Avoid a second attempt if no hardware acceleration is being used - options.HardwareDecodingCodecs = Array.FindAll(options.HardwareDecodingCodecs, val => !string.Equals(val, whichCodec, StringComparison.OrdinalIgnoreCase)); + options.HardwareDecodingCodecs = options.HardwareDecodingCodecs.Where(c => !string.Equals(c, whichCodec, StringComparison.OrdinalIgnoreCase)).ToArray(); // leave blank so ffmpeg will decide return null; @@ -6062,6 +5985,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox"); var isRkmppSupported = isLinux && IsRkmppFullSupported(); var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase); + var hardwareAccelerationType = options.HardwareAccelerationType; var ffmpegVersion = _mediaEncoder.EncoderVersion; @@ -6099,7 +6023,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Intel qsv/d3d11va/vaapi - if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + if (hardwareAccelerationType == HardwareAccelerationType.qsv) { if (options.PreferSystemNativeHwDecoder) { @@ -6125,7 +6049,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Nvidia cuda - if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) + if (hardwareAccelerationType == HardwareAccelerationType.nvenc) { if (isCudaSupported && isCodecAvailable) { @@ -6142,7 +6066,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Amd d3d11va - if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) + if (hardwareAccelerationType == HardwareAccelerationType.amf) { if (isD3d11Supported && isCodecAvailable) { @@ -6152,7 +6076,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Vaapi - if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + if (hardwareAccelerationType == HardwareAccelerationType.vaapi && isVaapiSupported && isCodecAvailable) { @@ -6161,7 +6085,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Apple videotoolbox - if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase) + if (hardwareAccelerationType == HardwareAccelerationType.videotoolbox && isVideotoolboxSupported && isCodecAvailable) { @@ -6169,7 +6093,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Rockchip rkmpp - if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase) + if (hardwareAccelerationType == HardwareAccelerationType.rkmpp && isRkmppSupported && isCodecAvailable) { @@ -6185,7 +6109,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isLinux = OperatingSystem.IsLinux(); if ((!isWindows && !isLinux) - || !string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.qsv) { return null; } @@ -6254,7 +6178,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetNvdecVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth) { if ((!OperatingSystem.IsWindows() && !OperatingSystem.IsLinux()) - || !string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.nvenc) { return null; } @@ -6319,7 +6243,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetAmfVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth) { if (!OperatingSystem.IsWindows() - || !string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.amf) { return null; } @@ -6375,7 +6299,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetVaapiVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth) { if (!OperatingSystem.IsLinux() - || !string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.vaapi) { return null; } @@ -6437,7 +6361,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetVideotoolboxVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth) { if (!OperatingSystem.IsMacOS() - || !string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox) { return null; } @@ -6485,7 +6409,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isLinux = OperatingSystem.IsLinux(); if (!isLinux - || !string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)) + || options.HardwareAccelerationType != HardwareAccelerationType.rkmpp) { return null; } @@ -6749,7 +6673,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.IsVideoRequest) { - if (!string.IsNullOrEmpty(state.InputContainer) && state.VideoType == VideoType.VideoFile && string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) + if (!string.IsNullOrEmpty(state.InputContainer) && state.VideoType == VideoType.VideoFile && encodingOptions.HardwareAccelerationType != HardwareAccelerationType.none) { var inputFormat = GetInputFormat(state.InputContainer); if (!string.IsNullOrEmpty(inputFormat)) @@ -6865,7 +6789,7 @@ namespace MediaBrowser.Controller.MediaEncoding state.SupportedAudioCodecs = supportedAudioCodecsList.ToArray(); - request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToAudioCodec(i)) + request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(_mediaEncoder.CanEncodeToAudioCodec) ?? state.SupportedAudioCodecs.FirstOrDefault(); } @@ -6991,7 +6915,7 @@ namespace MediaBrowser.Controller.MediaEncoding return " -codec:s:0 " + codec + " -disposition:s:0 default"; } - public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string defaultPreset) + public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, EncoderPreset defaultPreset) { // Get the output codec name var videoCodec = GetVideoEncoder(state, encodingOptions); @@ -7042,7 +6966,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultPreset) + public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, EncoderPreset defaultPreset) { var args = "-codec:v:0 " + videoCodec; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a6a443f3d..caa9cb499 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -224,7 +224,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (OperatingSystem.IsLinux() && SupportsHwaccel("vaapi") && !string.IsNullOrEmpty(options.VaapiDevice) - && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + && options.HardwareAccelerationType == HardwareAccelerationType.vaapi) { _isVaapiDeviceAmd = validator.CheckVaapiDeviceByDriverName("Mesa Gallium driver", options.VaapiDevice); _isVaapiDeviceInteliHD = validator.CheckVaapiDeviceByDriverName("Intel iHD driver", options.VaapiDevice); @@ -799,11 +799,12 @@ namespace MediaBrowser.MediaEncoding.Encoder if (allowHwAccel && enableKeyFrameOnlyExtraction) { - var supportsKeyFrameOnly = (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) - || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows()) - || (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.PreferSystemNativeHwDecoder) - || string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) - || string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase); + var hardwareAccelerationType = options.HardwareAccelerationType; + var supportsKeyFrameOnly = (hardwareAccelerationType == HardwareAccelerationType.nvenc && options.EnableEnhancedNvdecDecoder) + || (hardwareAccelerationType == HardwareAccelerationType.amf && OperatingSystem.IsWindows()) + || (hardwareAccelerationType == HardwareAccelerationType.qsv && options.PreferSystemNativeHwDecoder) + || hardwareAccelerationType == HardwareAccelerationType.vaapi + || hardwareAccelerationType == HardwareAccelerationType.videotoolbox; if (!supportsKeyFrameOnly) { // Disable hardware acceleration when the hardware decoder does not support keyframe only mode. @@ -817,7 +818,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!allowHwAccel) { options.EnableHardwareEncoding = false; - options.HardwareAccelerationType = string.Empty; + options.HardwareAccelerationType = HardwareAccelerationType.none; options.EnableTonemapping = false; } @@ -861,7 +862,7 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled } - if (options.HardwareAccelerationType.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase) && _isLowPriorityHwDecodeSupported) + if (options.HardwareAccelerationType == HardwareAccelerationType.videotoolbox && _isLowPriorityHwDecodeSupported) { // VideoToolbox supports low priority decoding, which is useful for trickplay inputArg = "-hwaccel_flags +low_priority " + inputArg; diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index 42f355b05..57557d55c 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -352,12 +352,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable { var audioCodec = state.ActualOutputAudioCodec; var videoCodec = state.ActualOutputVideoCodec; - var hardwareAccelerationTypeString = _serverConfigurationManager.GetEncodingOptions().HardwareAccelerationType; - HardwareEncodingType? hardwareAccelerationType = null; - if (Enum.TryParse(hardwareAccelerationTypeString, out var parsedHardwareAccelerationType)) - { - hardwareAccelerationType = parsedHardwareAccelerationType; - } + var hardwareAccelerationType = _serverConfigurationManager.GetEncodingOptions().HardwareAccelerationType; _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo { diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 4c5213d4e..d67a2479f 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1819 // XML serialization handles collections improperly, so we need to use arrays + #nullable disable using MediaBrowser.Model.Entities; @@ -30,9 +32,9 @@ public class EncodingOptions EnableTonemapping = false; EnableVppTonemapping = false; EnableVideoToolboxTonemapping = false; - TonemappingAlgorithm = "bt2390"; - TonemappingMode = "auto"; - TonemappingRange = "auto"; + TonemappingAlgorithm = TonemappingAlgorithm.bt2390; + TonemappingMode = TonemappingMode.auto; + TonemappingRange = TonemappingRange.auto; TonemappingDesat = 0; TonemappingPeak = 100; TonemappingParam = 0; @@ -41,7 +43,7 @@ public class EncodingOptions H264Crf = 23; H265Crf = 28; DeinterlaceDoubleRate = false; - DeinterlaceMethod = "yadif"; + DeinterlaceMethod = DeinterlaceMethod.yadif; EnableDecodingColorDepth10Hevc = true; EnableDecodingColorDepth10Vp9 = true; // Enhanced Nvdec or system native decoder is required for DoVi to SDR tone-mapping. @@ -53,8 +55,8 @@ public class EncodingOptions AllowHevcEncoding = false; AllowAv1Encoding = false; EnableSubtitleExtraction = true; - AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" }; - HardwareDecodingCodecs = new string[] { "h264", "vc1" }; + AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = ["mkv"]; + HardwareDecodingCodecs = ["h264", "vc1"]; } /// @@ -120,7 +122,7 @@ public class EncodingOptions /// /// Gets or sets the hardware acceleration type. /// - public string HardwareAccelerationType { get; set; } + public HardwareAccelerationType HardwareAccelerationType { get; set; } /// /// Gets or sets the FFmpeg path as set by the user via the UI. @@ -160,17 +162,17 @@ public class EncodingOptions /// /// Gets or sets the tone-mapping algorithm. /// - public string TonemappingAlgorithm { get; set; } + public TonemappingAlgorithm TonemappingAlgorithm { get; set; } /// /// Gets or sets the tone-mapping mode. /// - public string TonemappingMode { get; set; } + public TonemappingMode TonemappingMode { get; set; } /// /// Gets or sets the tone-mapping range. /// - public string TonemappingRange { get; set; } + public TonemappingRange TonemappingRange { get; set; } /// /// Gets or sets the tone-mapping desaturation. @@ -210,7 +212,7 @@ public class EncodingOptions /// /// Gets or sets the encoder preset. /// - public string EncoderPreset { get; set; } + public EncoderPreset? EncoderPreset { get; set; } /// /// Gets or sets a value indicating whether the framerate is doubled when deinterlacing. @@ -220,7 +222,7 @@ public class EncodingOptions /// /// Gets or sets the deinterlace method. /// - public string DeinterlaceMethod { get; set; } + public DeinterlaceMethod DeinterlaceMethod { get; set; } /// /// Gets or sets a value indicating whether 10bit HEVC decoding is enabled. diff --git a/MediaBrowser.Model/Entities/DeinterlaceMethod.cs b/MediaBrowser.Model/Entities/DeinterlaceMethod.cs new file mode 100644 index 000000000..d05aac433 --- /dev/null +++ b/MediaBrowser.Model/Entities/DeinterlaceMethod.cs @@ -0,0 +1,19 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing deinterlace methods. +/// +public enum DeinterlaceMethod +{ + /// + /// YADIF. + /// + yadif = 0, + + /// + /// BWDIF. + /// + bwdif = 1 +} diff --git a/MediaBrowser.Model/Entities/EncoderPreset.cs b/MediaBrowser.Model/Entities/EncoderPreset.cs new file mode 100644 index 000000000..74c071433 --- /dev/null +++ b/MediaBrowser.Model/Entities/EncoderPreset.cs @@ -0,0 +1,64 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing encoder presets. +/// +public enum EncoderPreset +{ + /// + /// Auto preset. + /// + auto = 0, + + /// + /// Placebo preset. + /// + placebo = 1, + + /// + /// Veryslow preset. + /// + veryslow = 2, + + /// + /// Slower preset. + /// + slower = 3, + + /// + /// Slow preset. + /// + slow = 4, + + /// + /// Medium preset. + /// + medium = 5, + + /// + /// Fast preset. + /// + fast = 6, + + /// + /// Faster preset. + /// + faster = 7, + + /// + /// Veryfast preset. + /// + veryfast = 8, + + /// + /// Superfast preset. + /// + superfast = 9, + + /// + /// Ultrafast preset. + /// + ultrafast = 10 +} diff --git a/MediaBrowser.Model/Entities/HardwareAccelerationType.cs b/MediaBrowser.Model/Entities/HardwareAccelerationType.cs new file mode 100644 index 000000000..198a2e00f --- /dev/null +++ b/MediaBrowser.Model/Entities/HardwareAccelerationType.cs @@ -0,0 +1,49 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing hardware acceleration types. +/// +public enum HardwareAccelerationType +{ + /// + /// Software accelleration. + /// + none = 0, + + /// + /// AMD AMF. + /// + amf = 1, + + /// + /// Intel Quick Sync Video. + /// + qsv = 2, + + /// + /// NVIDIA NVENC. + /// + nvenc = 3, + + /// + /// Video4Linux2 V4L2M2M. + /// + v4l2m2m = 4, + + /// + /// Video Acceleration API (VAAPI). + /// + vaapi = 5, + + /// + /// Video ToolBox. + /// + videotoolbox = 6, + + /// + /// Rockchip Media Process Platform (RKMPP). + /// + rkmpp = 7 +} diff --git a/MediaBrowser.Model/Entities/TonemappingAlgorithm.cs b/MediaBrowser.Model/Entities/TonemappingAlgorithm.cs new file mode 100644 index 000000000..488006e0b --- /dev/null +++ b/MediaBrowser.Model/Entities/TonemappingAlgorithm.cs @@ -0,0 +1,49 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing tonemapping algorithms. +/// +public enum TonemappingAlgorithm +{ + /// + /// None. + /// + none = 0, + + /// + /// Clip. + /// + clip = 1, + + /// + /// Linear. + /// + linear = 2, + + /// + /// Gamma. + /// + gamma = 3, + + /// + /// Reinhard. + /// + reinhard = 4, + + /// + /// Hable. + /// + hable = 5, + + /// + /// Mobius. + /// + mobius = 6, + + /// + /// BT2390. + /// + bt2390 = 7 +} diff --git a/MediaBrowser.Model/Entities/TonemappingMode.cs b/MediaBrowser.Model/Entities/TonemappingMode.cs new file mode 100644 index 000000000..e10a0b4ad --- /dev/null +++ b/MediaBrowser.Model/Entities/TonemappingMode.cs @@ -0,0 +1,34 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing tonemapping modes. +/// +public enum TonemappingMode +{ + /// + /// Auto. + /// + auto = 0, + + /// + /// Max. + /// + max = 1, + + /// + /// RGB. + /// + rgb = 2, + + /// + /// Lum. + /// + lum = 3, + + /// + /// ITP. + /// + itp = 4 +} diff --git a/MediaBrowser.Model/Entities/TonemappingRange.cs b/MediaBrowser.Model/Entities/TonemappingRange.cs new file mode 100644 index 000000000..b1446b81c --- /dev/null +++ b/MediaBrowser.Model/Entities/TonemappingRange.cs @@ -0,0 +1,24 @@ +#pragma warning disable SA1300 // Lowercase required for backwards compat. + +namespace MediaBrowser.Model.Entities; + +/// +/// Enum containing tonemapping ranges. +/// +public enum TonemappingRange +{ + /// + /// Auto. + /// + auto = 0, + + /// + /// TV. + /// + tv = 1, + + /// + /// PC. + /// + pc = 2 +} diff --git a/MediaBrowser.Model/Session/HardwareEncodingType.cs b/MediaBrowser.Model/Session/HardwareEncodingType.cs deleted file mode 100644 index cf424fef5..000000000 --- a/MediaBrowser.Model/Session/HardwareEncodingType.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace MediaBrowser.Model.Session -{ - /// - /// Enum HardwareEncodingType. - /// - public enum HardwareEncodingType - { - /// - /// AMD AMF. - /// - AMF = 0, - - /// - /// Intel Quick Sync Video. - /// - QSV = 1, - - /// - /// NVIDIA NVENC. - /// - NVENC = 2, - - /// - /// Video4Linux2 V4L2. - /// - V4L2M2M = 3, - - /// - /// Video Acceleration API (VAAPI). - /// - VAAPI = 4, - - /// - /// Video ToolBox. - /// - VideoToolBox = 5, - - /// - /// Rockchip Media Process Platform (RKMPP). - /// - RKMPP = 6 - } -} diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs index 000cbd4c5..ae25267ac 100644 --- a/MediaBrowser.Model/Session/TranscodingInfo.cs +++ b/MediaBrowser.Model/Session/TranscodingInfo.cs @@ -1,34 +1,76 @@ #nullable disable -#pragma warning disable CS1591 -namespace MediaBrowser.Model.Session +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Session; + +/// +/// Class holding information on a runnning transcode. +/// +public class TranscodingInfo { - public class TranscodingInfo - { - public string AudioCodec { get; set; } + /// + /// Gets or sets the thread count used for encoding. + /// + public string AudioCodec { get; set; } - public string VideoCodec { get; set; } + /// + /// Gets or sets the thread count used for encoding. + /// + public string VideoCodec { get; set; } - public string Container { get; set; } + /// + /// Gets or sets the thread count used for encoding. + /// + public string Container { get; set; } - public bool IsVideoDirect { get; set; } + /// + /// Gets or sets a value indicating whether the video is passed through. + /// + public bool IsVideoDirect { get; set; } - public bool IsAudioDirect { get; set; } + /// + /// Gets or sets a value indicating whether the audio is passed through. + /// + public bool IsAudioDirect { get; set; } - public int? Bitrate { get; set; } + /// + /// Gets or sets the bitrate. + /// + public int? Bitrate { get; set; } - public float? Framerate { get; set; } + /// + /// Gets or sets the framerate. + /// + public float? Framerate { get; set; } - public double? CompletionPercentage { get; set; } + /// + /// Gets or sets the completion percentage. + /// + public double? CompletionPercentage { get; set; } - public int? Width { get; set; } + /// + /// Gets or sets the video width. + /// + public int? Width { get; set; } - public int? Height { get; set; } + /// + /// Gets or sets the video height. + /// + public int? Height { get; set; } - public int? AudioChannels { get; set; } + /// + /// Gets or sets the audio channels. + /// + public int? AudioChannels { get; set; } - public HardwareEncodingType? HardwareAccelerationType { get; set; } + /// + /// Gets or sets the hardware acceleration type. + /// + public HardwareAccelerationType? HardwareAccelerationType { get; set; } - public TranscodeReason TranscodeReasons { get; set; } - } + /// + /// Gets or sets the transcode reasons. + /// + public TranscodeReason TranscodeReasons { get; set; } } diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs index 9a023d7ed..1846ba26b 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs @@ -128,7 +128,7 @@ public class DynamicHlsPlaylistGenerator : IDynamicHlsPlaylistGenerator return false; } - internal static bool IsExtractionAllowedForFile(ReadOnlySpan filePath, string[] allowedExtensions) + internal static bool IsExtractionAllowedForFile(ReadOnlySpan filePath, IReadOnlyList allowedExtensions) { var extension = Path.GetExtension(filePath); if (extension.IsEmpty) @@ -138,7 +138,7 @@ public class DynamicHlsPlaylistGenerator : IDynamicHlsPlaylistGenerator // Remove the leading dot var extensionWithoutDot = extension[1..]; - for (var i = 0; i < allowedExtensions.Length; i++) + for (var i = 0; i < allowedExtensions.Count; i++) { var allowedExtension = allowedExtensions[i].AsSpan().TrimStart('.'); if (extensionWithoutDot.Equals(allowedExtension, StringComparison.OrdinalIgnoreCase)) -- cgit v1.2.3 From 36d934f4c0df7abf5e0ab5a8d141c68f1a61b7b0 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 9 Sep 2024 23:24:45 +0800 Subject: Enable Rockchip MJPEG encoder for Trickplay (#12610) --- .../MediaEncoding/EncodingHelper.cs | 22 +++++++++------- .../Encoder/EncoderValidator.cs | 3 ++- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 29 ++++++++++++++-------- 3 files changed, 34 insertions(+), 20 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index fdc56652a..778c32c4b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -129,7 +129,8 @@ namespace MediaBrowser.Controller.MediaEncoding { { HardwareAccelerationType.vaapi, _defaultMjpegEncoder + "_vaapi" }, { HardwareAccelerationType.qsv, _defaultMjpegEncoder + "_qsv" }, - { HardwareAccelerationType.videotoolbox, _defaultMjpegEncoder + "_videotoolbox" } + { HardwareAccelerationType.videotoolbox, _defaultMjpegEncoder + "_videotoolbox" }, + { HardwareAccelerationType.rkmpp, _defaultMjpegEncoder + "_rkmpp" } }; public static readonly string[] LosslessAudioCodecs = @@ -5435,6 +5436,9 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwDecoder = !isRkmppDecoder; var isSwEncoder = !isRkmppEncoder; var isDrmInDrmOut = isRkmppDecoder && isRkmppEncoder; + var isEncoderSupportAfbc = isRkmppEncoder + && (vidEncoder.Contains("h264", StringComparison.OrdinalIgnoreCase) + || vidEncoder.Contains("hevc", StringComparison.OrdinalIgnoreCase)); var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); @@ -5493,7 +5497,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // INPUT rkmpp/drm surface(gem/dma-heap) - var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap; + var isFullAfbcPipeline = isEncoderSupportAfbc && isDrmInDrmOut && !doOclTonemap; var swapOutputWandH = doRkVppTranspose && swapWAndH; var outFormat = doOclTonemap ? "p010" : "nv12"; var hwScalePrefix = doRkVppTranspose ? "vpp" : "scale"; @@ -5531,12 +5535,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (doOclTonemap) { var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); - // enable tradeoffs for performance - if (!string.IsNullOrEmpty(tonemapFilter)) - { - tonemapFilter += ":tradeoff=1"; - } - mainFilters.Add(tonemapFilter); } @@ -5607,7 +5605,13 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add("hwupload=derive_device=rkmpp"); // try enabling AFBC to save DDR bandwidth - overlayFilters.Add("overlay_rkrga=eof_action=pass:repeatlast=0:format=nv12:afbc=1"); + var hwOverlayFilter = "overlay_rkrga=eof_action=pass:repeatlast=0:format=nv12"; + if (isEncoderSupportAfbc) + { + hwOverlayFilter += ":afbc=1"; + } + + overlayFilters.Add(hwOverlayFilter); } } else if (memoryOutput) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 2b6ed8fa0..73585caeb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -93,7 +93,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_videotoolbox", "mjpeg_videotoolbox", "h264_rkmpp", - "hevc_rkmpp" + "hevc_rkmpp", + "mjpeg_rkmpp" }; private static readonly string[] _requiredFilters = new[] diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index caa9cb499..6f8769252 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -896,21 +896,30 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new InvalidOperationException("Empty or invalid input argument."); } - float? encoderQuality = qualityScale; - if (vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) + // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst + // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best + var encoderQuality = Math.Clamp(qualityScale ?? 4, 1, 31); + var encoderQualityOption = "-qscale:v "; + + if (vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase) + || vidEncoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) { - // vaapi's mjpeg encoder uses jpeg quality divided by QP2LAMBDA (118) as input, instead of ffmpeg defined qscale - // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst - // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best - encoderQuality = (100 - ((qualityScale - 1) * (100 / 30))) / 118; + // vaapi and qsv's mjpeg encoder use jpeg quality as input, instead of ffmpeg defined qscale + encoderQuality = 100 - ((encoderQuality - 1) * (100 / 30)); + encoderQualityOption = "-global_quality:v "; } if (vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) { // videotoolbox's mjpeg encoder uses jpeg quality scaled to QP2LAMBDA (118) instead of ffmpeg defined qscale - // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst - // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best - encoderQuality = 118 - ((qualityScale - 1) * (118 / 30)); + encoderQuality = 118 - ((encoderQuality - 1) * (118 / 30)); + } + + if (vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase)) + { + // rkmpp's mjpeg encoder uses jpeg quality as input (max is 99, not 100), instead of ffmpeg defined qscale + encoderQuality = 99 - ((encoderQuality - 1) * (99 / 30)); + encoderQualityOption = "-qp_init:v "; } // Output arguments @@ -926,7 +935,7 @@ namespace MediaBrowser.MediaEncoding.Encoder filterParam, outputThreads.GetValueOrDefault(_threads), vidEncoder, - qualityScale.HasValue ? "-qscale:v " + encoderQuality.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty, + encoderQualityOption + encoderQuality + " ", vidEncoder.Contains("videotoolbox", StringComparison.InvariantCultureIgnoreCase) ? "-allow_sw 1 " : string.Empty, // allow_sw fallback for some intel macs "image2", outputPath); -- cgit v1.2.3 From 7c3c0aa940b29cba64ba241eee3cdcd7f65a9eb6 Mon Sep 17 00:00:00 2001 From: jaina heartles Date: Tue, 23 Jul 2024 15:47:38 -0700 Subject: Use subtitle cache when burning-in subs --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 10 ++++++---- MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs | 9 +++++++++ MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 6 ++++++ 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 778c32c4b..5cc604a02 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1652,13 +1652,15 @@ namespace MediaBrowser.Controller.MediaEncoding setPtsParam); } - var mediaPath = state.MediaPath ?? string.Empty; + var subtitlePath = _subtitleEncoder.GetSubtitleFilePath( + state.SubtitleStream, + state.MediaSource, + CancellationToken.None).GetAwaiter().GetResult(); return string.Format( CultureInfo.InvariantCulture, - "subtitles=f='{0}':si={1}{2}{3}{4}{5}", - _mediaEncoder.EscapeSubtitleFilterPath(mediaPath), - state.InternalSubtitleStreamOffset.ToString(CultureInfo.InvariantCulture), + "subtitles=f='{0}'{1}{2}{3}{4}", + _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath), alphaParam, sub2videoParam, fontParam, diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 5bf83a9e3..9bf27b3b2 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -44,5 +44,14 @@ namespace MediaBrowser.Controller.MediaEncoding /// The cancellation token. /// System.String. Task GetSubtitleFileCharacterSet(MediaStream subtitleStream, string language, MediaSourceInfo mediaSource, CancellationToken cancellationToken); + + /// + /// Gets the path to a subtitle file. + /// + /// The subtitle stream. + /// The media source. + /// The cancellation token. + /// System.String. + Task GetSubtitleFilePath(MediaStream subtitleStream, MediaSourceInfo mediaSource, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index dbb4b823e..88a1111a0 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -902,6 +902,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } + public async Task GetSubtitleFilePath(MediaStream subtitleStream, MediaSourceInfo mediaSource, CancellationToken cancellationToken) + { + var info = await GetReadableFile(mediaSource, subtitleStream, cancellationToken); + return info.Path; + } + /// public void Dispose() { -- cgit v1.2.3 From 6deebb449860c67f3b77ec4041efeaa217a18bb9 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 12 Sep 2024 23:52:03 +0800 Subject: Fix QSV presets may be empty (#12633) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5cc604a02..bf6d47ba1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1491,14 +1491,7 @@ namespace MediaBrowser.Controller.MediaEncoding { EncoderPreset[] valid_presets = [EncoderPreset.veryslow, EncoderPreset.slower, EncoderPreset.slow, EncoderPreset.medium, EncoderPreset.fast, EncoderPreset.faster, EncoderPreset.veryfast]; - if (valid_presets.Contains(encoderPreset)) - { - param += " -preset " + encodingOptions.EncoderPreset; - } - else - { - param += " -preset " + EncoderPreset.veryfast.ToString().ToLowerInvariant(); - } + param += " -preset " + (valid_presets.Contains(encoderPreset) ? encoderPreset : EncoderPreset.veryfast).ToString().ToLowerInvariant(); } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc) -- cgit v1.2.3 From 0ff7f28753ed4848b0d5cdbe615787bcf7f3426b Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 12 Sep 2024 23:52:24 +0800 Subject: Enable BWDIF VideoToolbox deint filter when available (#12634) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 ++++- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bf6d47ba1..5a4af8ce8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3237,9 +3237,12 @@ namespace MediaBrowser.Controller.MediaEncoding if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) { + var useBwdif = options.DeinterlaceMethod == DeinterlaceMethod.bwdif && _mediaEncoder.SupportsFilter("bwdif_videotoolbox"); + return string.Format( CultureInfo.InvariantCulture, - "yadif_videotoolbox={0}:-1:0", + "{0}_videotoolbox={1}:-1:0", + useBwdif ? "bwdif" : "yadif", doubleRateDeint ? "1" : "0"); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 73585caeb..b49fbf2ab 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -137,6 +137,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "flip_vulkan", // videotoolbox "yadif_videotoolbox", + "bwdif_videotoolbox", "scale_vt", "transpose_vt", "overlay_videotoolbox", -- cgit v1.2.3 From d3e7f53d93b7bfcc0a70c6bea069c6fb53e6c37e Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 17 Sep 2024 00:47:02 +0800 Subject: Fix some PGSSUB burn-in perf regressions (#12655) --- .../MediaEncoding/EncodingHelper.cs | 89 ++++++++++++++++------ 1 file changed, 66 insertions(+), 23 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5a4af8ce8..b186e3e53 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2980,6 +2980,8 @@ namespace MediaBrowser.Controller.MediaEncoding public static string GetGraphicalSubPreProcessFilters( int? videoWidth, int? videoHeight, + int? subtitleWidth, + int? subtitleHeight, int? requestedWidth, int? requestedHeight, int? requestedMaxWidth, @@ -2993,16 +2995,37 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxWidth, requestedMaxHeight); - if (outWidth.HasValue && outHeight.HasValue) + if (!outWidth.HasValue + || !outHeight.HasValue + || outWidth.Value <= 0 + || outHeight.Value <= 0) { - return string.Format( - CultureInfo.InvariantCulture, - @"scale,scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}", - outWidth.Value, - outHeight.Value); + return string.Empty; } - return string.Empty; + // Automatically add padding based on subtitle input + var filters = @"scale,scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}"; + + if (subtitleWidth.HasValue + && subtitleHeight.HasValue + && subtitleWidth.Value > 0 + && subtitleHeight.Value > 0) + { + var videoDar = (double)outWidth.Value / outHeight.Value; + var subtitleDar = (double)subtitleWidth.Value / subtitleHeight.Value; + + // No need to add padding when DAR is the same -> 1080p PGSSUB on 2160p video + if (videoDar == subtitleDar) + { + filters = @"scale,scale={0}:{1}:fast_bilinear"; + } + } + + return string.Format( + CultureInfo.InvariantCulture, + filters, + outWidth.Value, + outHeight.Value); } public static string GetAlphaSrcFilter( @@ -3507,7 +3530,9 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } @@ -3580,6 +3605,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -3683,7 +3710,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); subFilters.Add("format=yuva420p"); } @@ -3708,7 +3735,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } @@ -3783,6 +3810,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -3897,7 +3926,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); subFilters.Add("format=yuva420p"); } @@ -3924,7 +3953,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } @@ -4021,6 +4050,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -4193,7 +4224,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (hasGraphicalSubs) { // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -4229,7 +4260,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } @@ -4273,6 +4304,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -4439,7 +4472,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (hasGraphicalSubs) { // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -4474,7 +4507,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } @@ -4584,6 +4617,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -4725,7 +4760,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (hasGraphicalSubs) { // overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -4758,7 +4793,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); @@ -4950,7 +4985,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -5173,7 +5210,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); @@ -5324,7 +5363,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -5449,6 +5490,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasAssSubs = hasSubs && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + var subW = state.SubtitleStream?.Width; + var subH = state.SubtitleStream?.Height; var rotation = state.VideoStream?.Rotation ?? 0; var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state); @@ -5583,7 +5626,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); subFilters.Add("format=bgra"); } @@ -5616,7 +5659,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subPreProcFilters); overlayFilters.Add("overlay=eof_action=pass:repeatlast=0"); } -- cgit v1.2.3 From 41ac5f8d76ce11a852e4dafbf20ad57d63d55f96 Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 17 Sep 2024 21:08:16 +0800 Subject: Fix subtitle dar comparison when number not exact (#12660) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b186e3e53..88aa888a1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3015,7 +3015,7 @@ namespace MediaBrowser.Controller.MediaEncoding var subtitleDar = (double)subtitleWidth.Value / subtitleHeight.Value; // No need to add padding when DAR is the same -> 1080p PGSSUB on 2160p video - if (videoDar == subtitleDar) + if (Math.Abs(videoDar - subtitleDar) < 0.01f) { filters = @"scale,scale={0}:{1}:fast_bilinear"; } -- cgit v1.2.3 From 93db8990d951649c1d25003e2859776ac80e7440 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 19 Sep 2024 21:14:18 +0800 Subject: Enable HEVC RExt HW decoding for 4:2:2/4:4:4 content (#12664) --- .../MediaEncoding/EncodingHelper.cs | 175 ++++++++++++++++----- .../Configuration/EncodingOptions.cs | 12 ++ 2 files changed, 147 insertions(+), 40 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 88aa888a1..e26bcf21e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -299,7 +299,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.VideoStream is null || !options.EnableTonemapping - || GetVideoColorBitDepth(state) != 10 + || GetVideoColorBitDepth(state) < 10 || !_mediaEncoder.SupportsFilter("tonemapx")) { return false; @@ -312,7 +312,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.VideoStream is null || !options.EnableTonemapping - || GetVideoColorBitDepth(state) != 10) + || GetVideoColorBitDepth(state) < 10) { return false; } @@ -354,7 +354,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.VideoStream is null || !options.EnableVppTonemapping - || GetVideoColorBitDepth(state) != 10) + || GetVideoColorBitDepth(state) < 10) { return false; } @@ -377,7 +377,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.VideoStream is null || !options.EnableVideoToolboxTonemapping - || GetVideoColorBitDepth(state) != 10) + || GetVideoColorBitDepth(state) < 10) { return false; } @@ -388,6 +388,25 @@ namespace MediaBrowser.Controller.MediaEncoding && state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.HDR10Plus or VideoRangeType.DOVIWithHDR10 or VideoRangeType.DOVIWithHLG; } + private bool IsVideoStreamHevcRext(EncodingJobInfo state) + { + var videoStream = state.VideoStream; + if (videoStream is null) + { + return false; + } + + return string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) + && (string.Equals(videoStream.Profile, "Rext", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv422p", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv422p10le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv422p12le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase)); + } + /// /// Gets the name of the output video codec. /// @@ -3659,7 +3678,8 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add($"transpose_cuda=dir={tranposeDir}"); } - var outFormat = doCuTonemap ? string.Empty : "yuv420p"; + var isRext = IsVideoStreamHevcRext(state); + var outFormat = doCuTonemap ? (isRext ? "p010" : string.Empty) : "yuv420p"; var hwScaleFilter = GetHwScaleFilter("scale", "cuda", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); // hw scale mainFilters.Add(hwScaleFilter); @@ -4091,6 +4111,8 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (isD3d11vaDecoder || isQsvDecoder) { + var isRext = IsVideoStreamHevcRext(state); + var twoPassVppTonemap = isRext; var doVppProcamp = false; var procampParams = string.Empty; if (doVppTonemap) @@ -4100,21 +4122,21 @@ namespace MediaBrowser.Controller.MediaEncoding && options.VppTonemappingBrightness <= 100) { procampParams += $":brightness={options.VppTonemappingBrightness}"; - doVppProcamp = true; + twoPassVppTonemap = doVppProcamp = true; } if (options.VppTonemappingContrast > 1 && options.VppTonemappingContrast <= 10) { procampParams += $":contrast={options.VppTonemappingContrast}"; - doVppProcamp = true; + twoPassVppTonemap = doVppProcamp = true; } procampParams += doVppProcamp ? ":procamp=1:async_depth=2" : string.Empty; } - var outFormat = doOclTonemap ? (doVppTranspose ? "p010" : string.Empty) : "nv12"; - outFormat = (doVppTonemap && doVppProcamp) ? "p010" : outFormat; + var outFormat = doOclTonemap ? ((doVppTranspose || isRext) ? "p010" : string.Empty) : "nv12"; + outFormat = twoPassVppTonemap ? "p010" : outFormat; var swapOutputWandH = doVppTranspose && swapWAndH; var hwScalePrefix = (doVppTranspose || doVppTonemap) ? "vpp" : "scale"; @@ -4127,7 +4149,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap) { - hwScaleFilter += doVppProcamp ? procampParams : ":tonemap=1"; + hwScaleFilter += doVppProcamp ? procampParams : (twoPassVppTonemap ? string.Empty : ":tonemap=1"); } if (isD3d11vaDecoder) @@ -4151,7 +4173,7 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add(hwScaleFilter); // hw tonemap(w/ procamp) - if (doVppTonemap && doVppProcamp) + if (doVppTonemap && twoPassVppTonemap) { mainFilters.Add("vpp_qsv=tonemap=1:format=nv12:async_depth=2"); } @@ -4346,6 +4368,7 @@ namespace MediaBrowser.Controller.MediaEncoding else if (isVaapiDecoder || isQsvDecoder) { var hwFilterSuffix = isVaapiDecoder ? "vaapi" : "qsv"; + var isRext = IsVideoStreamHevcRext(state); // INPUT vaapi/qsv surface(vram) // hw deint @@ -4362,6 +4385,8 @@ namespace MediaBrowser.Controller.MediaEncoding } var outFormat = doOclTonemap ? ((isQsvDecoder && doVppTranspose) ? "p010" : string.Empty) : "nv12"; + outFormat = (doTonemap && isRext) ? "p010" : outFormat; + var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH; var hwScalePrefix = (isQsvDecoder && doVppTranspose) ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); @@ -4658,6 +4683,8 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (isVaapiDecoder) { + var isRext = IsVideoStreamHevcRext(state); + // INPUT vaapi surface(vram) // hw deint if (doDeintH2645) @@ -4672,7 +4699,7 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add($"transpose_vaapi=dir={tranposeDir}"); } - var outFormat = doTonemap ? string.Empty : "nv12"; + var outFormat = doTonemap ? (isRext ? "p010" : string.Empty) : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); // allocate extra pool sizes for vaapi vpp @@ -5974,7 +6001,11 @@ namespace MediaBrowser.Controller.MediaEncoding var decoderName = decoderPrefix + '_' + decoderSuffix; var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoderName) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase); - if (bitDepth == 10 && isCodecAvailable) + + // VideoToolbox decoders have built-in SW fallback + if (bitDepth == 10 + && isCodecAvailable + && (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox)) { if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase) && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase) @@ -6050,17 +6081,40 @@ namespace MediaBrowser.Controller.MediaEncoding && ffmpegVersion >= _minFFmpegDisplayRotationOption; var stripRotationDataArgs = stripRotationData ? " -display_rotation 0" : string.Empty; - if (bitDepth == 10 && isCodecAvailable) + // VideoToolbox decoders have built-in SW fallback + if (isCodecAvailable + && (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox)) { if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase) - && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase) - && !options.EnableDecodingColorDepth10Hevc) + && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase)) { - return null; + if (IsVideoStreamHevcRext(state)) + { + if (bitDepth <= 10 && !options.EnableDecodingColorDepth10HevcRext) + { + return null; + } + + if (bitDepth == 12 && !options.EnableDecodingColorDepth12HevcRext) + { + return null; + } + + if (hardwareAccelerationType == HardwareAccelerationType.vaapi + && !_mediaEncoder.IsVaapiDeviceInteliHD) + { + return null; + } + } + else if (bitDepth == 10 && !options.EnableDecodingColorDepth10Hevc) + { + return null; + } } if (string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase) && options.HardwareDecodingCodecs.Contains("vp9", StringComparison.OrdinalIgnoreCase) + && bitDepth == 10 && !options.EnableDecodingColorDepth10Vp9) { return null; @@ -6172,6 +6226,14 @@ namespace MediaBrowser.Controller.MediaEncoding var is8bitSwFormatsQsv = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsQsv = is8bitSwFormatsQsv || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8_10_12bitSwFormatsQsv = is8_10bitSwFormatsQsv + || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); // TODO: add more 8/10bit and 4:4:4 formats for Qsv after finishing the ffcheck tool if (is8bitSwFormatsQsv) @@ -6200,12 +6262,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (is8_10bitSwFormatsQsv) { - if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase)) - { - return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "qsv", "hevc", bitDepth); - } - if (string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)) { return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface) + GetHwDecoderName(options, "vp9", "qsv", "vp9", bitDepth); @@ -6217,6 +6273,15 @@ namespace MediaBrowser.Controller.MediaEncoding } } + if (is8_10_12bitSwFormatsQsv) + { + if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase)) + { + return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "qsv", "hevc", bitDepth); + } + } + return null; } @@ -6232,6 +6297,11 @@ namespace MediaBrowser.Controller.MediaEncoding var is8bitSwFormatsNvdec = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsNvdec = is8bitSwFormatsNvdec || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8_10_12bitSwFormatsNvdec = is8_10bitSwFormatsNvdec + || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); // TODO: add more 8/10/12bit and 4:4:4 formats for Nvdec after finishing the ffcheck tool if (is8bitSwFormatsNvdec) @@ -6265,12 +6335,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (is8_10bitSwFormatsNvdec) { - if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) - || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) - { - return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "cuvid", "hevc", bitDepth); - } - if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface) + GetHwDecoderName(options, "vp9", "cuvid", "vp9", bitDepth); @@ -6282,6 +6346,15 @@ namespace MediaBrowser.Controller.MediaEncoding } } + if (is8_10_12bitSwFormatsNvdec) + { + if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) + || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + { + return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "cuvid", "hevc", bitDepth); + } + } + return null; } @@ -6356,6 +6429,14 @@ namespace MediaBrowser.Controller.MediaEncoding var is8bitSwFormatsVaapi = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVaapi = is8bitSwFormatsVaapi || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8_10_12bitSwFormatsVaapi = is8_10bitSwFormatsVaapi + || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); if (is8bitSwFormatsVaapi) { @@ -6383,12 +6464,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (is8_10bitSwFormatsVaapi) { - if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) - || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) - { - return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface); - } - if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface); @@ -6400,6 +6475,15 @@ namespace MediaBrowser.Controller.MediaEncoding } } + if (is8_10_12bitSwFormatsVaapi) + { + if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) + || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + { + return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface); + } + } + return null; } @@ -6414,6 +6498,14 @@ namespace MediaBrowser.Controller.MediaEncoding var is8bitSwFormatsVt = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8_10_12bitSwFormatsVt = is8_10bitSwFormatsVt + || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); // The related patches make videotoolbox hardware surface working is only available in jellyfin-ffmpeg 7.0.1 at the moment. bool useHwSurface = (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface) && IsVideoToolboxFullSupported(); @@ -6434,15 +6526,18 @@ namespace MediaBrowser.Controller.MediaEncoding return GetHwaccelType(state, options, "h264", bitDepth, useHwSurface); } - if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) - || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface); + return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface); } + } - if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) + if (is8_10_12bitSwFormatsVt) + { + if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) + || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface); + return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface); } } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index d67a2479f..2720c0bdf 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -46,6 +46,8 @@ public class EncodingOptions DeinterlaceMethod = DeinterlaceMethod.yadif; EnableDecodingColorDepth10Hevc = true; EnableDecodingColorDepth10Vp9 = true; + EnableDecodingColorDepth10HevcRext = false; + EnableDecodingColorDepth12HevcRext = false; // Enhanced Nvdec or system native decoder is required for DoVi to SDR tone-mapping. EnableEnhancedNvdecDecoder = true; PreferSystemNativeHwDecoder = true; @@ -234,6 +236,16 @@ public class EncodingOptions /// public bool EnableDecodingColorDepth10Vp9 { get; set; } + /// + /// Gets or sets a value indicating whether 8/10bit HEVC RExt decoding is enabled. + /// + public bool EnableDecodingColorDepth10HevcRext { get; set; } + + /// + /// Gets or sets a value indicating whether 12bit HEVC RExt decoding is enabled. + /// + public bool EnableDecodingColorDepth12HevcRext { get; set; } + /// /// Gets or sets a value indicating whether the enhanced NVDEC is enabled. /// -- cgit v1.2.3 From e615b56a70d88c441589bb9f1fd0159e97ea4b1a Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 20 Sep 2024 21:01:58 +0800 Subject: Fix RKMPP 2pass scaling in Trickplay (#12675) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e26bcf21e..d8c4f2f85 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5570,13 +5570,22 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doOclTonemap ? "p010" : "nv12"; var hwScalePrefix = doRkVppTranspose ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); - var hwScaleFilter2 = GetHwScaleFilter(hwScalePrefix, "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var doScaling = GetHwScaleFilter(hwScalePrefix, "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!hasSubs || doRkVppTranspose || !isFullAfbcPipeline - || !string.IsNullOrEmpty(hwScaleFilter2)) + || !string.IsNullOrEmpty(doScaling)) { + // RGA3 hardware only support (1/8 ~ 8) scaling in each blit operation, + // but in Trickplay there's a case: (3840/320 == 12), enable 2pass for it + if (!string.IsNullOrEmpty(doScaling) + && !IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 8.0f)) + { + var hwScaleFilterFirstPass = $"scale_rkrga=w=iw/8:h=ih/8:format={outFormat}:afbc=1"; + mainFilters.Add(hwScaleFilterFirstPass); + } + if (!string.IsNullOrEmpty(hwScaleFilter) && doRkVppTranspose) { hwScaleFilter += $":transpose={tranposeDir}"; -- cgit v1.2.3 From d944f415f3cc0e5433d94b11a16684ca3f0131ec Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 00:34:47 +0800 Subject: Let HLS Controller decide if subtitle should be burn in Previously, we predicted whether the subtitle should be burned in with transcode reasons, but that was not accurate because the actual transcoding codec is only determined after the client has requested the stream. This pass through the option to the `DynamicHlsController` to handle the subtitle burn-in during the actual transcoding process. Now the client should be responsible to conditionally load the subtitle when this option is enabled. --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 63 ++++++++++++++-------- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 6 +++ .../MediaEncoding/BaseEncodingJobOptions.cs | 2 + .../MediaEncoding/EncodingHelper.cs | 38 +++++++------ MediaBrowser.Model/Dlna/StreamBuilder.cs | 18 ++----- MediaBrowser.Model/Dlna/StreamInfo.cs | 7 ++- 6 files changed, 81 insertions(+), 53 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index db1d86698..924f010e4 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -158,6 +158,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The max height. /// Optional. Whether to enable subtitles in the manifest. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Hls live stream retrieved. /// A containing the hls file. [HttpGet("Videos/{itemId}/live.m3u8")] @@ -216,7 +217,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, [FromQuery] bool? enableSubtitlesInManifest, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { VideoRequestDto streamingRequest = new VideoRequestDto { @@ -251,7 +253,7 @@ public class DynamicHlsController : BaseJellyfinApiController Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -271,7 +273,8 @@ public class DynamicHlsController : BaseJellyfinApiController MaxHeight = maxHeight, MaxWidth = maxWidth, EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; // CTS lifecycle is managed internally. @@ -398,6 +401,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Enable adaptive bitrate streaming. /// Enable trickplay image playlists being added to master playlist. /// Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Video stream returned. /// A containing the playlist file. [HttpGet("Videos/{itemId}/master.m3u8")] @@ -457,7 +461,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true, [FromQuery] bool enableTrickplay = true, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { var streamingRequest = new HlsVideoRequestDto { @@ -493,7 +498,7 @@ public class DynamicHlsController : BaseJellyfinApiController MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -512,7 +517,8 @@ public class DynamicHlsController : BaseJellyfinApiController StreamOptions = streamOptions, EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming, EnableTrickplay = enableTrickplay, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); @@ -572,6 +578,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The streaming options. /// Enable adaptive bitrate streaming. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Audio stream returned. /// A containing the playlist file. [HttpGet("Audio/{itemId}/master.m3u8")] @@ -629,7 +636,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, [FromQuery] bool enableAdaptiveBitrateStreaming = true, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { var streamingRequest = new HlsAudioRequestDto { @@ -663,7 +671,7 @@ public class DynamicHlsController : BaseJellyfinApiController Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -681,7 +689,8 @@ public class DynamicHlsController : BaseJellyfinApiController Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false); @@ -741,6 +750,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The . /// Optional. The streaming options. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Videos/{itemId}/main.m3u8")] @@ -797,7 +807,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new VideoRequestDto @@ -834,7 +845,7 @@ public class DynamicHlsController : BaseJellyfinApiController MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -851,7 +862,8 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -911,6 +923,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The . /// Optional. The streaming options. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Audio stream returned. /// A containing the audio file. [HttpGet("Audio/{itemId}/main.m3u8")] @@ -966,7 +979,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { using var cancellationTokenSource = new CancellationTokenSource(); var streamingRequest = new StreamingRequestDto @@ -1001,7 +1015,7 @@ public class DynamicHlsController : BaseJellyfinApiController Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -1018,7 +1032,8 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) @@ -1084,6 +1099,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The . /// Optional. The streaming options. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] @@ -1146,7 +1162,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { var streamingRequest = new VideoRequestDto { @@ -1185,7 +1202,7 @@ public class DynamicHlsController : BaseJellyfinApiController MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -1202,7 +1219,8 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await GetDynamicSegment(streamingRequest, segmentId) @@ -1267,6 +1285,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Optional. The . /// Optional. The streaming options. /// Optional. Whether to enable Audio Encoding. + /// Whether to always burn in subtitles when transcoding. /// Video stream returned. /// A containing the audio file. [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")] @@ -1328,7 +1347,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? videoStreamIndex, [FromQuery] EncodingContext? context, [FromQuery] Dictionary streamOptions, - [FromQuery] bool enableAudioVbrEncoding = true) + [FromQuery] bool enableAudioVbrEncoding = true, + [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false) { var streamingRequest = new StreamingRequestDto { @@ -1365,7 +1385,7 @@ public class DynamicHlsController : BaseJellyfinApiController Height = height, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, - SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, + SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External, MaxRefFrames = maxRefFrames, MaxVideoBitDepth = maxVideoBitDepth, RequireAvc = requireAvc ?? false, @@ -1382,7 +1402,8 @@ public class DynamicHlsController : BaseJellyfinApiController VideoStreamIndex = videoStreamIndex, Context = context ?? EncodingContext.Streaming, StreamOptions = streamOptions, - EnableAudioVbrEncoding = enableAudioVbrEncoding + EnableAudioVbrEncoding = enableAudioVbrEncoding, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding }; return await GetDynamicSegment(streamingRequest, segmentId) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 5050cab41..2d9ecd4f0 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -293,6 +293,7 @@ public class MediaInfoHelper mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; + mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true"; } else { @@ -310,6 +311,11 @@ public class MediaInfoHelper { mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; } + + if (streamInfo.AlwaysBurnInSubtitleWhenTranscoding) + { + mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true"; + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs index f77186e25..20f51ddb7 100644 --- a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs @@ -193,6 +193,8 @@ namespace MediaBrowser.Controller.MediaEncoding public bool EnableAudioVbrEncoding { get; set; } + public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; } + public string GetOption(string qualifier, string name) { var value = GetOption(qualifier + "-" + name); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index d8c4f2f85..f8ba8ddd8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -941,7 +941,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // DVBSUB uses the fixed canvas size 720x576 if (state.SubtitleStream is not null - && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + && ShouldEncodeSubtitle(state) && !state.SubtitleStream.IsTextSubtitleStream && !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase)) { @@ -1240,7 +1240,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sub2video for external graphical subtitles if (state.SubtitleStream is not null - && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + && ShouldEncodeSubtitle(state) && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleStream.IsExternal) { @@ -2554,7 +2554,7 @@ namespace MediaBrowser.Controller.MediaEncoding } var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive; - if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !isCopyingTimestamps) + if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state) && !isCopyingTimestamps) { var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds; @@ -2755,7 +2755,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream.IsExternal) { bool hasExternalGraphicsSubs = state.SubtitleStream is not null - && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + && ShouldEncodeSubtitle(state) && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream; int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1; @@ -3475,7 +3475,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doToneMap = IsSwTonemapAvailable(state, options); var requireDoviReshaping = doToneMap && state.VideoStream.VideoRangeType == VideoRangeType.DOVI; - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; @@ -3618,7 +3618,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doDeintH2645 = doDeintH264 || doDeintHevc; var doCuTonemap = IsHwTonemapAvailable(state, options); - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -3824,7 +3824,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doDeintH2645 = doDeintH264 || doDeintHevc; var doOclTonemap = IsHwTonemapAvailable(state, options); - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -4064,7 +4064,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doOclTonemap = !doVppTonemap && IsHwTonemapAvailable(state, options); var doTonemap = doVppTonemap || doOclTonemap; - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -4320,7 +4320,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doTonemap = doVaVppTonemap || doOclTonemap; var doDeintH2645 = doDeintH264 || doDeintHevc; - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -4636,7 +4636,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doTonemap = doVaVppTonemap || doOclTonemap; var doDeintH2645 = doDeintH264 || doDeintHevc; - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -4858,7 +4858,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doVkTonemap = IsVulkanHwTonemapAvailable(state, options); var doDeintH2645 = doDeintH264 || doDeintHevc; - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -5091,7 +5091,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doDeintH2645 = doDeintH264 || doDeintHevc; var doOclTonemap = IsHwTonemapAvailable(state, options); - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; @@ -5339,7 +5339,7 @@ namespace MediaBrowser.Controller.MediaEncoding var hwScaleFilter = GetHwScaleFilter("scale", "vt", scaleFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -5511,7 +5511,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doDeintH2645 = doDeintH264 || doDeintHevc; var doOclTonemap = IsHwTonemapAvailable(state, options); - var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream != null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs @@ -5722,7 +5722,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state); var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; @@ -7156,7 +7156,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += keyFrameArg; - var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state); var hasCopyTs = false; @@ -7361,5 +7361,11 @@ namespace MediaBrowser.Controller.MediaEncoding { return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); } + + private static bool ShouldEncodeSubtitle(EncodingJobInfo state) + { + return state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + || (state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding && !IsCopyCodec(state.OutputVideoCodec)); + } } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bf122dcc7..6c45f1946 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -639,7 +639,8 @@ namespace MediaBrowser.Model.Dlna RunTimeTicks = item.RunTimeTicks, Context = options.Context, DeviceProfile = options.Profile, - SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles) + SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles), + AlwaysBurnInSubtitleWhenTranscoding = options.AlwaysBurnInSubtitleWhenTranscoding }; var subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; @@ -767,20 +768,7 @@ namespace MediaBrowser.Model.Dlna if (subtitleStream is not null) { var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol); - - if (options.AlwaysBurnInSubtitleWhenTranscoding && (playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0) - { - playlistItem.SubtitleDeliveryMethod = SubtitleDeliveryMethod.Encode; - foreach (SubtitleProfile profile in options.Profile.SubtitleProfiles) - { - profile.Method = SubtitleDeliveryMethod.Encode; - } - } - else - { - playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; - } - + playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; playlistItem.SubtitleCodecs = [subtitleProfile.Format]; } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 3be686088..1ae4e1962 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -270,6 +270,11 @@ public class StreamInfo /// public bool EnableAudioVbrEncoding { get; set; } + /// + /// Gets or sets a value indicating whether always burn in subtitles when transcoding. + /// + public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; } + /// /// Gets a value indicating whether the stream is direct. /// @@ -953,7 +958,7 @@ public class StreamInfo list.Add(new NameValuePair("VideoCodec", videoCodecs)); list.Add(new NameValuePair("AudioCodec", audioCodecs)); list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); - list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && (item.AlwaysBurnInSubtitleWhenTranscoding || item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External) ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); -- cgit v1.2.3 From 56cf1a581c7f0ffc19fca284a13878d231136aaa Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 22 Sep 2024 10:01:47 +0800 Subject: Better bitrate and resolution normalization (#12644) --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 8 ++- .../MediaEncoding/EncodingHelper.cs | 8 ++- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 69 +++++++++++----------- 3 files changed, 50 insertions(+), 35 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 3cc6a393b..3a5db2f3f 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -219,11 +219,17 @@ public static class StreamingHelpers } else { + var h264EquivalentBitrate = EncodingHelper.ScaleBitrate( + state.OutputVideoBitrate.Value, + state.ActualOutputVideoCodec, + "h264"); var resolution = ResolutionNormalizer.Normalize( state.VideoStream?.BitRate, state.OutputVideoBitrate.Value, + h264EquivalentBitrate, state.VideoRequest.MaxWidth, - state.VideoRequest.MaxHeight); + state.VideoRequest.MaxHeight, + state.TargetFramerate); state.VideoRequest.MaxWidth = resolution.MaxWidth; state.VideoRequest.MaxHeight = resolution.MaxHeight; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index f8ba8ddd8..788bd0335 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2400,7 +2400,7 @@ namespace MediaBrowser.Controller.MediaEncoding return 1; } - private static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec) + public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec) { var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec); var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec); @@ -2424,6 +2424,12 @@ namespace MediaBrowser.Controller.MediaEncoding { scaleFactor = Math.Max(scaleFactor, 2); } + else if (bitrate >= 30000000) + { + // Don't scale beyond 30Mbps, it is hardly visually noticeable for most codecs with our prefer speed encoding + // and will cause extremely high bitrate to be used for av1->h264 transcoding that will overload clients and encoders + scaleFactor = 1; + } return Convert.ToInt32(scaleFactor * bitrate); } diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index 5d7daa81a..1a636b240 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -2,28 +2,33 @@ #pragma warning disable CS1591 using System; +using System.Linq; namespace MediaBrowser.Model.Dlna { public static class ResolutionNormalizer { - private static readonly ResolutionConfiguration[] Configurations = - new[] - { - new ResolutionConfiguration(426, 320000), - new ResolutionConfiguration(640, 400000), - new ResolutionConfiguration(720, 950000), - new ResolutionConfiguration(1280, 2500000), - new ResolutionConfiguration(1920, 4000000), - new ResolutionConfiguration(2560, 20000000), - new ResolutionConfiguration(3840, 35000000) - }; + // Please note: all bitrate here are in the scale of SDR h264 bitrate at 30fps + private static readonly ResolutionConfiguration[] _configurations = + [ + new ResolutionConfiguration(416, 365000), + new ResolutionConfiguration(640, 730000), + new ResolutionConfiguration(768, 1100000), + new ResolutionConfiguration(960, 3000000), + new ResolutionConfiguration(1280, 6000000), + new ResolutionConfiguration(1920, 13500000), + new ResolutionConfiguration(2560, 28000000), + new ResolutionConfiguration(3840, 50000000) + ]; public static ResolutionOptions Normalize( int? inputBitrate, int outputBitrate, + int h264EquivalentOutputBitrate, int? maxWidth, - int? maxHeight) + int? maxHeight, + float? targetFps, + bool isHdr = false) // We are not doing HDR transcoding for now, leave for future use { // If the bitrate isn't changing, then don't downscale the resolution if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value) @@ -38,16 +43,26 @@ namespace MediaBrowser.Model.Dlna } } - var resolutionConfig = GetResolutionConfiguration(outputBitrate); - if (resolutionConfig is not null) + var referenceBitrate = h264EquivalentOutputBitrate * (30.0f / (targetFps ?? 30.0f)); + + if (isHdr) { - var originvalValue = maxWidth; + referenceBitrate *= 0.8f; + } - maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth); - if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value) - { - maxHeight = null; - } + var resolutionConfig = GetResolutionConfiguration(Convert.ToInt32(referenceBitrate)); + + if (resolutionConfig is null) + { + return new ResolutionOptions { MaxWidth = maxWidth, MaxHeight = maxHeight }; + } + + var originWidthValue = maxWidth; + + maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth); + if (!originWidthValue.HasValue || originWidthValue.Value != maxWidth.Value) + { + maxHeight = null; } return new ResolutionOptions @@ -59,19 +74,7 @@ namespace MediaBrowser.Model.Dlna private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate) { - ResolutionConfiguration previousOption = null; - - foreach (var config in Configurations) - { - if (outputBitrate <= config.MaxBitrate) - { - return previousOption ?? config; - } - - previousOption = config; - } - - return null; + return _configurations.FirstOrDefault(config => outputBitrate <= config.MaxBitrate); } } } -- cgit v1.2.3 From b162e9290ba7e4265010079708e158399356635c Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 22 Sep 2024 22:58:15 +0800 Subject: Fix the diff between requested and actual resolution in RKMPP (#12680) --- .../MediaEncoding/EncodingHelper.cs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 788bd0335..5322b2635 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5574,9 +5574,8 @@ namespace MediaBrowser.Controller.MediaEncoding var isFullAfbcPipeline = isEncoderSupportAfbc && isDrmInDrmOut && !doOclTonemap; var swapOutputWandH = doRkVppTranspose && swapWAndH; var outFormat = doOclTonemap ? "p010" : "nv12"; - var hwScalePrefix = doRkVppTranspose ? "vpp" : "scale"; - var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); - var doScaling = GetHwScaleFilter(hwScalePrefix, "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var hwScaleFilter = GetHwScaleFilter("vpp", "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var doScaling = GetHwScaleFilter("vpp", "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!hasSubs || doRkVppTranspose @@ -5588,7 +5587,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(doScaling) && !IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 8.0f)) { - var hwScaleFilterFirstPass = $"scale_rkrga=w=iw/8:h=ih/8:format={outFormat}:afbc=1"; + var hwScaleFilterFirstPass = $"scale_rkrga=w=iw/7.9:h=ih/7.9:format={outFormat}:afbc=1"; mainFilters.Add(hwScaleFilterFirstPass); } @@ -5980,19 +5979,6 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var whichCodec = videoStream.Codec; - if (string.Equals(whichCodec, "avc", StringComparison.OrdinalIgnoreCase)) - { - whichCodec = "h264"; - } - else if (string.Equals(whichCodec, "h265", StringComparison.OrdinalIgnoreCase)) - { - whichCodec = "hevc"; - } - - // Avoid a second attempt if no hardware acceleration is being used - options.HardwareDecodingCodecs = options.HardwareDecodingCodecs.Where(c => !string.Equals(c, whichCodec, StringComparison.OrdinalIgnoreCase)).ToArray(); - // leave blank so ffmpeg will decide return null; } -- cgit v1.2.3 From 5d5afe10e8094f14e5443e1fd3295bdf325f1566 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 23 Sep 2024 08:50:18 +0800 Subject: Fix incorrect input range for certain hw JPEG encoders (#12683) --- .../MediaEncoding/EncodingHelper.cs | 81 +++++++++++++++++----- 1 file changed, 62 insertions(+), 19 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5322b2635..e9e8ed618 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -71,6 +71,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegAdvancedTonemapMode = new Version(7, 0, 1); private readonly Version _minFFmpegAlteredVaVkInterop = new Version(7, 0, 1); private readonly Version _minFFmpegQsvVppTonemapOption = new Version(7, 0, 1); + private readonly Version _minFFmpegQsvVppOutRangeOption = new Version(7, 0, 1); private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); @@ -3297,7 +3298,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Empty; } - public string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat) + private string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat, bool forceFullRange) { if (string.IsNullOrEmpty(hwTonemapSuffix)) { @@ -3307,7 +3308,7 @@ namespace MediaBrowser.Controller.MediaEncoding var args = string.Empty; var algorithm = options.TonemappingAlgorithm.ToString().ToLowerInvariant(); var mode = options.TonemappingMode.ToString().ToLowerInvariant(); - var range = options.TonemappingRange; + var range = forceFullRange ? TonemappingRange.pc : options.TonemappingRange; var rangeString = range.ToString().ToLowerInvariant(); if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase)) @@ -3377,7 +3378,7 @@ namespace MediaBrowser.Controller.MediaEncoding rangeString); } - public string GetLibplaceboFilter( + private string GetLibplaceboFilter( EncodingOptions options, string videoFormat, bool doTonemap, @@ -3386,7 +3387,8 @@ namespace MediaBrowser.Controller.MediaEncoding int? requestedWidth, int? requestedHeight, int? requestedMaxWidth, - int? requestedMaxHeight) + int? requestedMaxHeight, + bool forceFullRange) { var (outWidth, outHeight) = GetFixedOutputSize( videoWidth, @@ -3411,7 +3413,7 @@ namespace MediaBrowser.Controller.MediaEncoding var algorithm = options.TonemappingAlgorithm; var algorithmString = "clip"; var mode = options.TonemappingMode; - var range = options.TonemappingRange; + var range = forceFullRange ? TonemappingRange.pc : options.TonemappingRange; if (algorithm == TonemappingAlgorithm.bt2390) { @@ -3616,6 +3618,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isNvencEncoder = vidEncoder.Contains("nvenc", StringComparison.OrdinalIgnoreCase); var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isNvencEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isCuInCuOut = isNvDecoder && isNvencEncoder; var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; @@ -3694,7 +3697,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hw tonemap if (doCuTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "cuda", "yuv420p"); + var tonemapFilter = GetHwTonemapFilter(options, "cuda", "yuv420p", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -3823,6 +3826,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isAmfEncoder = vidEncoder.Contains("amf", StringComparison.OrdinalIgnoreCase); var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isAmfEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isDxInDxOut = isD3d11vaDecoder && isAmfEncoder; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); @@ -3902,7 +3906,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hw tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -4061,6 +4065,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isHwDecoder = isD3d11vaDecoder || isQsvDecoder; var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isQsvEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isQsvInQsvOut = isHwDecoder && isQsvEncoder; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); @@ -4119,6 +4124,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var isRext = IsVideoStreamHevcRext(state); var twoPassVppTonemap = isRext; + var doVppFullRangeOut = isMjpegEncoder + && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption; var doVppProcamp = false; var procampParams = string.Empty; if (doVppTonemap) @@ -4145,7 +4152,7 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = twoPassVppTonemap ? "p010" : outFormat; var swapOutputWandH = doVppTranspose && swapWAndH; - var hwScalePrefix = (doVppTranspose || doVppTonemap) ? "vpp" : "scale"; + var hwScalePrefix = (doVppTranspose || doVppTonemap || doVppFullRangeOut) ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTranspose) @@ -4153,6 +4160,11 @@ namespace MediaBrowser.Controller.MediaEncoding hwScaleFilter += $":transpose={tranposeDir}"; } + if (!string.IsNullOrEmpty(hwScaleFilter) && doVppFullRangeOut) + { + hwScaleFilter += ":out_range=pc"; + } + if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap) { hwScaleFilter += doVppProcamp ? procampParams : (twoPassVppTonemap ? string.Empty : ":tonemap=1"); @@ -4200,7 +4212,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hw tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -4317,6 +4329,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isHwDecoder = isVaapiDecoder || isQsvDecoder; var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isQsvEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isQsvInQsvOut = isHwDecoder && isQsvEncoder; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); @@ -4375,6 +4388,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var hwFilterSuffix = isVaapiDecoder ? "vaapi" : "qsv"; var isRext = IsVideoStreamHevcRext(state); + var doVppFullRangeOut = isMjpegEncoder + && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption; // INPUT vaapi/qsv surface(vram) // hw deint @@ -4394,7 +4409,7 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = (doTonemap && isRext) ? "p010" : outFormat; var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH; - var hwScalePrefix = (isQsvDecoder && doVppTranspose) ? "vpp" : "scale"; + var hwScalePrefix = (isQsvDecoder && (doVppTranspose || doVppFullRangeOut)) ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!string.IsNullOrEmpty(hwScaleFilter) && isQsvDecoder && doVppTranspose) @@ -4402,6 +4417,13 @@ namespace MediaBrowser.Controller.MediaEncoding hwScaleFilter += $":transpose={tranposeDir}"; } + if (!string.IsNullOrEmpty(hwScaleFilter) + && ((isVaapiDecoder && isMjpegEncoder) + || (isQsvDecoder && doVppFullRangeOut))) + { + hwScaleFilter += ":out_range=pc"; + } + // allocate extra pool sizes for vaapi vpp scale if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder) { @@ -4422,7 +4444,7 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add("format=vaapi"); } - var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); if (isQsvDecoder) @@ -4442,7 +4464,7 @@ namespace MediaBrowser.Controller.MediaEncoding // ocl tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -4633,6 +4655,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isVaapiEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isVaInVaOut = isVaapiDecoder && isVaapiEncoder; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); @@ -4708,6 +4731,11 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doTonemap ? (isRext ? "p010" : string.Empty) : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + { + hwScaleFilter += ":out_range=pc"; + } + // allocate extra pool sizes for vaapi vpp if (!string.IsNullOrEmpty(hwScaleFilter)) { @@ -4721,7 +4749,7 @@ namespace MediaBrowser.Controller.MediaEncoding // vaapi vpp tonemap if (doVaVppTonemap && isVaapiDecoder) { - var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -4734,7 +4762,7 @@ namespace MediaBrowser.Controller.MediaEncoding // ocl tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -4858,6 +4886,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isVaapiEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); @@ -4953,6 +4982,12 @@ namespace MediaBrowser.Controller.MediaEncoding // hw scale var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", "nv12", false, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + { + hwScaleFilter += ":out_range=pc"; + } + mainFilters.Add(hwScaleFilter); } } @@ -4973,7 +5008,7 @@ namespace MediaBrowser.Controller.MediaEncoding // vk libplacebo if (doVkTonemap || hasSubs) { - var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, isMjpegEncoder); mainFilters.Add(libplaceboFilter); mainFilters.Add("format=vulkan"); } @@ -5088,6 +5123,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isSwDecoder = string.IsNullOrEmpty(vidDecoder); var isSwEncoder = !isVaapiEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isVaInVaOut = isVaapiDecoder && isVaapiEncoder; var isi965Driver = _mediaEncoder.IsVaapiDeviceInteli965; var isAmdDriver = _mediaEncoder.IsVaapiDeviceAmd; @@ -5149,6 +5185,11 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = doOclTonemap ? string.Empty : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + { + hwScaleFilter += ":out_range=pc"; + } + // allocate extra pool sizes for vaapi vpp if (!string.IsNullOrEmpty(hwScaleFilter)) { @@ -5177,7 +5218,7 @@ namespace MediaBrowser.Controller.MediaEncoding // ocl tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -5303,6 +5344,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var inW = state.VideoStream?.Width; var inH = state.VideoStream?.Height; @@ -5384,7 +5426,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Metal tonemap if (doMetalTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "videotoolbox", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "videotoolbox", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } @@ -5507,6 +5549,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase); var isSwDecoder = !isRkmppDecoder; var isSwEncoder = !isRkmppEncoder; + var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase); var isDrmInDrmOut = isRkmppDecoder && isRkmppEncoder; var isEncoderSupportAfbc = isRkmppEncoder && (vidEncoder.Contains("h264", StringComparison.OrdinalIgnoreCase) @@ -5573,7 +5616,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isFullAfbcPipeline = isEncoderSupportAfbc && isDrmInDrmOut && !doOclTonemap; var swapOutputWandH = doRkVppTranspose && swapWAndH; - var outFormat = doOclTonemap ? "p010" : "nv12"; + var outFormat = doOclTonemap ? "p010" : (isMjpegEncoder ? "bgra" : "nv12"); // RGA only support full range in rgb fmts var hwScaleFilter = GetHwScaleFilter("vpp", "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); var doScaling = GetHwScaleFilter("vpp", "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); @@ -5616,7 +5659,7 @@ namespace MediaBrowser.Controller.MediaEncoding // ocl tonemap if (doOclTonemap) { - var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder); mainFilters.Add(tonemapFilter); } -- cgit v1.2.3 From a0204ada2f97b73f932e1ec40befd6cfa75e8e16 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 23 Sep 2024 23:02:31 +0800 Subject: Fix intel Xe kernel driver cannot be used with QSV (#12691) --- .../MediaEncoding/EncodingHelper.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e9e8ed618..b05d2ffb7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegAlteredVaVkInterop = new Version(7, 0, 1); private readonly Version _minFFmpegQsvVppTonemapOption = new Version(7, 0, 1); private readonly Version _minFFmpegQsvVppOutRangeOption = new Version(7, 0, 1); + private readonly Version _minFFmpegVaapiDeviceVendorId = new Version(7, 0, 1); private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); @@ -872,13 +873,15 @@ namespace MediaBrowser.Controller.MediaEncoding options); } - private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string srcDeviceAlias, string alias) + private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string vendorId, string srcDeviceAlias, string alias) { alias ??= VaapiAlias; + var haveVendorId = !string.IsNullOrEmpty(vendorId) + && _mediaEncoder.EncoderVersion >= _minFFmpegVaapiDeviceVendorId; - // 'renderNodePath' has higher priority than 'kernelDriver' + // Priority: 'renderNodePath' > 'vendorId' > 'kernelDriver' var driverOpts = string.IsNullOrEmpty(renderNodePath) - ? (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver) + ? (haveVendorId ? $",vendor_id={vendorId}" : (string.IsNullOrEmpty(kernelDriver) ? string.Empty : $",kernel_driver={kernelDriver}")) : renderNodePath; // 'driver' behaves similarly to env LIBVA_DRIVER_NAME @@ -913,7 +916,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (OperatingSystem.IsLinux()) { // derive qsv from vaapi device - return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias; + return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", "0x8086", null, VaapiAlias) + arg + "@" + VaapiAlias; } if (OperatingSystem.IsWindows()) @@ -1008,14 +1011,14 @@ namespace MediaBrowser.Controller.MediaEncoding if (_mediaEncoder.IsVaapiDeviceInteliHD) { - args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, null, VaapiAlias)); + args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, null, null, VaapiAlias)); } else if (_mediaEncoder.IsVaapiDeviceInteli965) { // Only override i965 since it has lower priority than iHD in libva lookup. Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME", "i965"); Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME_JELLYFIN", "i965"); - args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, null, VaapiAlias)); + args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, null, null, VaapiAlias)); } var filterDevArgs = string.Empty; @@ -1039,7 +1042,7 @@ namespace MediaBrowser.Controller.MediaEncoding && Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier) { args.Append(GetDrmDeviceArgs(options.VaapiDevice, DrmAlias)); - args.Append(GetVaapiDeviceArgs(null, null, null, DrmAlias, VaapiAlias)); + args.Append(GetVaapiDeviceArgs(null, null, null, null, DrmAlias, VaapiAlias)); args.Append(GetVulkanDeviceArgs(0, null, DrmAlias, VulkanAlias)); // libplacebo wants an explicitly set vulkan filter device. @@ -1047,7 +1050,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else { - args.Append(GetVaapiDeviceArgs(options.VaapiDevice, null, null, null, VaapiAlias)); + args.Append(GetVaapiDeviceArgs(options.VaapiDevice, null, null, null, null, VaapiAlias)); filterDevArgs = GetFilterHwDeviceArgs(VaapiAlias); if (doOclTonemap) -- cgit v1.2.3 From 0539fdc5e3a164b694d12d8d3f8437e2cc5b6457 Mon Sep 17 00:00:00 2001 From: gnattu Date: Mon, 23 Sep 2024 23:09:08 +0800 Subject: Fix libx264/libx265 auto preset (#12692) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b05d2ffb7..557eb5e32 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1444,7 +1444,13 @@ namespace MediaBrowser.Controller.MediaEncoding var encoderPreset = preset ?? defaultPreset; if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265) { - param += " -preset " + encoderPreset.ToString().ToLowerInvariant(); + var presetString = encoderPreset switch + { + EncoderPreset.auto => EncoderPreset.veryfast.ToString().ToLowerInvariant(), + _ => encoderPreset.ToString().ToLowerInvariant() + }; + + param += " -preset " + presetString; int encodeCrf = encodingOptions.H264Crf; if (isLibX265) -- cgit v1.2.3 From cb8f01065a03a5ba546c1ff33c7452d81c32da62 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 24 Sep 2024 01:24:49 +0800 Subject: Fix MJPEG hwenc may be incorrectly enabled on AMDGPU (#12695) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 557eb5e32..7076ed087 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -209,6 +209,14 @@ namespace MediaBrowser.Controller.MediaEncoding { var hwType = encodingOptions.HardwareAccelerationType; + // Only Intel has VA-API MJPEG encoder + if (hwType == HardwareAccelerationType.vaapi + && !(_mediaEncoder.IsVaapiDeviceInteliHD + || _mediaEncoder.IsVaapiDeviceInteli965)) + { + return _defaultMjpegEncoder; + } + if (hwType != HardwareAccelerationType.none && encodingOptions.EnableHardwareEncoding && _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder) -- cgit v1.2.3 From e5f5f7c3843d8de0fd212d77332e9d20f614db59 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 28 Sep 2024 04:14:42 +0000 Subject: Fix the invalid libplacebo filter option (#12732) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 7076ed087..e5b9ad167 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3441,7 +3441,7 @@ namespace MediaBrowser.Controller.MediaEncoding algorithmString = algorithm.ToString().ToLowerInvariant(); } - tonemapArg = ":tonemapping=" + algorithm + ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709"; + tonemapArg = $":tonemapping={algorithmString}:peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709"; if (range == TonemappingRange.tv || range == TonemappingRange.pc) { -- cgit v1.2.3 From f52f5290b2e387edd9520197a75eda9c63fde7e8 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 28 Sep 2024 09:36:33 +0000 Subject: Fix vpp tonemap pixfmt regression (#12733) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e5b9ad167..c8deca609 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4422,9 +4422,7 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add($"transpose_vaapi=dir={tranposeDir}"); } - var outFormat = doOclTonemap ? ((isQsvDecoder && doVppTranspose) ? "p010" : string.Empty) : "nv12"; - outFormat = (doTonemap && isRext) ? "p010" : outFormat; - + var outFormat = doTonemap ? (((isQsvDecoder && doVppTranspose) || isRext) ? "p010" : string.Empty) : "nv12"; var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH; var hwScalePrefix = (isQsvDecoder && (doVppTranspose || doVppFullRangeOut)) ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); -- cgit v1.2.3 From 77420739e6303787109b0f7e81ea067f7cd6b9f1 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 30 Sep 2024 02:21:36 +0000 Subject: Handle full range output in tonemap filter for Trickplay (#12746) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c8deca609..73618e104 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4177,7 +4177,7 @@ namespace MediaBrowser.Controller.MediaEncoding hwScaleFilter += $":transpose={tranposeDir}"; } - if (!string.IsNullOrEmpty(hwScaleFilter) && doVppFullRangeOut) + if (!string.IsNullOrEmpty(hwScaleFilter) && doVppFullRangeOut && !doOclTonemap) { hwScaleFilter += ":out_range=pc"; } @@ -4433,6 +4433,7 @@ namespace MediaBrowser.Controller.MediaEncoding } if (!string.IsNullOrEmpty(hwScaleFilter) + && !doOclTonemap && ((isVaapiDecoder && isMjpegEncoder) || (isQsvDecoder && doVppFullRangeOut))) { @@ -4746,7 +4747,7 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doTonemap ? (isRext ? "p010" : string.Empty) : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); - if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doOclTonemap) { hwScaleFilter += ":out_range=pc"; } @@ -4998,7 +4999,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hw scale var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", "nv12", false, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doVkTonemap) { hwScaleFilter += ":out_range=pc"; } @@ -5200,7 +5201,7 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = doOclTonemap ? string.Empty : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doOclTonemap) { hwScaleFilter += ":out_range=pc"; } -- cgit v1.2.3 From f36e73e74864b85faafcb820f5cd7d4016c718ce Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 1 Oct 2024 13:01:33 +0000 Subject: Improve Trickplay image quality using HQ scaler on intel (#12759) --- .../MediaEncoding/EncodingHelper.cs | 35 ++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 73618e104..c90704c5e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -73,6 +73,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegQsvVppTonemapOption = new Version(7, 0, 1); private readonly Version _minFFmpegQsvVppOutRangeOption = new Version(7, 0, 1); private readonly Version _minFFmpegVaapiDeviceVendorId = new Version(7, 0, 1); + private readonly Version _minFFmpegQsvVppScaleModeOption = new Version(6, 0); private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); @@ -4143,6 +4144,8 @@ namespace MediaBrowser.Controller.MediaEncoding var twoPassVppTonemap = isRext; var doVppFullRangeOut = isMjpegEncoder && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption; + var doVppScaleModeHq = isMjpegEncoder + && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppScaleModeOption; var doVppProcamp = false; var procampParams = string.Empty; if (doVppTonemap) @@ -4169,17 +4172,17 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = twoPassVppTonemap ? "p010" : outFormat; var swapOutputWandH = doVppTranspose && swapWAndH; - var hwScalePrefix = (doVppTranspose || doVppTonemap || doVppFullRangeOut) ? "vpp" : "scale"; - var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); + var hwScaleFilter = GetHwScaleFilter("vpp", "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTranspose) { hwScaleFilter += $":transpose={tranposeDir}"; } - if (!string.IsNullOrEmpty(hwScaleFilter) && doVppFullRangeOut && !doOclTonemap) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) { - hwScaleFilter += ":out_range=pc"; + hwScaleFilter += (doVppFullRangeOut && !doOclTonemap) ? ":out_range=pc" : string.Empty; + hwScaleFilter += doVppScaleModeHq ? ":scale_mode=hq" : string.Empty; } if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap) @@ -4407,6 +4410,8 @@ namespace MediaBrowser.Controller.MediaEncoding var isRext = IsVideoStreamHevcRext(state); var doVppFullRangeOut = isMjpegEncoder && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption; + var doVppScaleModeHq = isMjpegEncoder + && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppScaleModeOption; // INPUT vaapi/qsv surface(vram) // hw deint @@ -4424,7 +4429,7 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doTonemap ? (((isQsvDecoder && doVppTranspose) || isRext) ? "p010" : string.Empty) : "nv12"; var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH; - var hwScalePrefix = (isQsvDecoder && (doVppTranspose || doVppFullRangeOut)) ? "vpp" : "scale"; + var hwScalePrefix = isQsvDecoder ? "vpp" : "scale"; var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); if (!string.IsNullOrEmpty(hwScaleFilter) && isQsvDecoder && doVppTranspose) @@ -4432,12 +4437,10 @@ namespace MediaBrowser.Controller.MediaEncoding hwScaleFilter += $":transpose={tranposeDir}"; } - if (!string.IsNullOrEmpty(hwScaleFilter) - && !doOclTonemap - && ((isVaapiDecoder && isMjpegEncoder) - || (isQsvDecoder && doVppFullRangeOut))) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) { - hwScaleFilter += ":out_range=pc"; + hwScaleFilter += ((isQsvDecoder && !doVppFullRangeOut) || doOclTonemap) ? string.Empty : ":out_range=pc"; + hwScaleFilter += isQsvDecoder ? (doVppScaleModeHq ? ":scale_mode=hq" : string.Empty) : ":mode=hq"; } // allocate extra pool sizes for vaapi vpp scale @@ -4747,9 +4750,10 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doTonemap ? (isRext ? "p010" : string.Empty) : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH); - if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doOclTonemap) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) { - hwScaleFilter += ":out_range=pc"; + hwScaleFilter += doOclTonemap ? string.Empty : ":out_range=pc"; + hwScaleFilter += ":mode=hq"; } // allocate extra pool sizes for vaapi vpp @@ -5001,7 +5005,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doVkTonemap) { - hwScaleFilter += ":out_range=pc"; + hwScaleFilter += ":out_range=pc:mode=hq"; } mainFilters.Add(hwScaleFilter); @@ -5201,9 +5205,10 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = doOclTonemap ? string.Empty : "nv12"; var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doOclTonemap) + if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder) { - hwScaleFilter += ":out_range=pc"; + hwScaleFilter += doOclTonemap ? string.Empty : ":out_range=pc"; + hwScaleFilter += ":mode=hq"; } // allocate extra pool sizes for vaapi vpp -- cgit v1.2.3 From 431770255914161ad6a71b93eea2473d18a1daf2 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 1 Oct 2024 13:02:01 +0000 Subject: Fix RKMPP-DRM-OpenCL mapping (#12758) --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c90704c5e..a29d41426 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5674,7 +5674,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (doOclTonemap && isRkmppDecoder) { // map from rkmpp/drm to opencl via drm-opencl interop. - mainFilters.Add("hwmap=derive_device=opencl:mode=read"); + mainFilters.Add("hwmap=derive_device=opencl"); } // ocl tonemap @@ -5717,7 +5717,7 @@ namespace MediaBrowser.Controller.MediaEncoding { // OUTPUT drm(nv12) surface(gem/dma-heap) // reverse-mapping via drm-opencl interop. - mainFilters.Add("hwmap=derive_device=rkmpp:mode=write:reverse=1"); + mainFilters.Add("hwmap=derive_device=rkmpp:reverse=1"); mainFilters.Add("format=drm_prime"); } } -- cgit v1.2.3 From 368edeb8ad23f85c5146daae2d7059acde69bb74 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Wed, 2 Oct 2024 16:34:40 +0000 Subject: Fix full range in SW decoder & HW encoder for Trickplay (#12763) --- .../MediaEncoding/EncodingHelper.cs | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a29d41426..ae33da1fb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4126,6 +4126,12 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12"); var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + if (isMjpegEncoder && !doOclTonemap) + { + // sw decoder + hw mjpeg encoder + swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc"; + } + // sw scale mainFilters.Add(swScaleFilter); mainFilters.Add($"format={outFormat}"); @@ -4392,6 +4398,12 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12"); var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + if (isMjpegEncoder && !doOclTonemap) + { + // sw decoder + hw mjpeg encoder + swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc"; + } + // sw scale mainFilters.Add(swScaleFilter); mainFilters.Add($"format={outFormat}"); @@ -4717,6 +4729,12 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doOclTonemap ? "yuv420p10le" : "nv12"; var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + if (isMjpegEncoder && !doOclTonemap) + { + // sw decoder + hw mjpeg encoder + swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc"; + } + // sw scale mainFilters.Add(swScaleFilter); mainFilters.Add($"format={outFormat}"); @@ -5180,6 +5198,12 @@ namespace MediaBrowser.Controller.MediaEncoding outFormat = doOclTonemap ? "yuv420p10le" : "nv12"; var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + if (isMjpegEncoder && !doOclTonemap) + { + // sw decoder + hw mjpeg encoder + swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc"; + } + // sw scale mainFilters.Add(swScaleFilter); mainFilters.Add("format=" + outFormat); @@ -5614,6 +5638,12 @@ namespace MediaBrowser.Controller.MediaEncoding var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12"); var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + if (isMjpegEncoder && !doOclTonemap) + { + // sw decoder + hw mjpeg encoder + swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc"; + } + if (!string.IsNullOrEmpty(swScaleFilter)) { swScaleFilter += ":flags=fast_bilinear"; -- cgit v1.2.3 From b496f979f02c6b65a2a6f1887558d8cbd9d8d7b6 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 3 Oct 2024 14:18:40 +0000 Subject: Clean up deprecated -vsync option (#12765) --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 32 ++++++++++++++++++++-- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- src/Jellyfin.LiveTv/IO/EncodedRecorder.cs | 4 +-- 4 files changed, 34 insertions(+), 6 deletions(-) (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs') diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 48fda471a..54e0527c9 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1908,7 +1908,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (!string.IsNullOrEmpty(state.OutputVideoSync)) { - args += " -vsync " + state.OutputVideoSync; + args += EncodingHelper.GetVideoSyncOption(state.OutputVideoSync, _mediaEncoder.EncoderVersion); } args += _encodingHelper.GetOutputFFlags(state); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ae33da1fb..c120e08fa 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -6857,7 +6857,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(state.InputVideoSync)) { - inputModifier += " -vsync " + state.InputVideoSync; + inputModifier += GetVideoSyncOption(state.InputVideoSync, _mediaEncoder.EncoderVersion); } if (state.ReadInputAtNativeFramerate && state.InputProtocol != MediaProtocol.Rtsp) @@ -7280,7 +7280,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(state.OutputVideoSync)) { - args += " -vsync " + state.OutputVideoSync; + args += GetVideoSyncOption(state.OutputVideoSync, _mediaEncoder.EncoderVersion); } args += GetOutputFFlags(state); @@ -7453,5 +7453,33 @@ namespace MediaBrowser.Controller.MediaEncoding return state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode || (state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding && !IsCopyCodec(state.OutputVideoCodec)); } + + public static string GetVideoSyncOption(string videoSync, Version encoderVersion) + { + if (string.IsNullOrEmpty(videoSync)) + { + return string.Empty; + } + + if (encoderVersion >= new Version(5, 1)) + { + if (int.TryParse(videoSync, CultureInfo.InvariantCulture, out var vsync)) + { + return vsync switch + { + -1 => " -fps_mode auto", + 0 => " -fps_mode passthrough", + 1 => " -fps_mode cfr", + 2 => " -fps_mode vfr", + _ => string.Empty + }; + } + + return string.Empty; + } + + // -vsync is deprecated in FFmpeg 5.1 and will be removed in the future. + return $" -vsync {videoSync}"; + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index ace8bda19..ec0401576 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -945,7 +945,7 @@ namespace MediaBrowser.MediaEncoding.Encoder vidEncoder, encoderQualityOption + encoderQuality + " ", vidEncoder.Contains("videotoolbox", StringComparison.InvariantCultureIgnoreCase) ? "-allow_sw 1 " : string.Empty, // allow_sw fallback for some intel macs - EncoderVersion >= new Version(5, 1) ? "-fps_mode passthrough " : "-vsync passthrough ", // passthrough timestamp + EncodingHelper.GetVideoSyncOption("0", EncoderVersion).Trim() + " ", // passthrough timestamp "image2", outputPath); diff --git a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs index ff00c8999..0c660637f 100644 --- a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs +++ b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs @@ -130,7 +130,7 @@ namespace Jellyfin.LiveTv.IO const int MaxBitrate = 25000000; videoArgs = string.Format( CultureInfo.InvariantCulture, - "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41", + "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -profile:v high -level 41", GetOutputSizeParam(), MaxBitrate); } @@ -157,7 +157,7 @@ namespace Jellyfin.LiveTv.IO flags.Add("+genpts"); } - var inputModifier = "-async 1 -vsync -1"; + var inputModifier = "-async 1"; if (flags.Count > 0) { -- cgit v1.2.3