Przeglądaj źródła

Merge pull request #2809 from nyanmisaka/hwaccel

Add more separate hardware decoding toggles, support videotoolbox
Anthony Lavado 5 lat temu
rodzic
commit
25f8e596cb

+ 219 - 51
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -74,7 +74,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                     {"omx",                  hwEncoder + "_omx"},
                     {"omx",                  hwEncoder + "_omx"},
                     {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
                     {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
                     {"mediacodec",           hwEncoder + "_mediacodec"},
                     {"mediacodec",           hwEncoder + "_mediacodec"},
-                    {"vaapi",                hwEncoder + "_vaapi"}
+                    {"vaapi",                hwEncoder + "_vaapi"},
+                    {"videotoolbox",         hwEncoder + "_videotoolbox"}
                 };
                 };
 
 
                 if (!string.IsNullOrEmpty(hwType)
                 if (!string.IsNullOrEmpty(hwType)
@@ -104,7 +105,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 return false;
                 return false;
             }
             }
 
 
-            return true;
+            return _mediaEncoder.SupportsHwaccel("vaapi");
+
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -445,31 +447,41 @@ namespace MediaBrowser.Controller.MediaEncoding
         public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
         public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
         {
         {
             var arg = new StringBuilder();
             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;
 
 
             if (state.IsVideoRequest
             if (state.IsVideoRequest
                 && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
                 && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
             {
             {
-                arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi")
-                    .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
             if (state.IsVideoRequest
                 && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
             {
             {
-                var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
-                var outputVideoCodec = GetVideoEncoder(state, encodingOptions);
-
                 var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
                 var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
 
 
                 if (!hasTextSubs)
                 if (!hasTextSubs)
                 {
                 {
-                    // While using QSV encoder
-                    if ((outputVideoCodec ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
+                     if (isQsvEncoder)
                     {
                     {
-                        // While using QSV decoder
-                        if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
+                        if (isQsvDecoder)
                         {
                         {
                             arg.Append("-hwaccel qsv ");
                             arg.Append("-hwaccel qsv ");
                         }
                         }
@@ -527,6 +539,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1;
                 || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1;
         }
         }
 
 
+        // TODO This is auto inserted into the mpegts mux so it might not be needed
+        // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb
         public string GetBitStreamArgs(MediaStream stream)
         public string GetBitStreamArgs(MediaStream stream)
         {
         {
             if (IsH264(stream))
             if (IsH264(stream))
@@ -551,8 +565,8 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
             {
                 if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    // With vpx when crf is used, b:v becomes a max rate
-                    // https://trac.ffmpeg.org/wiki/vpxEncodingGuide.
+                    // When crf is used with vpx, b:v becomes a max rate
+                    // https://trac.ffmpeg.org/wiki/Encode/VP9
                     return string.Format(
                     return string.Format(
                         CultureInfo.InvariantCulture,
                         CultureInfo.InvariantCulture,
                         " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
                         " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
@@ -1525,8 +1539,9 @@ namespace MediaBrowser.Controller.MediaEncoding
             EncodingOptions options,
             EncodingOptions options,
             string outputVideoCodec)
             string outputVideoCodec)
         {
         {
-            var outputSizeParam = string.Empty;
+            outputVideoCodec ??= string.Empty;
 
 
+            var outputSizeParam = string.Empty;
             var request = state.BaseRequest;
             var request = state.BaseRequest;
 
 
             // Add resolution params, if specified
             // Add resolution params, if specified
@@ -1569,16 +1584,14 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             var videoSizeParam = string.Empty;
             var videoSizeParam = string.Empty;
-            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
+            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
 
 
             // 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)
             {
             {
-                // force_original_aspect_ratio=decrease
-                // Enable decreasing output video width or height if necessary to keep the original aspect ratio
                 videoSizeParam = string.Format(
                 videoSizeParam = string.Format(
                     CultureInfo.InvariantCulture,
                     CultureInfo.InvariantCulture,
-                    "scale={0}:{1}:force_original_aspect_ratio=decrease",
+                    "scale={0}:{1}",
                     state.VideoStream.Width.Value,
                     state.VideoStream.Width.Value,
                     state.VideoStream.Height.Value);
                     state.VideoStream.Height.Value);
 
 
@@ -1591,8 +1604,10 @@ namespace MediaBrowser.Controller.MediaEncoding
                 // For VAAPI and CUVID decoder
                 // For VAAPI and CUVID decoder
                 // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video,
                 // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video,
                 // thus needs to be manually adjusted.
                 // thus needs to be manually adjusted.
-                if ((IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
-                    || (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1)
+                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 videoStream = state.VideoStream;
                     var inputWidth = videoStream?.Width;
                     var inputWidth = videoStream?.Width;
@@ -1603,7 +1618,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     {
                     {
                         videoSizeParam = string.Format(
                         videoSizeParam = string.Format(
                         CultureInfo.InvariantCulture,
                         CultureInfo.InvariantCulture,
-                        "scale={0}:{1}:force_original_aspect_ratio=decrease",
+                        "scale={0}:{1}",
                         width.Value,
                         width.Value,
                         height.Value);
                         height.Value);
                     }
                     }
@@ -1634,7 +1649,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             // 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 (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
+            else if (IsVaapiSupported(state) && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1
                 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
                 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
             {
             {
                 /*
                 /*
@@ -1652,7 +1667,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     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.
                 */
                 */
-                if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))
+                if (videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1)
                 {
                 {
                     retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\"";
                     retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\"";
                 }
                 }
@@ -1975,7 +1990,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             var videoStream = state.VideoStream;
             var videoStream = state.VideoStream;
             var filters = new List<string>();
             var filters = new List<string>();
 
 
-            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
+            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
             var inputWidth = videoStream?.Width;
             var inputWidth = videoStream?.Width;
             var inputHeight = videoStream?.Height;
             var inputHeight = videoStream?.Height;
             var threeDFormat = state.MediaSource.Video3DFormat;
             var threeDFormat = state.MediaSource.Video3DFormat;
@@ -2000,7 +2015,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             // 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 (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
+            else if (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();
@@ -2509,16 +2524,17 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// </summary>
         /// </summary>
         protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
         protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
         {
         {
+            var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile;
+            var videoStream = state.VideoStream;
+            var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase)
+                || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase));
+
+
             if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
             if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            return GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
-        }
-
-        public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
-        {
             // Only use alternative encoders for video files.
             // Only use alternative encoders for video files.
             // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
             // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
             // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
             // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
@@ -2531,6 +2547,14 @@ namespace MediaBrowser.Controller.MediaEncoding
                 && !string.IsNullOrEmpty(videoStream.Codec)
                 && !string.IsNullOrEmpty(videoStream.Codec)
                 && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
                 && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
             {
             {
+                // Only hevc and vp9 formats have 10-bit hardware decoder support now.
+                if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase)
+                    || string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)))
+                {
+                    return null;
+                }
+
                 if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     switch (videoStream.Codec.ToLowerInvariant())
                     switch (videoStream.Codec.ToLowerInvariant())
@@ -2552,8 +2576,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                         case "h265":
                             if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             {
                             {
-                                // return "-c:v hevc_qsv -load_plugin hevc_hw ";
-                                return "-c:v hevc_qsv";
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_qsv";
                             }
                             }
                             break;
                             break;
                         case "mpeg2video":
                         case "mpeg2video":
@@ -2568,6 +2592,19 @@ namespace MediaBrowser.Controller.MediaEncoding
                                 return "-c:v vc1_qsv";
                                 return "-c:v vc1_qsv";
                             }
                             }
                             break;
                             break;
+                        case "vp8":
+                            if (_mediaEncoder.SupportsDecoder("vp8_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v vp8_qsv";
+                            }
+                            break;
+                        case "vp9":
+                            if (_mediaEncoder.SupportsDecoder("vp9_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_qsv";
+                            }
+                            break;
                     }
                     }
                 }
                 }
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
@@ -2578,12 +2615,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";
                             }
                             }
                             break;
                             break;
@@ -2591,7 +2622,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                         case "h265":
                             if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             {
                             {
-                                return "-c:v hevc_cuvid";
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_cuvid";
                             }
                             }
                             break;
                             break;
                         case "mpeg2video":
                         case "mpeg2video":
@@ -2612,6 +2644,19 @@ namespace MediaBrowser.Controller.MediaEncoding
                                 return "-c:v mpeg4_cuvid";
                                 return "-c:v mpeg4_cuvid";
                             }
                             }
                             break;
                             break;
+                        case "vp8":
+                            if (_mediaEncoder.SupportsDecoder("vp8_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v vp8_cuvid";
+                            }
+                            break;
+                        case "vp9":
+                            if (_mediaEncoder.SupportsDecoder("vp9_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_cuvid";
+                            }
+                            break;
                     }
                     }
                 }
                 }
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "mediacodec", StringComparison.OrdinalIgnoreCase))
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "mediacodec", StringComparison.OrdinalIgnoreCase))
@@ -2629,7 +2674,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                         case "h265":
                             if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase))
                             {
                             {
-                                return "-c:v hevc_mediacodec";
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_mediacodec";
                             }
                             }
                             break;
                             break;
                         case "mpeg2video":
                         case "mpeg2video":
@@ -2653,7 +2699,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "vp9":
                         case "vp9":
                             if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
                             if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase))
                             {
                             {
-                                return "-c:v vp9_mediacodec";
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_mediacodec";
                             }
                             }
                             break;
                             break;
                     }
                     }
@@ -2691,27 +2738,148 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
                 }
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+                    switch (videoStream.Codec.ToLowerInvariant())
                     {
                     {
-                        if (Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1))
-                            return "-hwaccel d3d11va";
-                        else
-                            return "-hwaccel dxva2";
+                        case "avc":
+                        case "h264":
+                            return GetHwaccelType(state, encodingOptions, "h264");
+                        case "hevc":
+                        case "h265":
+                            return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc");
+                        case "mpeg2video":
+                            return GetHwaccelType(state, encodingOptions, "mpeg2video");
+                        case "vc1":
+                            return GetHwaccelType(state, encodingOptions, "vc1");
+                        case "mpeg4":
+                            return GetHwaccelType(state, encodingOptions, "mpeg4");
+                        case "vp9":
+                            return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9");
                     }
                     }
