From 44cb9f5fddce7430a242961ba0d89111b75e2e8f Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 15 Feb 2024 21:52:41 +0800 Subject: [PATCH 01/16] feat: add hw scale filter for videotoolbox Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 22 +++++++++++++++---- .../Encoder/EncoderValidator.cs | 1 + 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bb867aba30..9c0beceba8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2954,6 +2954,13 @@ namespace MediaBrowser.Controller.MediaEncoding { arg2 = (isSizeFixed ? ':' : '=') + arg2; } + if (string.Equals(hwScaleSuffix, "vt", StringComparison.OrdinalIgnoreCase)) + { + // VideoToolBox scaling filter requires different syntax + arg1 = isSizeFixed ? ("=" + outWidth.Value + ":" + outHeight.Value) : string.Empty; + // VideoToolBox does format conversion automatically + arg2 = string.Empty; + } if (!string.IsNullOrEmpty(hwScaleSuffix) && (isSizeFixed || isFormatFixed)) { @@ -4980,6 +4987,7 @@ namespace MediaBrowser.Controller.MediaEncoding var newfilters = new List(); var noOverlay = swFilterChain.OverlayFilters.Count == 0; var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); + var supportsHwScale = _mediaEncoder.SupportsFilter("scale_vt"); // fallback to software filters if we are using filters not supported by hardware yet. var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); @@ -4988,14 +4996,20 @@ namespace MediaBrowser.Controller.MediaEncoding return swFilterChain; } - // ffmpeg cannot use videotoolbox to scale - var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); - newfilters.Add(swScaleFilter); - + if (!supportsHwScale) + { + var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); + newfilters.Add(swScaleFilter); + } // hwupload on videotoolbox encoders can automatically convert AVFrame into its CVPixelBuffer equivalent // videotoolbox will automatically convert the CVPixelBuffer to a pixel format the encoder supports, so we don't have to set a pixel format explicitly here // This will reduce CPU usage significantly on UHD videos with 10 bit colors because we bypassed the ffmpeg pixel format conversion newfilters.Add("hwupload"); + if (supportsHwScale) + { + var hwScaleFilter = GetHwScaleFilter("vt", "", inW, inH, reqW, reqH, reqMaxW, reqMaxH); + newfilters.Add(hwScaleFilter); + } if (doDeintH2645) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index fdca283908..fae9656cfd 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -128,6 +128,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "overlay_vulkan", // videotoolbox "yadif_videotoolbox", + "scale_vt", // rkrga "scale_rkrga", "vpp_rkrga", From c18ef13b3b9d23e486322b76a5f0b59aba9e0467 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 15 Feb 2024 23:00:51 +0800 Subject: [PATCH 02/16] feat: add tone mapping for videotoolbox Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9c0beceba8..19fe9ccd77 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -272,7 +272,8 @@ namespace MediaBrowser.Controller.MediaEncoding var isNvdecDecoder = vidDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase); var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isD3d11vaDecoder = vidDecoder.Contains("d3d11va", StringComparison.OrdinalIgnoreCase); - return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder; + var isVideoToolBoxDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder || isVideoToolBoxDecoder; } return state.VideoStream.VideoRange == VideoRange.HDR @@ -4988,6 +4989,8 @@ namespace MediaBrowser.Controller.MediaEncoding var noOverlay = swFilterChain.OverlayFilters.Count == 0; var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); var supportsHwScale = _mediaEncoder.SupportsFilter("scale_vt"); + // VideoToolbox is special. It does not use a separate tone mapping filter like others. Instead, it performs both tone mapping and scaling in a single filter. + var useHwToneMapping = IsHwTonemapAvailable(state, options) && supportsHwScale; // fallback to software filters if we are using filters not supported by hardware yet. var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); @@ -5008,6 +5011,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (supportsHwScale) { var hwScaleFilter = GetHwScaleFilter("vt", "", inW, inH, reqW, reqH, reqMaxW, reqMaxH); + if (useHwToneMapping) + { + hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) ? "scale_vt=0:0:bt709:bt709:bt709" + : string.Format(CultureInfo.InvariantCulture, hwScaleFilter, ":bt709:bt709:bt709"); + } newfilters.Add(hwScaleFilter); } From cf3e3e2c3d3cb5255de6f8f261ee03ead9e1357f Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 15 Feb 2024 23:22:32 +0800 Subject: [PATCH 03/16] fix: code style Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 19fe9ccd77..d23eb00fea 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2955,6 +2955,7 @@ namespace MediaBrowser.Controller.MediaEncoding { arg2 = (isSizeFixed ? ':' : '=') + arg2; } + if (string.Equals(hwScaleSuffix, "vt", StringComparison.OrdinalIgnoreCase)) { // VideoToolBox scaling filter requires different syntax @@ -5004,18 +5005,20 @@ namespace MediaBrowser.Controller.MediaEncoding var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); newfilters.Add(swScaleFilter); } + // hwupload on videotoolbox encoders can automatically convert AVFrame into its CVPixelBuffer equivalent // videotoolbox will automatically convert the CVPixelBuffer to a pixel format the encoder supports, so we don't have to set a pixel format explicitly here // This will reduce CPU usage significantly on UHD videos with 10 bit colors because we bypassed the ffmpeg pixel format conversion newfilters.Add("hwupload"); if (supportsHwScale) { - var hwScaleFilter = GetHwScaleFilter("vt", "", inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var hwScaleFilter = GetHwScaleFilter("vt", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH); if (useHwToneMapping) { hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) ? "scale_vt=0:0:bt709:bt709:bt709" : string.Format(CultureInfo.InvariantCulture, hwScaleFilter, ":bt709:bt709:bt709"); } + newfilters.Add(hwScaleFilter); } From 5c743f2b4d242b9b9ea438b2331439f58a92c447 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 16 Feb 2024 00:16:59 +0800 Subject: [PATCH 04/16] feat: separate videotoolbox tone mapping option Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 14 +++++++++++++- .../Configuration/EncodingOptions.cs | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index d23eb00fea..62799f01e1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -309,6 +309,18 @@ namespace MediaBrowser.Controller.MediaEncoding && state.VideoStream.VideoRangeType == VideoRangeType.HDR10; } + private bool IsVideoToolboxVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options) + { + if (state.VideoStream is null + || !options.EnableVideoToolboxTonemapping + || GetVideoColorBitDepth(state) != 10) + { + return false; + } + return state.VideoStream.VideoRange == VideoRange.HDR + && state.VideoStream.VideoRangeType == VideoRangeType.HDR10; + } + /// /// Gets the name of the output video codec. /// @@ -4991,7 +5003,7 @@ namespace MediaBrowser.Controller.MediaEncoding var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); var supportsHwScale = _mediaEncoder.SupportsFilter("scale_vt"); // VideoToolbox is special. It does not use a separate tone mapping filter like others. Instead, it performs both tone mapping and scaling in a single filter. - var useHwToneMapping = IsHwTonemapAvailable(state, options) && supportsHwScale; + var useHwToneMapping = IsVideoToolboxVppTonemapAvailable(state, options) && supportsHwScale; // fallback to software filters if we are using filters not supported by hardware yet. var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 84c735f9ca..13ebebded4 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -146,6 +146,11 @@ public class EncodingOptions /// public bool EnableVppTonemapping { get; set; } + /// + /// Gets or sets a value indicating whether videotoolbox tonemapping is enabled. + /// + public bool EnableVideoToolboxTonemapping { get; set; } + /// /// Gets or sets the tone-mapping algorithm. /// From 0a4457dd68329a1100d26a1758baef360ca3577e Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 16 Feb 2024 00:18:19 +0800 Subject: [PATCH 05/16] fix: typo Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 62799f01e1..2ffebfa542 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -309,7 +309,7 @@ namespace MediaBrowser.Controller.MediaEncoding && state.VideoStream.VideoRangeType == VideoRangeType.HDR10; } - private bool IsVideoToolboxVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options) + private bool IsVideoToolboxTonemapAvailable(EncodingJobInfo state, EncodingOptions options) { if (state.VideoStream is null || !options.EnableVideoToolboxTonemapping @@ -5003,7 +5003,7 @@ namespace MediaBrowser.Controller.MediaEncoding var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); var supportsHwScale = _mediaEncoder.SupportsFilter("scale_vt"); // VideoToolbox is special. It does not use a separate tone mapping filter like others. Instead, it performs both tone mapping and scaling in a single filter. - var useHwToneMapping = IsVideoToolboxVppTonemapAvailable(state, options) && supportsHwScale; + var useHwToneMapping = IsVideoToolboxTonemapAvailable(state, options) && supportsHwScale; // fallback to software filters if we are using filters not supported by hardware yet. var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); From 2f3e5cfa06bf0a59dcb1b651c9d9f5b1ba9849ed Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 16 Feb 2024 01:08:17 +0800 Subject: [PATCH 06/16] fix: correctly set the supported formats of videotoolbox tone mapping Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2ffebfa542..c3d5e58471 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -272,8 +272,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isNvdecDecoder = vidDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase); var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isD3d11vaDecoder = vidDecoder.Contains("d3d11va", StringComparison.OrdinalIgnoreCase); - var isVideoToolBoxDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); - return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder || isVideoToolBoxDecoder; + return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder; } return state.VideoStream.VideoRange == VideoRange.HDR @@ -317,8 +316,11 @@ namespace MediaBrowser.Controller.MediaEncoding { return false; } + + // Certain DV profile 5 video works in Safari with direct playing, but the VideoToolBox does not produce correct mapping results with trascoding. + // All other HDR formats working. return state.VideoStream.VideoRange == VideoRange.HDR - && state.VideoStream.VideoRangeType == VideoRangeType.HDR10; + && state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.HDR10Plus; } /// @@ -5025,7 +5027,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (supportsHwScale) { var hwScaleFilter = GetHwScaleFilter("vt", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (useHwToneMapping) + if (true) { hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) ? "scale_vt=0:0:bt709:bt709:bt709" : string.Format(CultureInfo.InvariantCulture, hwScaleFilter, ":bt709:bt709:bt709"); From 0a8560f64acc37286eead442a1c11b74796bd4bf Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 16 Feb 2024 01:30:12 +0800 Subject: [PATCH 07/16] fix: use hardware filter option name explicitly Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c3d5e58471..b5e45317b8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2970,14 +2970,6 @@ namespace MediaBrowser.Controller.MediaEncoding arg2 = (isSizeFixed ? ':' : '=') + arg2; } - if (string.Equals(hwScaleSuffix, "vt", StringComparison.OrdinalIgnoreCase)) - { - // VideoToolBox scaling filter requires different syntax - arg1 = isSizeFixed ? ("=" + outWidth.Value + ":" + outHeight.Value) : string.Empty; - // VideoToolBox does format conversion automatically - arg2 = string.Empty; - } - if (!string.IsNullOrEmpty(hwScaleSuffix) && (isSizeFixed || isFormatFixed)) { return string.Format( @@ -5026,11 +5018,13 @@ namespace MediaBrowser.Controller.MediaEncoding newfilters.Add("hwupload"); if (supportsHwScale) { - var hwScaleFilter = GetHwScaleFilter("vt", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (true) + var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + if (useHwToneMapping) { - hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) ? "scale_vt=0:0:bt709:bt709:bt709" - : string.Format(CultureInfo.InvariantCulture, hwScaleFilter, ":bt709:bt709:bt709"); + var tonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; + hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) + ? "scale_vt=" + tonemapArgs + : hwScaleFilter + ":" + tonemapArgs; } newfilters.Add(hwScaleFilter); From 21bf557145204f87cca26116857d25ff6c742209 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 16 Feb 2024 01:43:40 +0800 Subject: [PATCH 08/16] fix: use hardware videotoolbox filter even only scale is available Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index b5e45317b8..f5873c7ca9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4999,7 +4999,7 @@ namespace MediaBrowser.Controller.MediaEncoding // VideoToolbox is special. It does not use a separate tone mapping filter like others. Instead, it performs both tone mapping and scaling in a single filter. var useHwToneMapping = IsVideoToolboxTonemapAvailable(state, options) && supportsHwScale; // fallback to software filters if we are using filters not supported by hardware yet. - var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint); + var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint || supportsHwScale); if (!useHardwareFilters) { From 1cb7264f0d568ef7c093265e760d5418db291194 Mon Sep 17 00:00:00 2001 From: gnattu Date: Wed, 28 Feb 2024 17:56:59 +0800 Subject: [PATCH 09/16] feat: fully support videotoolbox hardware filters Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 149 +++++++++++++----- .../Encoder/EncoderValidator.cs | 1 + 2 files changed, 109 insertions(+), 41 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index f5873c7ca9..20a465ba98 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -253,6 +253,14 @@ namespace MediaBrowser.Controller.MediaEncoding && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync); } + private bool IsVideoToolBoxFullSupported() + { + return _mediaEncoder.SupportsHwaccel("videotoolbox") + && _mediaEncoder.SupportsFilter("yadif_videotoolbox") + && _mediaEncoder.SupportsFilter("overlay_videotoolbox") + && _mediaEncoder.SupportsFilter("scale_vt"); + } + private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options) { if (state.VideoStream is null @@ -272,7 +280,8 @@ namespace MediaBrowser.Controller.MediaEncoding var isNvdecDecoder = vidDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase); var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); var isD3d11vaDecoder = vidDecoder.Contains("d3d11va", StringComparison.OrdinalIgnoreCase); - return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder; + var isVideoToolBoxDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder || isVideoToolBoxDecoder; } return state.VideoStream.VideoRange == VideoRange.HDR @@ -317,7 +326,7 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - // Certain DV profile 5 video works in Safari with direct playing, but the VideoToolBox does not produce correct mapping results with trascoding. + // Certain DV profile 5 video works in Safari with direct playing, but the VideoToolBox does not produce correct mapping results with transcoding. // All other HDR formats working. return state.VideoStream.VideoRange == VideoRange.HDR && state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.HDR10Plus; @@ -4976,12 +4985,6 @@ namespace MediaBrowser.Controller.MediaEncoding return swFilterChain; } - if (_mediaEncoder.EncoderVersion.CompareTo(new Version("5.0.0")) < 0) - { - // All features used here requires ffmpeg 5.0 or later, fallback to software filters if using an old ffmpeg - return swFilterChain; - } - var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintH2645 = doDeintH264 || doDeintHevc; @@ -4991,52 +4994,110 @@ namespace MediaBrowser.Controller.MediaEncoding var reqH = state.BaseRequest.Height; var reqMaxW = state.BaseRequest.MaxWidth; var reqMaxH = state.BaseRequest.MaxHeight; - var threeDFormat = state.MediaSource.Video3DFormat; - var newfilters = new List(); - var noOverlay = swFilterChain.OverlayFilters.Count == 0; - var supportsHwDeint = _mediaEncoder.SupportsFilter("yadif_videotoolbox"); - var supportsHwScale = _mediaEncoder.SupportsFilter("scale_vt"); - // VideoToolbox is special. It does not use a separate tone mapping filter like others. Instead, it performs both tone mapping and scaling in a single filter. - var useHwToneMapping = IsVideoToolboxTonemapAvailable(state, options) && supportsHwScale; - // fallback to software filters if we are using filters not supported by hardware yet. - var useHardwareFilters = noOverlay && (!doDeintH2645 || supportsHwDeint || supportsHwScale); + var mainFilters = new List(); + var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; + var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; + var hasAssSubs = hasSubs + && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); + // VideoToolbox is special. It does not use a separate tone mapping filter like others. + // Instead, it performs both tone mapping and scaling in a single filter. + var useVtToneMapping = IsVideoToolboxTonemapAvailable(state, options); + // Use OpenCL tone mapping as a fallback + var useOclToneMapping = !useVtToneMapping && IsHwTonemapAvailable(state, options); + // Fallback to software filters if we are using filters not supported by hardware yet. + // OpenCL won't work without proper VT support as the hwmap interop required will not be present there + var useHardwareFilters = IsVideoToolBoxFullSupported(); if (!useHardwareFilters) { return swFilterChain; } - if (!supportsHwScale) + if (!(useVtToneMapping || useOclToneMapping || hasSubs || doDeintH2645)) { - var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); - newfilters.Add(swScaleFilter); + // Dummy action to return empty filters when nothing to do. + return (mainFilters, mainFilters, mainFilters); } - // hwupload on videotoolbox encoders can automatically convert AVFrame into its CVPixelBuffer equivalent - // videotoolbox will automatically convert the CVPixelBuffer to a pixel format the encoder supports, so we don't have to set a pixel format explicitly here - // This will reduce CPU usage significantly on UHD videos with 10 bit colors because we bypassed the ffmpeg pixel format conversion - newfilters.Add("hwupload"); - if (supportsHwScale) + // Override the color when doing OpenCL Tone mapping, where we are using hardware surface output. + if (useOclToneMapping) { - var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (useHwToneMapping) - { - var tonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; - hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) - ? "scale_vt=" + tonemapArgs - : hwScaleFilter + ":" + tonemapArgs; - } + mainFilters.Add(GetOverwriteColorPropertiesParam(state, true)); + } + // With OpenCL tone mapping, we are using native hwmap and there's no need to specify a format for the main stream. + // However, for other cases, we have to specify the format for the main stream when we are doing subtitle burn-in. + // This is because the default upload option is not always processable with VideoToolbox. + // Most notably, yuv420p should be replaced by nv12. + else if (hasSubs) + { + var is8Bit = string.Equals("yuv420p", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is10Bit = string.Equals("yuv420p10le", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); - newfilters.Add(hwScaleFilter); + if (is8Bit) + { + mainFilters.Add("format=nv12"); + } else if (is10Bit) + { + mainFilters.Add("format=p010"); + } + } + + mainFilters.Add("hwupload"); + var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + if (useVtToneMapping) + { + const string TonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; + hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) + ? "scale_vt=" + TonemapArgs + : hwScaleFilter + ":" + TonemapArgs; + } + + mainFilters.Add(hwScaleFilter); + + if (useOclToneMapping) + { + mainFilters.Add("hwmap=derive_device=opencl"); + mainFilters.Add(GetHwTonemapFilter(options, "opencl", "nv12")); + mainFilters.Add("hwmap=derive_device=videotoolbox:reverse=1"); } if (doDeintH2645) { var deintFilter = GetHwDeinterlaceFilter(state, options, "videotoolbox"); - newfilters.Add(deintFilter); + mainFilters.Add(deintFilter); } - return (newfilters, swFilterChain.SubFilters, swFilterChain.OverlayFilters); + var subFilters = new List(); + var overlayFilters = new List(); + + if (hasSubs) + { + if (hasGraphicalSubs) + { + var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + subFilters.Add(subPreProcFilters); + subFilters.Add("format=bgra"); + } + else if (hasTextSubs) + { + var framerate = state.VideoStream?.RealFrameRate; + var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10; + + var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate); + var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true); + subFilters.Add(alphaSrcFilter); + subFilters.Add("format=bgra"); + subFilters.Add(subTextSubtitlesFilter); + } + + subFilters.Add("hwupload=derive_device=videotoolbox"); + overlayFilters.Add("overlay_videotoolbox=eof_action=pass:repeatlast=0"); + } + + return (mainFilters, subFilters, overlayFilters); } /// @@ -6028,22 +6089,28 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + // Hardware surface only make sense when interop with OpenCL + // VideoToolbox's Hardware surface in ffmpeg is not only slower than hwupload, but also breaks HDR in many cases. + // For example: https://trac.ffmpeg.org/ticket/10884 + var useOclToneMapping = !IsVideoToolboxTonemapAvailable(state, options) && IsHwTonemapAvailable(state, options); + var useHwSurface = useOclToneMapping && IsVideoToolBoxFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); + if (is8bitSwFormatsVt) { if (string.Equals("avc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) || string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "h264", bitDepth, false); + return GetHwaccelType(state, options, "h264", bitDepth, useHwSurface); } if (string.Equals("mpeg2video", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "mpeg2video", bitDepth, false); + return GetHwaccelType(state, options, "mpeg2video", bitDepth, useHwSurface); } if (string.Equals("mpeg4", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "mpeg4", bitDepth, false); + return GetHwaccelType(state, options, "mpeg4", bitDepth, useHwSurface); } } @@ -6052,12 +6119,12 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase) || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "hevc", bitDepth, false); + return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface); } if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) { - return GetHwaccelType(state, options, "vp9", bitDepth, false); + return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface); } } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index fae9656cfd..6549125d36 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -129,6 +129,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // videotoolbox "yadif_videotoolbox", "scale_vt", + "overlay_videotoolbox", // rkrga "scale_rkrga", "vpp_rkrga", From ec896a901c269ad4cebc2705f64339bbf0c19bf7 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 29 Feb 2024 05:37:31 +0800 Subject: [PATCH 10/16] fix: code style Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 20a465ba98..202cfd5b74 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5026,6 +5026,7 @@ namespace MediaBrowser.Controller.MediaEncoding { mainFilters.Add(GetOverwriteColorPropertiesParam(state, true)); } + // With OpenCL tone mapping, we are using native hwmap and there's no need to specify a format for the main stream. // However, for other cases, we have to specify the format for the main stream when we are doing subtitle burn-in. // This is because the default upload option is not always processable with VideoToolbox. @@ -5039,7 +5040,8 @@ namespace MediaBrowser.Controller.MediaEncoding if (is8Bit) { mainFilters.Add("format=nv12"); - } else if (is10Bit) + } + else if (is10Bit) { mainFilters.Add("format=p010"); } From a30dc81b28aae64ca4e28930819bc0c4f682c7b3 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 29 Feb 2024 09:03:00 +0800 Subject: [PATCH 11/16] fix: stack overflow Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 +++- MediaBrowser.Model/Configuration/EncodingOptions.cs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 202cfd5b74..0fc27f9f79 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -6094,7 +6094,9 @@ namespace MediaBrowser.Controller.MediaEncoding // Hardware surface only make sense when interop with OpenCL // VideoToolbox's Hardware surface in ffmpeg is not only slower than hwupload, but also breaks HDR in many cases. // For example: https://trac.ffmpeg.org/ticket/10884 - var useOclToneMapping = !IsVideoToolboxTonemapAvailable(state, options) && IsHwTonemapAvailable(state, options); + var useOclToneMapping = !IsVideoToolboxTonemapAvailable(state, options) && + options.EnableTonemapping && + state.VideoStream.VideoRangeType == VideoRangeType.DOVI; var useHwSurface = useOclToneMapping && IsVideoToolBoxFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); if (is8bitSwFormatsVt) diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 13ebebded4..ab6f0d867c 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -28,6 +28,7 @@ public class EncodingOptions VaapiDevice = "/dev/dri/renderD128"; EnableTonemapping = false; EnableVppTonemapping = false; + EnableVideoToolboxTonemapping = false; TonemappingAlgorithm = "bt2390"; TonemappingMode = "auto"; TonemappingRange = "auto"; From f31549cc0de0973e69b052718252bae6a2a45522 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 8 Mar 2024 23:23:24 +0800 Subject: [PATCH 12/16] fix: code clean up Co-authored-by: nyanmisaka Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 157 ++++++++++-------- 1 file changed, 91 insertions(+), 66 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 0fc27f9f79..1ac40c7184 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -253,7 +253,7 @@ namespace MediaBrowser.Controller.MediaEncoding && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync); } - private bool IsVideoToolBoxFullSupported() + private bool IsVideoToolboxFullSupported() { return _mediaEncoder.SupportsHwaccel("videotoolbox") && _mediaEncoder.SupportsFilter("yadif_videotoolbox") @@ -4978,100 +4978,118 @@ namespace MediaBrowser.Controller.MediaEncoding return (null, null, null); } - var swFilterChain = GetSwVidFilterChain(state, options, vidEncoder); + var isMacOS = OperatingSystem.IsMacOS(); + var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty; + var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + var isVtFullSupported = isMacOS && IsVideoToolboxFullSupported(); + var isVtOclSupported = isVtFullSupported && IsOpenclFullSupported(); - if (!options.EnableHardwareEncoding) + // legacy videotoolbox pipeline (disable hw filters) + if (!isVtEncoder + || !isVtOclSupported + || !_mediaEncoder.SupportsFilter("alphasrc")) { - return swFilterChain; + return GetSwVidFilterChain(state, options, vidEncoder); } - var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); - var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); - var doDeintH2645 = doDeintH264 || doDeintHevc; + // preferred videotoolbox + vt/ocl filters pipeline + return GetAppleVidFiltersPreferred(state, options, vidDecoder, vidEncoder); + } + + public (List MainFilters, List SubFilters, List OverlayFilters) GetAppleVidFiltersPreferred( + EncodingJobInfo state, + EncodingOptions options, + string vidDecoder, + string vidEncoder) + { var inW = state.VideoStream?.Width; var inH = state.VideoStream?.Height; var reqW = state.BaseRequest.Width; var reqH = state.BaseRequest.Height; var reqMaxW = state.BaseRequest.MaxWidth; var reqMaxH = state.BaseRequest.MaxHeight; - var mainFilters = new List(); + var threeDFormat = state.MediaSource.Video3DFormat; + + var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + + var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); + var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); + var doDeintH2645 = doDeintH264 || doDeintHevc; + var doVtTonemap = IsVideoToolboxTonemapAvailable(state, options); + var doOclTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); + var doTonemap = doVtTonemap || doOclTonemap; + var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; var hasAssSubs = hasSubs - && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); - // VideoToolbox is special. It does not use a separate tone mapping filter like others. - // Instead, it performs both tone mapping and scaling in a single filter. - var useVtToneMapping = IsVideoToolboxTonemapAvailable(state, options); - // Use OpenCL tone mapping as a fallback - var useOclToneMapping = !useVtToneMapping && IsHwTonemapAvailable(state, options); - // Fallback to software filters if we are using filters not supported by hardware yet. - // OpenCL won't work without proper VT support as the hwmap interop required will not be present there - var useHardwareFilters = IsVideoToolBoxFullSupported(); + && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); - if (!useHardwareFilters) + // FIXME: scale_vt lacks of format option for the time being. + // hwdownload/hwmap to sw requires setting a format explicitly. + if (!isVtEncoder) { - return swFilterChain; + // should not happen. + return (null, null, null); } - if (!(useVtToneMapping || useOclToneMapping || hasSubs || doDeintH2645)) + /* Make main filters for video stream */ + var mainFilters = new List(); + + if (!(doTonemap || hasSubs || doDeintH2645)) { // Dummy action to return empty filters when nothing to do. return (mainFilters, mainFilters, mainFilters); } - // Override the color when doing OpenCL Tone mapping, where we are using hardware surface output. - if (useOclToneMapping) + // Color override is only required for OpenCL where hardware surface is in use + if (doOclTonemap) { - mainFilters.Add(GetOverwriteColorPropertiesParam(state, true)); + mainFilters.Add(GetOverwriteColorPropertiesParam(state, doOclTonemap)); } - // With OpenCL tone mapping, we are using native hwmap and there's no need to specify a format for the main stream. - // However, for other cases, we have to specify the format for the main stream when we are doing subtitle burn-in. - // This is because the default upload option is not always processable with VideoToolbox. - // Most notably, yuv420p should be replaced by nv12. - else if (hasSubs) - { - var is8Bit = string.Equals("yuv420p", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) - || string.Equals("yuvj420p", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); - var is10Bit = string.Equals("yuv420p10le", state.VideoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); - - if (is8Bit) - { - mainFilters.Add("format=nv12"); - } - else if (is10Bit) - { - mainFilters.Add("format=p010"); - } - } - - mainFilters.Add("hwupload"); - var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); - if (useVtToneMapping) - { - const string TonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; - hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) - ? "scale_vt=" + TonemapArgs - : hwScaleFilter + ":" + TonemapArgs; - } - - mainFilters.Add(hwScaleFilter); - - if (useOclToneMapping) - { - mainFilters.Add("hwmap=derive_device=opencl"); - mainFilters.Add(GetHwTonemapFilter(options, "opencl", "nv12")); - mainFilters.Add("hwmap=derive_device=videotoolbox:reverse=1"); - } + // INPUT videotoolbox/memory surface(vram/uma) + // this will pass-through automatically if in/out format matches. + mainFilters.Add("format=nv12|p010le|videotoolbox_vld"); + mainFilters.Add("hwupload=derive_device=videotoolbox"); + // hw deint if (doDeintH2645) { var deintFilter = GetHwDeinterlaceFilter(state, options, "videotoolbox"); mainFilters.Add(deintFilter); } + var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + if (doVtTonemap) + { + const string VtTonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; + + // scale_vt can handle scaling & tonemapping in one shot, just like vpp_qsv. + hwScaleFilter = string.IsNullOrEmpty(hwScaleFilter) + ? "scale_vt=" + VtTonemapArgs + : hwScaleFilter + ":" + VtTonemapArgs; + } + + // hw scale & vt tonemap + mainFilters.Add(hwScaleFilter); + + // ocl tonemap + if (doOclTonemap) + { + // map from videotoolbox to opencl via videotoolbox-opencl interop. + mainFilters.Add("hwmap=derive_device=opencl:mode=read"); + + var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12"); + mainFilters.Add(tonemapFilter); + + // OUTPUT videotoolbox(nv12) surface(vram/uma) + // reverse-mapping via videotoolbox-opencl interop. + mainFilters.Add("hwmap=derive_device=videotoolbox:mode=write:reverse=1"); + } + + /* Make sub and overlay filters for subtitle stream */ var subFilters = new List(); var overlayFilters = new List(); @@ -6094,10 +6112,17 @@ namespace MediaBrowser.Controller.MediaEncoding // Hardware surface only make sense when interop with OpenCL // VideoToolbox's Hardware surface in ffmpeg is not only slower than hwupload, but also breaks HDR in many cases. // For example: https://trac.ffmpeg.org/ticket/10884 - var useOclToneMapping = !IsVideoToolboxTonemapAvailable(state, options) && - options.EnableTonemapping && - state.VideoStream.VideoRangeType == VideoRangeType.DOVI; - var useHwSurface = useOclToneMapping && IsVideoToolBoxFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); + var useOclToneMapping = !IsVideoToolboxTonemapAvailable(state, options) + && options.EnableTonemapping + && state.VideoStream is not null + && GetVideoColorBitDepth(state) == 10 + && state.VideoStream.VideoRange == VideoRange.HDR + && (state.VideoStream.VideoRangeType == VideoRangeType.HDR10 + || state.VideoStream.VideoRangeType == VideoRangeType.HLG + || (state.VideoStream.VideoRangeType == VideoRangeType.DOVI + && string.Equals(state.VideoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase))); + + var useHwSurface = useOclToneMapping && IsVideoToolboxFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); if (is8bitSwFormatsVt) { From 2f668710400f433f6f0dcb9bd412d7060f91a294 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 8 Mar 2024 23:37:27 +0800 Subject: [PATCH 13/16] fix: also check if we are doing scaling Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1ac40c7184..65224115b0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5011,6 +5011,7 @@ namespace MediaBrowser.Controller.MediaEncoding var threeDFormat = state.MediaSource.Video3DFormat; var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); + var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); @@ -5019,6 +5020,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doOclTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); var doTonemap = doVtTonemap || doOclTonemap; + var doScale = !string.IsNullOrEmpty(hwScaleFilter); var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; @@ -5037,7 +5039,7 @@ namespace MediaBrowser.Controller.MediaEncoding /* Make main filters for video stream */ var mainFilters = new List(); - if (!(doTonemap || hasSubs || doDeintH2645)) + if (!(doTonemap || doScale || hasSubs || doDeintH2645)) { // Dummy action to return empty filters when nothing to do. return (mainFilters, mainFilters, mainFilters); @@ -5061,7 +5063,6 @@ namespace MediaBrowser.Controller.MediaEncoding mainFilters.Add(deintFilter); } - var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); if (doVtTonemap) { const string VtTonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709"; From d10ad6c383954e6acb76581d5459aa9740c66688 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 9 Mar 2024 11:02:53 +0800 Subject: [PATCH 14/16] fix: no need to check filters prematurely Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 65224115b0..37aa295dce 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5039,12 +5039,6 @@ namespace MediaBrowser.Controller.MediaEncoding /* Make main filters for video stream */ var mainFilters = new List(); - if (!(doTonemap || doScale || hasSubs || doDeintH2645)) - { - // Dummy action to return empty filters when nothing to do. - return (mainFilters, mainFilters, mainFilters); - } - // Color override is only required for OpenCL where hardware surface is in use if (doOclTonemap) { From e6dee627e3543e5d29fae0b1f96facab5092310c Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 9 Mar 2024 14:22:27 +0800 Subject: [PATCH 15/16] fix: force a pixel format for 10-bit inputs Signed-off-by: gnattu --- .../MediaEncoding/EncodingHelper.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 37aa295dce..770e86f96d 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5011,16 +5011,22 @@ namespace MediaBrowser.Controller.MediaEncoding var threeDFormat = state.MediaSource.Video3DFormat; var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase); - var hwScaleFilter = GetHwScaleFilter("vt", null, inW, inH, reqW, reqH, reqMaxW, reqMaxH); var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintH2645 = doDeintH264 || doDeintHevc; var doVtTonemap = IsVideoToolboxTonemapAvailable(state, options); var doOclTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); - var doTonemap = doVtTonemap || doOclTonemap; - var doScale = !string.IsNullOrEmpty(hwScaleFilter); + var scaleFormat = string.Empty; + if (GetVideoColorBitDepth(state) == 10) + { + // Use P010 for OpenCL tone mapping, otherwise force an 8bit output. + scaleFormat = doOclTonemap ? "p010le" : "nv12"; + } + + var hwScaleFilter = GetHwScaleFilter("vt", scaleFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream; var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream; @@ -5028,8 +5034,6 @@ namespace MediaBrowser.Controller.MediaEncoding && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)); - // FIXME: scale_vt lacks of format option for the time being. - // hwdownload/hwmap to sw requires setting a format explicitly. if (!isVtEncoder) { // should not happen. From 0909ee7208a2d1473455bb1c60863e026a55860d Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 9 Mar 2024 15:16:00 +0800 Subject: [PATCH 16/16] fix: convert all non-yuv420 inputs to nv12 Signed-off-by: gnattu --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 770e86f96d..ea39891a6e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5019,7 +5019,7 @@ namespace MediaBrowser.Controller.MediaEncoding var doOclTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options); var scaleFormat = string.Empty; - if (GetVideoColorBitDepth(state) == 10) + if (!string.Equals(state.VideoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)) { // Use P010 for OpenCL tone mapping, otherwise force an 8bit output. scaleFormat = doOclTonemap ? "p010le" : "nv12";