Explorar el Código

Merge pull request #3569 from nyanmisaka/textsub-fix

Fix QSV subtitle burn-in on windows and P010 detect
Joshua M. Boniface hace 4 años
padre
commit
0b73f3d646
Se han modificado 1 ficheros con 189 adiciones y 116 borrados
  1. 189 116
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

+ 189 - 116
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Runtime.InteropServices;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
 using Jellyfin.Data.Enums;
 using Jellyfin.Data.Enums;
@@ -449,41 +450,59 @@ namespace MediaBrowser.Controller.MediaEncoding
             var arg = new StringBuilder();
             var arg = new StringBuilder();
             var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
             var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty;
             var outputVideoCodec = GetVideoEncoder(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 (isVaapiDecoder)
+                    {
+                        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 hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
-
-                if (!hasTextSubs)
+                if (state.IsVideoRequest
+                    && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                     if (isQsvEncoder)
+                    var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+
+                    if (isQsvEncoder)
                     {
                     {
                         if (isQsvDecoder)
                         if (isQsvDecoder)
                         {
                         {
-                            arg.Append("-hwaccel qsv -init_hw_device qsv=hw ");
+                            if (isLinux)
+                            {
+                                if (hasGraphicalSubs)
+                                {
+                                    arg.Append("-init_hw_device qsv=hw -filter_hw_device hw ");
+                                }
+                                else
+                                {
+                                    arg.Append("-hwaccel qsv -init_hw_device qsv=hw ");
+                                }
+                            }
+
+                            if (isWindows)
+                            {
+                                arg.Append("-hwaccel qsv -init_hw_device qsv=hw ");
+                            }
                         }
                         }
                         // While using SW decoder
                         // While using SW decoder
                         else
                         else
@@ -806,6 +825,34 @@ namespace MediaBrowser.Controller.MediaEncoding
                         break;
                         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
             else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm
             {
             {
                 // Values 0-3, 0 being highest quality but slower
                 // Values 0-3, 0 being highest quality but slower
@@ -1555,28 +1602,28 @@ namespace MediaBrowser.Controller.MediaEncoding
                 var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
                 var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
                 if (index != -1)
                 if (index != -1)
                 {
                 {
-                    outputSizeParam = "," + outputSizeParam.Substring(index);
+                    outputSizeParam = outputSizeParam.Substring(index);
                 }
                 }
                 else
                 else
                 {
                 {
                     index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
                     index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
                     if (index != -1)
                     if (index != -1)
                     {
                     {
-                        outputSizeParam = "," + outputSizeParam.Substring(index);
+                        outputSizeParam = outputSizeParam.Substring(index);
                     }
                     }
                     else
                     else
                     {
                     {
                         index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
                         index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
                         if (index != -1)
                         if (index != -1)
                         {
                         {
-                            outputSizeParam = "," + outputSizeParam.Substring(index);
+                            outputSizeParam = outputSizeParam.Substring(index);
                         }
                         }
                         else
                         else
                         {
                         {
                             index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
                             index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
                             if (index != -1)
                             if (index != -1)
                             {
                             {
-                                outputSizeParam = "," + outputSizeParam.Substring(index);
+                                outputSizeParam = outputSizeParam.Substring(index);
                             }
                             }
                         }
                         }
                     }
                     }
@@ -1585,43 +1632,30 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
             var videoSizeParam = string.Empty;
             var videoSizeParam = string.Empty;
             var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
             var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
+            var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
 
 
             // Setup subtitle scaling
             // Setup subtitle scaling
             if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
             if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
             {
             {
-                videoSizeParam = string.Format(
-                    CultureInfo.InvariantCulture,
-                    "scale={0}:{1}",
-                    state.VideoStream.Width.Value,
-                    state.VideoStream.Height.Value);
+                // 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);
 
 
-                // For QSV, feed it into hardware encoder now
-                if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
+                if (width.HasValue && height.HasValue)
                 {
                 {
-                    videoSizeParam += ",hwupload=extra_hw_frames=64";
-                }
-
-                // For VAAPI and CUVID 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(
+                    videoSizeParam = string.Format(
                         CultureInfo.InvariantCulture,
                         CultureInfo.InvariantCulture,
-                        "scale={0}:{1}",
+                        "scale={0}x{1}",
                         width.Value,
                         width.Value,
                         height.Value);
                         height.Value);
-                    }
+                }
+
+                // For QSV, feed it into hardware encoder now
+                if (isLinux && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
+                {
+                    videoSizeParam += ",hwupload=extra_hw_frames=64";
                 }
                 }
             }
             }
 
 
@@ -1634,7 +1668,10 @@ namespace MediaBrowser.Controller.MediaEncoding
                 : state.SubtitleStream.Index;
                 : state.SubtitleStream.Index;
 
 
             // Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
             // 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
             // When the input may or may not be hardware VAAPI decodable
             if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
@@ -1644,7 +1681,6 @@ namespace MediaBrowser.Controller.MediaEncoding
                     [sub]: SW scaling subtitle to FixedOutputSize
                     [sub]: SW scaling subtitle to FixedOutputSize
                     [base][sub]: SW overlay
                     [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\"";
                 retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\"";
             }
             }
 
 
@@ -1657,7 +1693,6 @@ namespace MediaBrowser.Controller.MediaEncoding
                     [sub]: SW scaling subtitle to FixedOutputSize
                     [sub]: SW scaling subtitle to FixedOutputSize
                     [base][sub]: SW overlay
                     [base][sub]: SW overlay
                 */
                 */
-                outputSizeParam = outputSizeParam.TrimStart(',');
                 retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
                 retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"";
             }
             }
             else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
             else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
@@ -1666,14 +1701,13 @@ namespace MediaBrowser.Controller.MediaEncoding
                     QSV in FFMpeg can now setup hardware overlay for transcodes.
                     QSV in FFMpeg can now setup hardware overlay for transcodes.
                     For software decoding and hardware encoding option, frames must be hwuploaded into hardware
                     For software decoding and hardware encoding option, frames must be hwuploaded into hardware
                     with fixed frame size.
                     with fixed frame size.
+                    Currently only supports linux.
                 */
                 */
-                if (videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
+                if (isLinux)
                 {
                 {
-                    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 +1779,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 requestedMaxWidth,
                 requestedMaxWidth,
                 requestedMaxHeight);
                 requestedMaxHeight);
 
 
-            var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
-
             if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
             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
                 && width.HasValue
                 && height.HasValue)
                 && height.HasValue)
             {
             {
@@ -1769,15 +1801,20 @@ namespace MediaBrowser.Controller.MediaEncoding
                     filters.Add(
                     filters.Add(
                         string.Format(
                         string.Format(
                             CultureInfo.InvariantCulture,
                             CultureInfo.InvariantCulture,
-                            "{0}=w={1}:h={2}:format=nv12",
+                            "{0}=w={1}:h={2}:format=nv12{3}",
                             qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
                             qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi",
                             outputWidth,
                             outputWidth,
-                            outputHeight));
+                            outputHeight,
+                            (qsv_or_vaapi && state.DeInterlace("h264", true)) ? ":deinterlace=1" : string.Empty));
                 }
                 }
                 else
                 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
             else if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1
@@ -1998,8 +2035,6 @@ namespace MediaBrowser.Controller.MediaEncoding
             var inputHeight = videoStream?.Height;
             var inputHeight = videoStream?.Height;
             var threeDFormat = state.MediaSource.Video3DFormat;
             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
             // When the input may or may not be hardware VAAPI decodable
             if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -2010,20 +2045,16 @@ namespace MediaBrowser.Controller.MediaEncoding
             // When the input may or may not be hardware QSV decodable
             // When the input may or may not be hardware QSV decodable
             else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
             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
             // 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))
                 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
             {
             {
                 var codec = videoStream.Codec.ToLowerInvariant();
                 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 = IsColorDepth10(state);
 
 
                 // Assert 10-bit hardware VAAPI decodable
                 // Assert 10-bit hardware VAAPI decodable
                 if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
                 if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -2054,31 +2085,32 @@ namespace MediaBrowser.Controller.MediaEncoding
                 {
                 {
                     filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_vaapi"));
                     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
             // 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;
                 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 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)
                 if (string.Equals(options.DeinterlaceMethod, "yadif_bob", StringComparison.OrdinalIgnoreCase) && (inputFramerate ?? 60) <= 30)
                 {
                 {
-                    filters.Add("yadif=1:-1:0");
+                    deintParam = "yadif=1:-1:0";
                 }
                 }
                 else
                 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);
+                    }
                 }
                 }
             }
             }
 
 