-                    else
+                }
+                else if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+                {
+                    switch (videoStream.Codec.ToLowerInvariant())
+                    {
+                        case "avc":
+                        case "h264":
+                            return GetHwaccelType(state, encodingOptions, "h264");
+                        case "hevc":
+                        case "h265":
+                            return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc");
+                        case "mpeg2video":
+                            return GetHwaccelType(state, encodingOptions, "mpeg2video");
+                        case "vc1":
+                            return GetHwaccelType(state, encodingOptions, "vc1");
+                        case "vp8":
+                            return GetHwaccelType(state, encodingOptions, "vp8");
+                        case "vp9":
+                            return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9");
+                    }
+                }
+                else if (string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+                {
+                    switch (videoStream.Codec.ToLowerInvariant())
                     {
                     {
-                        return "-hwaccel vaapi";
+                        case "avc":
+                        case "h264":
+                            if (_mediaEncoder.SupportsDecoder("h264_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v h264_opencl";
+                            }
+                            break;
+                        case "hevc":
+                        case "h265":
+                            if (_mediaEncoder.SupportsDecoder("hevc_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_opencl";
+                            }
+                            break;
+                        case "mpeg2video":
+                            if (_mediaEncoder.SupportsDecoder("mpeg2_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v mpeg2_opencl";
+                            }
+                            break;
+                        case "mpeg4":
+                            if (_mediaEncoder.SupportsDecoder("mpeg4_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v mpeg4_opencl";
+                            }
+                            break;
+                        case "vc1":
+                            if (_mediaEncoder.SupportsDecoder("vc1_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v vc1_opencl";
+                            }
+                            break;
+                        case "vp8":
+                            if (_mediaEncoder.SupportsDecoder("vp8_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return "-c:v vp8_opencl";
+                            }
+                            break;
+                        case "vp9":
+                            if (_mediaEncoder.SupportsDecoder("vp9_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase))
+                            {
+                                return (isColorDepth10 &&
+                                    !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_opencl";
+                            }
+                            break;
                     }
                     }
                 }
                 }
             }
             }
 
 
+            var whichCodec = videoStream.Codec.ToLowerInvariant();
+            switch (whichCodec)
+            {
+                case "avc":
+                    whichCodec = "h264";
+                    break;
+                case "h265":
+                    whichCodec = "hevc";
+                    break;
+            }
+
             // Avoid a second attempt if no hardware acceleration is being used
             // Avoid a second attempt if no hardware acceleration is being used
-            encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
+            encodingOptions.HardwareDecodingCodecs = encodingOptions.HardwareDecodingCodecs.Where(val => val != whichCodec).ToArray();
 
 
             // leave blank so ffmpeg will decide
             // leave blank so ffmpeg will decide
             return null;
             return null;
         }
         }
 
 
+        /// <summary>
+        /// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system
+        /// </summary>
+        public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec)
+        {
+            var isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
+            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)
+                {
+                    return "-hwaccel vaapi";
+                }
+                else if (isWindows8orLater)
+                {
+                    return "-hwaccel d3d11va";
+                }
+                else
+                {
+                    return "-hwaccel dxva2";
+                }
+            }
+
+            return null;
+        }
+
         public string GetSubtitleEmbedArguments(EncodingJobInfo state)
         public string GetSubtitleEmbedArguments(EncodingJobInfo state)
         {
         {
             if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
             if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)

+ 15 - 2
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -27,12 +27,26 @@ namespace MediaBrowser.Controller.MediaEncoding
         string EncoderPath { get; }
         string EncoderPath { get; }
 
 
         /// <summary>
         /// <summary>
-        /// Supportses the decoder.
+        /// Whether given encoder codec is supported.
+        /// </summary>
+        /// <param name="encoder">The encoder.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+        bool SupportsEncoder(string encoder);
+
+        /// <summary>
+        /// Whether given decoder codec is supported.
         /// </summary>
         /// </summary>
         /// <param name="decoder">The decoder.</param>
         /// <param name="decoder">The decoder.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         bool SupportsDecoder(string decoder);
         bool SupportsDecoder(string decoder);
 
 
+        /// <summary>
+        /// Whether given hardware acceleration type is supported.
+        /// </summary>
+        /// <param name="hwaccel">The hwaccel.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+        bool SupportsHwaccel(string hwaccel);
+
         /// <summary>
         /// <summary>
         /// Extracts the audio image.
         /// Extracts the audio image.
         /// </summary>
         /// </summary>
@@ -98,7 +112,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         void SetFFmpegPath();
         void SetFFmpegPath();
 
 
         void UpdateEncoderPath(string path, string pathType);
         void UpdateEncoderPath(string path, string pathType);
-        bool SupportsEncoder(string encoder);
 
 
         IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber);
         IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber);
     }
     }

