From 32fb34a4cc675ba1751f6a66b72f7e7bda99d836 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jul 2020 21:09:04 +0800 Subject: [PATCH 1/4] Fix QSV subtitle burn-in on windows and P010 detect --- .../MediaEncoding/EncodingHelper.cs | 175 ++++++++++++------ 1 file changed, 114 insertions(+), 61 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 534e0c372..62811c323 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -475,21 +475,33 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; - if (!hasTextSubs) + if (isQsvEncoder) { - if (isQsvEncoder) + if (isQsvDecoder) { - if (isQsvDecoder) + if (!isWindows) + { + if (hasGraphicalSubs) + { + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + } + else + { + arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); + } + } + else { arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); } - // While using SW decoder - else - { - arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); - } + } + // While using SW decoder + else + { + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); } } } @@ -806,6 +818,34 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } + else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + switch (encodingOptions.EncoderPreset) + { + case "veryslow": + case "slow": + case "slower": + 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; + } + } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm { // Values 0-3, 0 being highest quality but slower @@ -1555,28 +1595,28 @@ namespace MediaBrowser.Controller.MediaEncoding var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } } } @@ -1591,17 +1631,18 @@ namespace MediaBrowser.Controller.MediaEncoding { videoSizeParam = string.Format( CultureInfo.InvariantCulture, - "scale={0}:{1}", + "scale={0}x{1}", state.VideoStream.Width.Value, state.VideoStream.Height.Value); // For QSV, feed it into hardware encoder now - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + if (!isWindows && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { videoSizeParam += ",hwupload=extra_hw_frames=64"; } - // For VAAPI and CUVID decoder + // For VAAPI and NVDEC decoder // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video, // thus needs to be manually adjusted. if (videoDecoder.IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 @@ -1618,7 +1659,7 @@ namespace MediaBrowser.Controller.MediaEncoding { videoSizeParam = string.Format( CultureInfo.InvariantCulture, - "scale={0}:{1}", + "scale={0}x{1}", width.Value, height.Value); } @@ -1634,7 +1675,10 @@ namespace MediaBrowser.Controller.MediaEncoding : state.SubtitleStream.Index; // Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference) - var retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\""; + // Always put the scaler before the overlay for better performance + var retStr = !string.IsNullOrEmpty(outputSizeParam) ? + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"" : + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""; // When the input may or may not be hardware VAAPI decodable if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) @@ -1644,7 +1688,6 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - outputSizeParam = outputSizeParam.TrimStart(','); retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""; } @@ -1657,7 +1700,6 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - outputSizeParam = outputSizeParam.TrimStart(','); retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; } else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) @@ -1666,14 +1708,14 @@ namespace MediaBrowser.Controller.MediaEncoding QSV in FFMpeg can now setup hardware overlay for transcodes. For software decoding and hardware encoding option, frames must be hwuploaded into hardware with fixed frame size. + Currently only supports linux. */ - if (videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) + var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + if (!isWindows) { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; - } - else - { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwupload=extra_hw_frames=64[v];[v][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; + retStr = !string.IsNullOrEmpty(outputSizeParam) ? + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" : + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""; } } @@ -1745,10 +1787,8 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxWidth, requestedMaxHeight); - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) && width.HasValue && height.HasValue) { @@ -1769,15 +1809,20 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add( string.Format( CultureInfo.InvariantCulture, - "{0}=w={1}:h={2}:format=nv12", + "{0}=w={1}:h={2}:format=nv12{3}", qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", outputWidth, - outputHeight)); + outputHeight, + (qsv_or_vaapi && state.DeInterlace("h264", true)) ? ":deinterlace=1" : string.Empty)); } else { - // set w=0:h=0 for vpp_qsv to keep the original dimensions, otherwise it will fail. - filters.Add(string.Format(CultureInfo.InvariantCulture, "{0}format=nv12", qsv_or_vaapi ? "vpp_qsv=w=0:h=0:" : "scale_vaapi=")); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "{0}=format=nv12{1}", + qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", + (qsv_or_vaapi && state.DeInterlace("h264", true)) ? ":deinterlace=1" : string.Empty)); } } else if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 @@ -1998,8 +2043,6 @@ namespace MediaBrowser.Controller.MediaEncoding var inputHeight = videoStream?.Height; var threeDFormat = state.MediaSource.Video3DFormat; - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - // When the input may or may not be hardware VAAPI decodable if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { @@ -2010,11 +2053,8 @@ namespace MediaBrowser.Controller.MediaEncoding // When the input may or may not be hardware QSV decodable else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - if (!hasTextSubs) - { - filters.Add("format=nv12|qsv"); - filters.Add("hwupload=extra_hw_frames=64"); - } + filters.Add("format=nv12|qsv"); + filters.Add("hwupload=extra_hw_frames=64"); } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first @@ -2022,8 +2062,10 @@ namespace MediaBrowser.Controller.MediaEncoding && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) { var codec = videoStream.Codec.ToLowerInvariant(); - var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase)); + var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) + && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)); // Assert 10-bit hardware VAAPI decodable if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) @@ -2054,31 +2096,32 @@ namespace MediaBrowser.Controller.MediaEncoding { filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_vaapi")); } - else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) - { - if (!hasTextSubs) - { - filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv")); - } - } } // Add software deinterlace filter before scaling filter - if (((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) - && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - && !string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) - || (hasTextSubs && state.DeInterlace("h264", true) && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))) + if (state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) { + var deintParam = string.Empty; var inputFramerate = videoStream?.RealFrameRate; // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle if (string.Equals(options.DeinterlaceMethod, "yadif_bob", StringComparison.OrdinalIgnoreCase) && (inputFramerate ?? 60) <= 30) { - filters.Add("yadif=1:-1:0"); + deintParam = "yadif=1:-1:0"; } else { - filters.Add("yadif=0:-1:0"); + deintParam = "yadif=0:-1:0"; + } + + if (!string.IsNullOrEmpty(deintParam)) + { + if (!string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + && videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) == -1) + { + filters.Add(deintParam); + } } } @@ -2303,11 +2346,19 @@ namespace MediaBrowser.Controller.MediaEncoding && width.HasValue && height.HasValue) { - inputModifier += string.Format( - CultureInfo.InvariantCulture, - " -resize {0}x{1}", - width.Value, - height.Value); + if (width.HasValue && height.HasValue) + { + inputModifier += string.Format( + CultureInfo.InvariantCulture, + " -resize {0}x{1}", + width.Value, + height.Value); + } + + if (state.DeInterlace("h264", true)) + { + inputModifier += " -deint 1"; + } } } } @@ -2554,7 +2605,9 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) { var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) - && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase)); + && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)); // Only hevc and vp9 formats have 10-bit hardware decoder support now. if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) From c1c1a2f7515edf157022a3ba421b5d66889aa525 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 17 Jul 2020 01:30:35 +0800 Subject: [PATCH 2/4] Match the size of subtitle stream and video stream --- .../MediaEncoding/EncodingHelper.cs | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 62811c323..ffe2bcf28 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1625,45 +1625,31 @@ namespace MediaBrowser.Controller.MediaEncoding var videoSizeParam = string.Empty; var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; + var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; // Setup subtitle scaling if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) { - videoSizeParam = string.Format( + // Adjust the size of graphical subtitles to fit the video stream. + var videoStream = state.VideoStream; + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; + var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); + + if (width.HasValue && height.HasValue) + { + videoSizeParam = string.Format( CultureInfo.InvariantCulture, "scale={0}x{1}", - state.VideoStream.Width.Value, - state.VideoStream.Height.Value); + width.Value, + height.Value); + } // For QSV, feed it into hardware encoder now - var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; if (!isWindows && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { videoSizeParam += ",hwupload=extra_hw_frames=64"; } - - // For VAAPI and NVDEC decoder - // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video, - // thus needs to be manually adjusted. - if (videoDecoder.IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 - || (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) - && (videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 - || outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1))) - { - var videoStream = state.VideoStream; - var inputWidth = videoStream?.Width; - var inputHeight = videoStream?.Height; - var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); - - if (width.HasValue && height.HasValue) - { - videoSizeParam = string.Format( - CultureInfo.InvariantCulture, - "scale={0}x{1}", - width.Value, - height.Value); - } - } } var mapPrefix = state.SubtitleStream.IsExternal ? @@ -1710,7 +1696,6 @@ namespace MediaBrowser.Controller.MediaEncoding with fixed frame size. Currently only supports linux. */ - var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; if (!isWindows) { retStr = !string.IsNullOrEmpty(outputSizeParam) ? From e1ba1fcebea0df71239bbc379ab436327488b9a8 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 17 Jul 2020 01:41:24 +0800 Subject: [PATCH 3/4] fix indent --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ffe2bcf28..6b3bbc34a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1639,10 +1639,10 @@ namespace MediaBrowser.Controller.MediaEncoding if (width.HasValue && height.HasValue) { videoSizeParam = string.Format( - CultureInfo.InvariantCulture, - "scale={0}x{1}", - width.Value, - height.Value); + CultureInfo.InvariantCulture, + "scale={0}x{1}", + width.Value, + height.Value); } // For QSV, feed it into hardware encoder now From 77f9417d84b30a56226497329c59e0e1e50e61a4 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 17 Jul 2020 19:32:15 +0800 Subject: [PATCH 4/4] enhance platform check and 10bit detect --- .../MediaEncoding/EncodingHelper.cs | 161 +++++++++++------- 1 file changed, 98 insertions(+), 63 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6b3bbc34a..ffbda42c3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using Jellyfin.Data.Enums; @@ -449,60 +450,66 @@ namespace MediaBrowser.Controller.MediaEncoding var arg = new StringBuilder(); var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty; var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty; - bool isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; - bool isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; - bool isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; - bool isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + if (!IsCopyCodec(outputVideoCodec)) { - if (isVaapiDecoder) + if (state.IsVideoRequest + && IsVaapiSupported(state) + && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) { - arg.Append("-hwaccel_output_format vaapi ") - .Append("-vaapi_device ") - .Append(encodingOptions.VaapiDevice) - .Append(" "); - } - else if (!isVaapiDecoder && isVaapiEncoder) - { - arg.Append("-vaapi_device ") - .Append(encodingOptions.VaapiDevice) - .Append(" "); - } - } - - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) - { - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; - - if (isQsvEncoder) - { - if (isQsvDecoder) + if (isVaapiDecoder) { - if (!isWindows) + arg.Append("-hwaccel_output_format vaapi ") + .Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } + else if (!isVaapiDecoder && isVaapiEncoder) + { + arg.Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } + } + + if (state.IsVideoRequest + && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + { + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + + if (isQsvEncoder) + { + if (isQsvDecoder) { - if (hasGraphicalSubs) + if (isLinux) { - arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + if (hasGraphicalSubs) + { + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + } + else + { + arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); + } } - else + + if (isWindows) { arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); } } + // While using SW decoder else { - arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); } } - // While using SW decoder - else - { - arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); - } } } @@ -1625,7 +1632,7 @@ namespace MediaBrowser.Controller.MediaEncoding var videoSizeParam = string.Empty; var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; - var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); // Setup subtitle scaling if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) @@ -1646,7 +1653,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // For QSV, feed it into hardware encoder now - if (!isWindows && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + if (isLinux && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { videoSizeParam += ",hwupload=extra_hw_frames=64"; } @@ -1696,7 +1703,7 @@ namespace MediaBrowser.Controller.MediaEncoding with fixed frame size. Currently only supports linux. */ - if (!isWindows) + if (isLinux) { retStr = !string.IsNullOrEmpty(outputSizeParam) ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" : @@ -2043,14 +2050,11 @@ namespace MediaBrowser.Controller.MediaEncoding } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first - else if (videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 + else if (IsVaapiSupported(state) && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) { var codec = videoStream.Codec.ToLowerInvariant(); - var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) - && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)); + var isColorDepth10 = IsColorDepth10(state); // Assert 10-bit hardware VAAPI decodable if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) @@ -2318,7 +2322,8 @@ namespace MediaBrowser.Controller.MediaEncoding { inputModifier += " " + videoDecoder; - if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) + if (!IsCopyCodec(state.OutputVideoCodec) + && (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) { var videoStream = state.VideoStream; var inputWidth = videoStream?.Width; @@ -2589,10 +2594,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) { - var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) - && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase)); + var isColorDepth10 = IsColorDepth10(state); // Only hevc and vp9 formats have 10-bit hardware decoder support now. if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) @@ -2669,13 +2671,6 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - // cuvid decoder does not support 10-bit input. - if ((videoStream.BitDepth ?? 8) > 8) - { - encodingOptions.HardwareDecodingCodecs = Array.Empty(); - return null; - } - return "-c:v h264_cuvid"; } @@ -2942,21 +2937,24 @@ namespace MediaBrowser.Controller.MediaEncoding /// public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec) { - var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1); var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va"); if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) { - if (!isWindows) + if (isLinux) { return "-hwaccel vaapi"; } - else if (isWindows8orLater) + + if (isWindows && isWindows8orLater) { return "-hwaccel d3d11va"; } - else + + if (isWindows && !isWindows8orLater) { return "-hwaccel dxva2"; } @@ -3225,5 +3223,42 @@ namespace MediaBrowser.Controller.MediaEncoding { return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); } + + public static bool IsColorDepth10(EncodingJobInfo state) + { + var result = false; + var videoStream = state.VideoStream; + + if (videoStream != null) + { + if (!string.IsNullOrEmpty(videoStream.PixelFormat)) + { + result = videoStream.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase); + if (result) + { + return true; + } + } + + if (!string.IsNullOrEmpty(videoStream.Profile)) + { + result = videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase); + if (result) + { + return true; + } + } + + result = (videoStream.BitDepth ?? 8) == 10; + if (result) + { + return true; + } + } + + return result; + } } }