@@ -2290,7 +2322,8 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
             {
                 inputModifier += " " + videoDecoder;
                 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 videoStream = state.VideoStream;
                     var inputWidth = videoStream?.Width;
                     var inputWidth = videoStream?.Width;
@@ -2303,11 +2336,19 @@ namespace MediaBrowser.Controller.MediaEncoding
                         && width.HasValue
                         && width.HasValue
                         && height.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";
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -2553,8 +2594,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
             if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
             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));
+                var isColorDepth10 = IsColorDepth10(state);
 
 
                 // Only hevc and vp9 formats have 10-bit hardware decoder support now.
                 // Only hevc and vp9 formats have 10-bit hardware decoder support now.
                 if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
                 if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -2631,13 +2671,6 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h264":
                         case "h264":
                             if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
                             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<string>();
-                                    return null;
-                                }
-
                                 return "-c:v h264_cuvid";
                                 return "-c:v h264_cuvid";
                             }
                             }
 
 
@@ -2904,21 +2937,24 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// </summary>
         /// </summary>
         public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec)
         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 isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1);
             var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va");
             var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va");
 
 
             if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
             if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
             {
             {
-                if (!isWindows)
+                if (isLinux)
                 {
                 {
                     return "-hwaccel vaapi";
                     return "-hwaccel vaapi";
                 }
                 }
-                else if (isWindows8orLater)
+
+                if (isWindows && isWindows8orLater)
                 {
                 {
                     return "-hwaccel d3d11va";
                     return "-hwaccel d3d11va";
                 }
                 }
-                else
+
+                if (isWindows && !isWindows8orLater)
                 {
                 {
                     return "-hwaccel dxva2";
                     return "-hwaccel dxva2";
                 }
                 }
@@ -3187,5 +3223,42 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
         {
             return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
             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;
+        }
     }
     }
 }
 }