+ 65 - 16
MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs

@@ -14,23 +14,45 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
 
         private static readonly string[] requiredDecoders = new[]
         private static readonly string[] requiredDecoders = new[]
         {
         {
+            "h264",
+            "hevc",
             "mpeg2video",
             "mpeg2video",
+            "mpeg4",
+            "msmpeg4",
+            "dts",
+            "ac3",
+            "aac",
+            "mp3",
             "h264_qsv",
             "h264_qsv",
             "hevc_qsv",
             "hevc_qsv",
             "mpeg2_qsv",
             "mpeg2_qsv",
-            "mpeg2_mmal",
-            "mpeg4_mmal",
             "vc1_qsv",
             "vc1_qsv",
-            "vc1_mmal",
+            "vp8_qsv",
+            "vp9_qsv",
             "h264_cuvid",
             "h264_cuvid",
             "hevc_cuvid",
             "hevc_cuvid",
-            "dts",
-            "ac3",
-            "aac",
-            "mp3",
-            "h264",
+            "mpeg2_cuvid",
+            "vc1_cuvid",
+            "mpeg4_cuvid",
+            "vp8_cuvid",
+            "vp9_cuvid",
             "h264_mmal",
             "h264_mmal",
-            "hevc"
+            "mpeg2_mmal",
+            "mpeg4_mmal",
+            "vc1_mmal",
+            "h264_mediacodec",
+            "hevc_mediacodec",
+            "mpeg2_mediacodec",
+            "mpeg4_mediacodec",
+            "vp8_mediacodec",
+            "vp9_mediacodec",
+            "h264_opencl",
+            "hevc_opencl",
+            "mpeg2_opencl",
+            "mpeg4_opencl",
+            "vp8_opencl",
+            "vp9_opencl",
+            "vc1_opencl"
         };
         };
 
 
         private static readonly string[] requiredEncoders = new[]
         private static readonly string[] requiredEncoders = new[]
