diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9a6f1231f1..f101752124 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -112,6 +112,13 @@ namespace MediaBrowser.Controller.MediaEncoding return _mediaEncoder.SupportsHwaccel("vaapi"); } + private bool IsTonemappingSupported(EncodingJobInfo state, EncodingOptions options) + { + var videoStream = state.VideoStream; + var isColorDepth10 = IsColorDepth10(state); + return isColorDepth10 && _mediaEncoder.SupportsHwaccel("opencl") && options.EnableTonemapping && !string.IsNullOrEmpty(videoStream.VideoRange) && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase); + } + /// /// Gets the name of the output video codec. /// @@ -468,6 +475,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions); if (!IsCopyCodec(outputVideoCodec)) { @@ -477,10 +485,24 @@ namespace MediaBrowser.Controller.MediaEncoding { if (isVaapiDecoder) { - arg.Append("-hwaccel_output_format vaapi ") - .Append("-vaapi_device ") - .Append(encodingOptions.VaapiDevice) - .Append(' '); + if (isTonemappingSupported) + { + arg.Append("-init_hw_device vaapi=va:") + .Append(encodingOptions.VaapiDevice) + .Append(' ') + .Append("-init_hw_device opencl=ocl@va ") + .Append("-hwaccel vaapi ") + .Append("-hwaccel_device va ") + .Append("-hwaccel_output_format vaapi ") + .Append("-filter_hw_device ocl "); + } + else + { + arg.Append("-hwaccel_output_format vaapi ") + .Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(' '); + } } else if (!isVaapiDecoder && isVaapiEncoder) { @@ -529,13 +551,7 @@ namespace MediaBrowser.Controller.MediaEncoding && (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder) || (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder)) { - var isColorDepth10 = IsColorDepth10(state); - - if (isColorDepth10 - && _mediaEncoder.SupportsHwaccel("opencl") - && encodingOptions.EnableTonemapping - && !string.IsNullOrEmpty(state.VideoStream.VideoRange) - && state.VideoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) + if (isTonemappingSupported) { arg.Append("-init_hw_device opencl=ocl:") .Append(encodingOptions.OpenclDevice) @@ -1997,6 +2013,7 @@ namespace MediaBrowser.Controller.MediaEncoding public List GetScalingFilters( EncodingJobInfo state, + EncodingOptions options, int? videoWidth, int? videoHeight, Video3DFormat? threedFormat, @@ -2035,6 +2052,19 @@ namespace MediaBrowser.Controller.MediaEncoding || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); + var isTonemappingSupported = IsTonemappingSupported(state, options); + var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !qsv_or_vaapi; + + var outputPixFmt = string.Empty; + if (isTonemappingSupported && isTonemappingSupportedOnVaapi) + { + outputPixFmt = "format=p010:out_range=limited"; + } + else + { + outputPixFmt = "format=nv12"; + } + if (!videoWidth.HasValue || outputWidth != videoWidth.Value || !videoHeight.HasValue @@ -2045,10 +2075,11 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add( string.Format( CultureInfo.InvariantCulture, - "{0}=w={1}:h={2}:format=nv12{3}", + "{0}=w={1}:h={2}{3}{4}", qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", outputWidth, outputHeight, + ":" + outputPixFmt, (qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty)); } else @@ -2056,8 +2087,9 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add( string.Format( CultureInfo.InvariantCulture, - "{0}=format=nv12{1}", + "{0}={1}{2}", qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", + outputPixFmt, (qsv_or_vaapi && isDeintEnabled) ? ":deinterlace=1" : string.Empty)); } } @@ -2290,6 +2322,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwDecoder = string.IsNullOrEmpty(videoDecoder); var isD3d11vaDecoder = videoDecoder.IndexOf("d3d11va", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiHevcEncoder = outputVideoCodec.IndexOf("hevc_vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isQsvH264Encoder = outputVideoCodec.IndexOf("h264_qsv", StringComparison.OrdinalIgnoreCase) != -1; @@ -2300,6 +2333,10 @@ namespace MediaBrowser.Controller.MediaEncoding var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1; var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); var isColorDepth10 = IsColorDepth10(state); + var isTonemappingSupported = IsTonemappingSupported(state, options); + var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder; + var isTonemappingSupportedOnAmf = string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder; + var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder); var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; @@ -2311,18 +2348,13 @@ namespace MediaBrowser.Controller.MediaEncoding var isDeinterlaceH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var isDeinterlaceHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); - if ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && isNvdecHevcDecoder || isSwDecoder) - || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && isD3d11vaDecoder || isSwDecoder)) + if (isTonemappingSupportedOnNvenc || isTonemappingSupportedOnAmf || isTonemappingSupportedOnVaapi) { // Currently only with the use of NVENC decoder can we get a decent performance. // Currently only the HEVC/H265 format is supported with NVDEC decoder. // NVIDIA Pascal and Turing or higher are recommended. // AMD Polaris and Vega or higher are recommended. - if (isColorDepth10 - && _mediaEncoder.SupportsHwaccel("opencl") - && options.EnableTonemapping - && !string.IsNullOrEmpty(videoStream.VideoRange) - && videoStream.VideoRange.Contains("HDR", StringComparison.OrdinalIgnoreCase)) + if (isTonemappingSupported) { var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}"; @@ -2353,12 +2385,32 @@ namespace MediaBrowser.Controller.MediaEncoding // Convert to hardware pixel format p010 when using SW decoder. filters.Add("format=p010"); + + // Upload the HDR10 or HLG data to the OpenCL device, + // use tonemap_opencl filter for tone mapping, + // and then download the SDR data to memory. + filters.Add("hwupload"); + } + + if (isVaapiDecoder) + { + isScalingInAdvance = true; + filters.AddRange( + GetScalingFilters( + state, + options, + inputWidth, + inputHeight, + threeDFormat, + videoDecoder, + outputVideoCodec, + request.Width, + request.Height, + request.MaxWidth, + request.MaxHeight)); + filters.Add("hwmap"); } - // Upload the HDR10 or HLG data to the OpenCL device, - // use tonemap_opencl filter for tone mapping, - // and then download the SDR data to memory. - filters.Add("hwupload"); filters.Add( string.Format( CultureInfo.InvariantCulture, @@ -2369,21 +2421,30 @@ namespace MediaBrowser.Controller.MediaEncoding options.TonemappingPeak, options.TonemappingParam, options.TonemappingRange)); - filters.Add("hwdownload"); - if (isLibX264Encoder - || isLibX265Encoder - || hasGraphicalSubs - || (isNvdecHevcDecoder && isDeinterlaceHevc) - || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc)) + if (isSwDecoder || isD3d11vaDecoder) { - filters.Add("format=nv12"); + filters.Add("hwdownload"); + + if (isLibX264Encoder + || isLibX265Encoder + || hasGraphicalSubs + || (isNvdecHevcDecoder && isDeinterlaceHevc) + || (!isNvdecHevcDecoder && isDeinterlaceH264 || isDeinterlaceHevc)) + { + filters.Add("format=nv12"); + } + } + + if (isVaapiDecoder) + { + filters.Add("hwmap=derive_device=vaapi:reverse=1"); } } } // When the input may or may not be hardware VAAPI decodable - if (isVaapiH264Encoder || isVaapiHevcEncoder) + if ((isVaapiH264Encoder || isVaapiHevcEncoder) && !isTonemappingSupported && !isTonemappingSupportedOnVaapi) { filters.Add("format=nv12|vaapi"); filters.Add("hwupload"); @@ -2467,6 +2528,7 @@ namespace MediaBrowser.Controller.MediaEncoding filters.AddRange( GetScalingFilters( state, + options, inputWidth, inputHeight, threeDFormat, @@ -2483,6 +2545,13 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasTextSubs) { + // Convert hw context from ocl to va. + // For tonemapping and text subs burn-in. + if (isTonemappingSupported && isTonemappingSupportedOnVaapi) + { + filters.Add("scale_vaapi"); + } + // Test passed on Intel and AMD gfx filters.Add("hwmap=mode=read+write"); filters.Add("format=nv12");