ソースを参照

Merge pull request #2809 from nyanmisaka/hwaccel

Add more separate hardware decoding toggles, support videotoolbox
Anthony Lavado 5 年 前
コミット
25f8e596cb

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

@@ -74,7 +74,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                     {"omx",                  hwEncoder + "_omx"},
                     {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
                     {"mediacodec",           hwEncoder + "_mediacodec"},
-                    {"vaapi",                hwEncoder + "_vaapi"}
+                    {"vaapi",                hwEncoder + "_vaapi"},
+                    {"videotoolbox",         hwEncoder + "_videotoolbox"}
                 };
 
                 if (!string.IsNullOrEmpty(hwType)
@@ -104,7 +105,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 return false;
             }
 
-            return true;
+            return _mediaEncoder.SupportsHwaccel("vaapi");
+
         }
 
         /// <summary>
@@ -445,31 +447,41 @@ namespace MediaBrowser.Controller.MediaEncoding
         public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
         {
             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
                 && 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
                 && 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;
 
                 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 ");
                         }
@@ -527,6 +539,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 || 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)
         {
             if (IsH264(stream))
@@ -551,8 +565,8 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
                 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(
                         CultureInfo.InvariantCulture,
                         " -maxrate:v {0} -bufsize:v {1} -b:v {0}",
@@ -1525,8 +1539,9 @@ namespace MediaBrowser.Controller.MediaEncoding
             EncodingOptions options,
             string outputVideoCodec)
         {
-            var outputSizeParam = string.Empty;
+            outputVideoCodec ??= string.Empty;
 
+            var outputSizeParam = string.Empty;
             var request = state.BaseRequest;
 
             // Add resolution params, if specified
@@ -1569,16 +1584,14 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
 
             var videoSizeParam = string.Empty;
-            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
+            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
 
             // Setup subtitle scaling
             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(
                     CultureInfo.InvariantCulture,
-                    "scale={0}:{1}:force_original_aspect_ratio=decrease",
+                    "scale={0}:{1}",
                     state.VideoStream.Width.Value,
                     state.VideoStream.Height.Value);
 
@@ -1591,8 +1604,10 @@ namespace MediaBrowser.Controller.MediaEncoding
                 // 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 ((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 inputWidth = videoStream?.Width;
@@ -1603,7 +1618,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     {
                         videoSizeParam = string.Format(
                         CultureInfo.InvariantCulture,
-                        "scale={0}:{1}:force_original_aspect_ratio=decrease",
+                        "scale={0}:{1}",
                         width.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
-            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))
             {
                 /*
@@ -1652,7 +1667,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     For software decoding and hardware encoding option, frames must be hwuploaded into hardware
                     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}\"";
                 }
@@ -1975,7 +1990,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             var videoStream = state.VideoStream;
             var filters = new List<string>();
 
-            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options);
+            var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty;
             var inputWidth = videoStream?.Width;
             var inputHeight = videoStream?.Height;
             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
-            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))
             {
                 var codec = videoStream.Codec.ToLowerInvariant();
@@ -2509,16 +2524,17 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// </summary>
         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))
             {
                 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.
             // 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.
@@ -2531,6 +2547,14 @@ namespace MediaBrowser.Controller.MediaEncoding
                 && !string.IsNullOrEmpty(videoStream.Codec)
                 && !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))
                 {
                     switch (videoStream.Codec.ToLowerInvariant())
@@ -2552,8 +2576,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                             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;
                         case "mpeg2video":
@@ -2568,6 +2592,19 @@ namespace MediaBrowser.Controller.MediaEncoding
                                 return "-c:v vc1_qsv";
                             }
                             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))
@@ -2578,12 +2615,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<string>();
-                                    return null;
-                                }
                                 return "-c:v h264_cuvid";
                             }
                             break;
@@ -2591,7 +2622,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                             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;
                         case "mpeg2video":
@@ -2612,6 +2644,19 @@ namespace MediaBrowser.Controller.MediaEncoding
                                 return "-c:v mpeg4_cuvid";
                             }
                             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))
@@ -2629,7 +2674,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "h265":
                             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;
                         case "mpeg2video":
@@ -2653,7 +2699,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                         case "vp9":
                             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;
                     }
@@ -2691,27 +2738,148 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
                 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
-            encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
+            encodingOptions.HardwareDecodingCodecs = encodingOptions.HardwareDecodingCodecs.Where(val => val != whichCodec).ToArray();
 
             // leave blank so ffmpeg will decide
             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)
         {
             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; }
 
         /// <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>
         /// <param name="decoder">The decoder.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         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>
         /// Extracts the audio image.
         /// </summary>
@@ -98,7 +112,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         void SetFFmpegPath();
 
         void UpdateEncoderPath(string path, string pathType);
-        bool SupportsEncoder(string encoder);
 
         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[]
         {
+            "h264",
+            "hevc",
             "mpeg2video",
+            "mpeg4",
+            "msmpeg4",
+            "dts",
+            "ac3",
+            "aac",
+            "mp3",
             "h264_qsv",
             "hevc_qsv",
             "mpeg2_qsv",
-            "mpeg2_mmal",
-            "mpeg4_mmal",
             "vc1_qsv",
-            "vc1_mmal",
+            "vp8_qsv",
+            "vp9_qsv",
             "h264_cuvid",
             "hevc_cuvid",
-            "dts",
-            "ac3",
-            "aac",
-            "mp3",
-            "h264",
+            "mpeg2_cuvid",
+            "vc1_cuvid",
+            "mpeg4_cuvid",
+            "vp8_cuvid",
+            "vp9_cuvid",
             "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[]
@@ -43,22 +65,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
             "libvpx-vp9",
             "aac",
             "libfdk_aac",
+            "ac3",
             "libmp3lame",
             "libopus",
             "libvorbis",
             "srt",
-            "h264_nvenc",
-            "hevc_nvenc",
+            "h264_amf",
+            "hevc_amf",
             "h264_qsv",
             "hevc_qsv",
-            "h264_omx",
-            "hevc_omx",
+            "h264_nvenc",
+            "hevc_nvenc",
             "h264_vaapi",
             "hevc_vaapi",
+            "h264_omx",
+            "hevc_omx",
             "h264_v4l2m2m",
-            "ac3",
-            "h264_amf",
-            "hevc_amf"
+            "h264_videotoolbox",
+            "hevc_videotoolbox"
         };
 
         // 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> GetHwaccels() => GetHwaccelTypes();
+
         /// <summary>
         /// 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
@@ -218,6 +244,29 @@ namespace MediaBrowser.MediaEncoding.Encoder
             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)
         {
             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());
                 SetAvailableEncoders(validator.GetEncoders());
+                SetAvailableHwaccels(validator.GetHwaccels());
             }
 
             _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()));
         }
 
+        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)
         {
             return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
@@ -267,6 +275,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
         }
 
+        public bool SupportsHwaccel(string hwaccel)
+        {
+            return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
+        }
+
         public bool CanEncodeToAudioCodec(string codec)
         {
             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;
 
                 // 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-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 DeinterlaceMethod { get; set; }
-
+        public bool EnableDecodingColorDepth10Hevc { get; set; }
+        public bool EnableDecodingColorDepth10Vp9 { get; set; }
         public bool EnableHardwareEncoding { get; set; }
 
         public bool EnableSubtitleExtraction { get; set; }
@@ -54,6 +55,8 @@ namespace MediaBrowser.Model.Configuration
             H264Crf = 23;
             H265Crf = 28;
             DeinterlaceMethod = "yadif";
+            EnableDecodingColorDepth10Hevc = true;
+            EnableDecodingColorDepth10Vp9 = true;
             EnableHardwareEncoding = true;
             EnableSubtitleExtraction = true;
             HardwareDecodingCodecs = new string[] { "h264", "vc1" };