@@ -43,22 +65,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
             "libvpx-vp9",
             "libvpx-vp9",
             "aac",
             "aac",
             "libfdk_aac",
             "libfdk_aac",
+            "ac3",
             "libmp3lame",
             "libmp3lame",
             "libopus",
             "libopus",
             "libvorbis",
             "libvorbis",
             "srt",
             "srt",
-            "h264_nvenc",
-            "hevc_nvenc",
+            "h264_amf",
+            "hevc_amf",
             "h264_qsv",
             "h264_qsv",
             "hevc_qsv",
             "hevc_qsv",
-            "h264_omx",
-            "hevc_omx",
+            "h264_nvenc",
+            "hevc_nvenc",
             "h264_vaapi",
             "h264_vaapi",
             "hevc_vaapi",
             "hevc_vaapi",
+            "h264_omx",
+            "hevc_omx",
             "h264_v4l2m2m",
             "h264_v4l2m2m",
-            "ac3",
-            "h264_amf",
-            "hevc_amf"
+            "h264_videotoolbox",
+            "hevc_videotoolbox"
         };
         };
 
 
         // Try and use the individual library versions to determine a FFmpeg version
         // Try and use the individual library versions to determine a FFmpeg version
@@ -159,6 +183,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
 
         public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder);
         public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder);
 
 
+        public IEnumerable<string> GetHwaccels() => GetHwaccelTypes();
+
         /// <summary>
         /// <summary>
         /// Using the output from "ffmpeg -version" work out the FFmpeg version.
         /// Using the output from "ffmpeg -version" work out the FFmpeg version.
         /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
         /// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
@@ -218,6 +244,29 @@ namespace MediaBrowser.MediaEncoding.Encoder
             Decoder
             Decoder
         }
         }
 
 
+        private IEnumerable<string> GetHwaccelTypes()
+        {
+            string output = null;
+            try
+            {
+                output = GetProcessOutput(_encoderPath, "-hwaccels");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError(ex, "Error detecting available hwaccel types");
+            }
+
+            if (string.IsNullOrWhiteSpace(output))
+            {
+                return Enumerable.Empty<string>();
+            }
+
+            var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList();
+            _logger.LogInformation("Available hwaccel types: {Types}", found);
+
+            return found;
+        }
+
         private IEnumerable<string> GetCodecs(Codec codec)
         private IEnumerable<string> GetCodecs(Codec codec)
         {
         {
             string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";
             string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";

+ 13 - 0
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -111,6 +111,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
 
                 SetAvailableDecoders(validator.GetDecoders());
                 SetAvailableDecoders(validator.GetDecoders());
                 SetAvailableEncoders(validator.GetEncoders());
                 SetAvailableEncoders(validator.GetEncoders());
+                SetAvailableHwaccels(validator.GetHwaccels());
             }
             }
 
 
             _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
             _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
@@ -257,6 +258,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
             // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
             // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
         }
         }
 
 
+        private List<string> _hwaccels = new List<string>();
+        public void SetAvailableHwaccels(IEnumerable<string> list)
+        {
+            _hwaccels = list.ToList();
+            //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray()));
+        }
+
         public bool SupportsEncoder(string encoder)
         public bool SupportsEncoder(string encoder)
         {
         {
             return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
             return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
@@ -267,6 +275,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
             return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
         }
         }
 
 
+        public bool SupportsHwaccel(string hwaccel)
+        {
+            return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
+        }
+
         public bool CanEncodeToAudioCodec(string codec)
         public bool CanEncodeToAudioCodec(string codec)
         {
         {
             if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))

+ 1 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -736,7 +736,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
                 var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
 
 
                 // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
                 // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
-                if ((path.EndsWith(".ass") || path.EndsWith(".ssa"))
+                if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt"))
                     && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
                     && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
                         || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
                         || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
                 {
                 {

+ 4 - 1
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -36,7 +36,8 @@ namespace MediaBrowser.Model.Configuration
         public string EncoderPreset { get; set; }
         public string EncoderPreset { get; set; }
 
 
         public string DeinterlaceMethod { get; set; }
         public string DeinterlaceMethod { get; set; }
-
+        public bool EnableDecodingColorDepth10Hevc { get; set; }
+        public bool EnableDecodingColorDepth10Vp9 { get; set; }
         public bool EnableHardwareEncoding { get; set; }
         public bool EnableHardwareEncoding { get; set; }
 
 
         public bool EnableSubtitleExtraction { get; set; }
         public bool EnableSubtitleExtraction { get; set; }
@@ -54,6 +55,8 @@ namespace MediaBrowser.Model.Configuration
             H264Crf = 23;
             H264Crf = 23;
             H265Crf = 28;
             H265Crf = 28;
             DeinterlaceMethod = "yadif";
             DeinterlaceMethod = "yadif";
+            EnableDecodingColorDepth10Hevc = true;
+            EnableDecodingColorDepth10Vp9 = true;
             EnableHardwareEncoding = true;
             EnableHardwareEncoding = true;
             EnableSubtitleExtraction = true;
             EnableSubtitleExtraction = true;
             HardwareDecodingCodecs = new string[] { "h264", "vc1" };
             HardwareDecodingCodecs = new string[] { "h264", "vc1" };