From fac6831653c6db798fc5483df5fcb3668bd93a5b Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 3 Mar 2020 23:56:50 +0800 Subject: [PATCH 1/6] fix various bugs in VAAPI hardware acceleration --- .../Playback/Hls/DynamicHlsService.cs | 20 +- .../MediaEncoding/EncodingHelper.cs | 237 ++++++++++++------ 2 files changed, 175 insertions(+), 82 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 262f517869..734d907746 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -946,8 +946,6 @@ namespace MediaBrowser.Api.Playback.Hls ); } - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultEncoderPreset()); // Unable to force key frames to h264_qsv transcode @@ -962,24 +960,26 @@ namespace MediaBrowser.Api.Playback.Hls //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + // Add resolution params, if specified if (!hasGraphicalSubs) { - args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec, true); + args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec); } - // This is for internal graphical subs + // This is for graphical subs if (hasGraphicalSubs) { args += EncodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec); } - //args += " -flags -global_header"; - } + if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) + { + args += " -start_at_zero"; + } - if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1) - { - args += " -copyts"; + //args += " -flags -global_header"; } if (!string.IsNullOrEmpty(state.OutputVideoSync)) @@ -1024,7 +1024,7 @@ namespace MediaBrowser.Api.Playback.Hls } return string.Format( - "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"", + "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"", inputModifier, EncodingHelper.GetInputArgument(state, encodingOptions), threads, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 342c764146..8fb6170160 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -460,16 +460,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) { - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - var hwOutputFormat = "vaapi"; - - if (hasGraphicalSubs) - { - hwOutputFormat = "yuv420p"; - } - - arg.Append("-hwaccel vaapi -hwaccel_output_format ") - .Append(hwOutputFormat) + arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi") .Append(" -vaapi_device ") .Append(encodingOptions.VaapiDevice) .Append(' '); @@ -503,7 +494,8 @@ namespace MediaBrowser.Controller.MediaEncoding && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { - if (state.VideoStream != null && state.VideoStream.Width.HasValue) + if (state.VideoStream != null && state.VideoStream.Width.HasValue + && !string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) { // This is hacky but not sure how to get the exact subtitle resolution int height = Convert.ToInt32(state.VideoStream.Width.Value / 16.0 * 9.0); @@ -1546,9 +1538,13 @@ namespace MediaBrowser.Controller.MediaEncoding } /// - /// Gets the internal graphical subtitle param. + /// Gets the graphical subtitle param. /// - public string GetGraphicalSubtitleParam(EncodingJobInfo state, EncodingOptions options, string outputVideoCodec) + public string GetGraphicalSubtitleParam( + EncodingJobInfo state, + EncodingOptions options, + string outputVideoCodec, + bool allowTimeStampCopy = true) { var outputSizeParam = string.Empty; @@ -1562,34 +1558,26 @@ namespace MediaBrowser.Controller.MediaEncoding { outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"'); - if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + var index = outputSizeParam.IndexOf("deinterlace", StringComparison.OrdinalIgnoreCase); + if (index != -1) { - var index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase); - if (index != -1) - { - outputSizeParam = "," + outputSizeParam.Substring(index); - } + outputSizeParam = "," + outputSizeParam.Substring(index); } else { - var index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); + index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); if (index != -1) { outputSizeParam = "," + outputSizeParam.Substring(index); } - } - } - - if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - && outputSizeParam.Length == 0) - { - outputSizeParam = ",format=nv12|vaapi,hwupload"; - - // Add parameters to use VAAPI with burn-in subttiles (GH issue #642) - if (state.SubtitleStream != null - && state.SubtitleStream.IsTextSubtitleStream - && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) { - outputSizeParam += ",hwmap=mode=read+write+direct"; + else + { + index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); + if (index != -1) + { + outputSizeParam = "," + outputSizeParam.Substring(index); + } + } } } @@ -1604,11 +1592,29 @@ namespace MediaBrowser.Controller.MediaEncoding state.VideoStream.Width.Value, state.VideoStream.Height.Value); - //For QSV, feed it into hardware encoder now + // For QSV, feed it into hardware encoder now if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { videoSizeParam += ",hwupload=extra_hw_frames=64"; } + + // For VAAPI + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + { + var videoStream = state.VideoStream; + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; + var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); + + if (width.HasValue && height.HasValue) + { + videoSizeParam = string.Format( + CultureInfo.InvariantCulture, + "scale={0}:{1}", + width.Value, + height.Value); + } + } } var mapPrefix = state.SubtitleStream.IsExternal ? @@ -1624,7 +1630,53 @@ namespace MediaBrowser.Controller.MediaEncoding // 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}\""; - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + // When the input may or may not be hardware VAAPI decodable + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) + { + /* + [base]: HW scaling video to OutputSize + [sub]: SW scaling subtitle to FixedOutputSize + [base][sub]: SW overlay + */ + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]format=nv12|vaapi,hwupload{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""; + } + + // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first + else if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding) + { + /* + [base]: SW scaling video to OutputSize + [sub]: SW scaling subtitle to FixedOutputSize + [base][sub]: SW overlay + */ + var videoStream = state.VideoStream; + var codec = videoStream.Codec.ToLowerInvariant(); + + // Assert 10-bit hardware VAAPI decodable + if (!string.IsNullOrEmpty(videoStream.PixelFormat) + && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 + && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))) + { + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload,format=p010le,format=nv12{3}[base];[base][sub]overlay\""; + } + + // Assert 8-bit hardware VAAPI decodable + else if (!string.IsNullOrEmpty(videoStream.PixelFormat) + && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) == -1) + { + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload,format=nv12{3}[base];[base][sub]overlay\""; + } + else + { + outputSizeParam = outputSizeParam.TrimStart(','); + + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; + } + } + + else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { /* QSV in FFMpeg can now setup hardware overlay for transcodes. @@ -1641,7 +1693,14 @@ namespace MediaBrowser.Controller.MediaEncoding } } - return string.Format( + var output = string.Empty; + + if (allowTimeStampCopy) + { + output += " -copyts"; + } + + output += string.Format( CultureInfo.InvariantCulture, retStr, mapPrefix, @@ -1649,6 +1708,8 @@ namespace MediaBrowser.Controller.MediaEncoding state.VideoStream.Index, outputSizeParam, videoSizeParam); + + return output; } private (int? width, int? height) GetFixedOutputSize( @@ -1951,42 +2012,52 @@ namespace MediaBrowser.Controller.MediaEncoding var videoStream = state.VideoStream; var filters = new List(); - // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first - var hwType = options.HardwareAccelerationType ?? string.Empty; - if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding ) - { - filters.Add("hwdownload"); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; + var threeDFormat = state.MediaSource.Video3DFormat; - // If transcoding from 10 bit, transform colour spaces too - if (!string.IsNullOrEmpty(videoStream.PixelFormat) - && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 - && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) - { - filters.Add("format=p010le"); - filters.Add("format=nv12"); - } - else - { - filters.Add("format=nv12"); - } - } - - if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + // When the input may or may not be hardware VAAPI decodable + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) { filters.Add("format=nv12|vaapi"); filters.Add("hwupload"); } - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); - - // If we are software decoding, and hardware encoding - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) - && (string.IsNullOrEmpty(videoDecoder) || !videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))) + // When the input may or may not be hardware QSV decodable + else if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) { 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 + else if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding) + { + var codec = videoStream.Codec.ToLowerInvariant(); + + // Assert 10-bit hardware VAAPI decodable + if (!string.IsNullOrEmpty(videoStream.PixelFormat) + && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 + && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))) + { + filters.Add("hwdownload"); + filters.Add("format=p010le"); + filters.Add("format=nv12"); + } + + // Assert 8-bit hardware VAAPI decodable + else if (!string.IsNullOrEmpty(videoStream.PixelFormat) + && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) == -1) + { + filters.Add("hwdownload"); + filters.Add("format=nv12"); + } + } + + // Add hardware deinterlace filter before scaling filter if (state.DeInterlace("h264", true)) { if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) @@ -1999,6 +2070,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } + // 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)) { @@ -2015,12 +2087,22 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var inputWidth = videoStream?.Width; - var inputHeight = videoStream?.Height; - var threeDFormat = state.MediaSource.Video3DFormat; - + // Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight)); + // Add parameters to use VAAPI with burn-in text subttiles (GH issue #642) + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) + { + if (state.SubtitleStream != null + && state.SubtitleStream.IsTextSubtitleStream + && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) + { + // Test passed on Intel and AMD gfx + filters.Add("hwmap=mode=read+write"); + filters.Add("format=nv12"); + } + } + var output = string.Empty; if (state.SubtitleStream != null @@ -2772,14 +2854,27 @@ namespace MediaBrowser.Controller.MediaEncoding var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasCopyTs = false; + // Add resolution params, if specified if (!hasGraphicalSubs) { var outputSizeParam = GetOutputSizeParam(state, encodingOptions, videoCodec); + args += outputSizeParam; + hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1; } + // This is for graphical subs + if (hasGraphicalSubs) + { + var graphicalSubtitleParam = GetGraphicalSubtitleParam(state, encodingOptions, videoCodec); + + args += graphicalSubtitleParam; + + hasCopyTs = graphicalSubtitleParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1; + } + if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps) { if (!hasCopyTs) @@ -2787,13 +2882,12 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -copyts"; } - args += " -avoid_negative_ts disabled -start_at_zero"; - } + args += " -avoid_negative_ts disabled"; - // This is for internal graphical subs - if (hasGraphicalSubs) - { - args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec); + if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) + { + args += " -start_at_zero"; + } } var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultPreset); @@ -2899,6 +2993,5 @@ namespace MediaBrowser.Controller.MediaEncoding string.Empty, string.Empty).Trim(); } - } } From 111095c2b0554adad2f5a0f93883dc9d050bcdd4 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 21 Mar 2020 03:29:33 +0800 Subject: [PATCH 2/6] fix QSV HWA failed when burning text subtitles ffmpeg 4.3+ is required for better transcoding speed(more than twice increase). Using qsv on Linux also requires a fix in ffmpeg 4.3+. See https://github.com/FFmpeg/FFmpeg/commit/74007dd86a87289a075926704fae5bd8ef313bb5 --- .../MediaEncoding/EncodingHelper.cs | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 8fb6170160..66d8afb982 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -471,20 +471,23 @@ namespace MediaBrowser.Controller.MediaEncoding { var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); var outputVideoCodec = GetVideoEncoder(state, encodingOptions); + + var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if (encodingOptions.EnableHardwareEncoding && outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + if (!hasTextSubs) { - if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + if (encodingOptions.EnableHardwareEncoding && outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase)) { - arg.Append("-hwaccel qsv "); - } - else - { - arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + { + arg.Append("-hwaccel qsv "); + } + else + { + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + } } } - - arg.Append(videoDecoder + " "); } arg.Append("-i ") @@ -1749,7 +1752,8 @@ namespace MediaBrowser.Controller.MediaEncoding return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight)); } - public List GetScalingFilters(int? videoWidth, + public List GetScalingFilters(EncodingJobInfo state, + int? videoWidth, int? videoHeight, Video3DFormat? threedFormat, string videoDecoder, @@ -1768,7 +1772,9 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxWidth, requestedMaxHeight); - if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + + if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || ((string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)) && width.HasValue && height.HasValue) { @@ -2017,6 +2023,8 @@ namespace MediaBrowser.Controller.MediaEncoding var inputHeight = videoStream?.Height; var threeDFormat = state.MediaSource.Video3DFormat; + var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + // When the input may or may not be hardware VAAPI decodable if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) { @@ -2027,8 +2035,11 @@ namespace MediaBrowser.Controller.MediaEncoding // When the input may or may not be hardware QSV decodable else if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) { - filters.Add("format=nv12|qsv"); - filters.Add("hwupload=extra_hw_frames=64"); + if (!hasTextSubs) + { + 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 @@ -2088,7 +2099,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr - filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight)); + filters.AddRange(GetScalingFilters(state, inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight)); // Add parameters to use VAAPI with burn-in text subttiles (GH issue #642) if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && options.EnableHardwareEncoding) @@ -2607,6 +2618,12 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { + // cuvid decoder does not support 10-bit input + if ((videoStream.BitDepth ?? 8) > 8) + { + encodingOptions.HardwareDecodingCodecs = Array.Empty(); + return null; + } return "-c:v h264_cuvid "; } break; From 0c6ac38454e3b997e4d1d072f2957a918f953e1c Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 21 Mar 2020 20:20:39 +0800 Subject: [PATCH 3/6] fix graphical subtitle scaling for NVDEC --- .../MediaEncoding/EncodingHelper.cs | 119 +++++++----------- 1 file changed, 42 insertions(+), 77 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 66d8afb982..5241bc2eba 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -476,12 +476,15 @@ namespace MediaBrowser.Controller.MediaEncoding if (!hasTextSubs) { - if (encodingOptions.EnableHardwareEncoding && outputVideoCodec.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + // While using QSV encoder + if ((outputVideoCodec ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) { - if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + // While using QSV decoder + if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) { arg.Append("-hwaccel qsv "); } + // While using SW decoder else { arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); @@ -497,18 +500,6 @@ namespace MediaBrowser.Controller.MediaEncoding && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { - if (state.VideoStream != null && state.VideoStream.Width.HasValue - && !string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) - { - // This is hacky but not sure how to get the exact subtitle resolution - int height = Convert.ToInt32(state.VideoStream.Width.Value / 16.0 * 9.0); - - arg.Append(" -canvas_size ") - .Append(state.VideoStream.Width.Value.ToString(CultureInfo.InvariantCulture)) - .Append(':') - .Append(height.ToString(CultureInfo.InvariantCulture)); - } - var subtitlePath = state.SubtitleStream.Path; if (string.Equals(Path.GetExtension(subtitlePath), ".sub", StringComparison.OrdinalIgnoreCase)) @@ -1546,8 +1537,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetGraphicalSubtitleParam( EncodingJobInfo state, EncodingOptions options, - string outputVideoCodec, - bool allowTimeStampCopy = true) + string outputVideoCodec) { var outputSizeParam = string.Empty; @@ -1561,30 +1551,39 @@ namespace MediaBrowser.Controller.MediaEncoding { outputSizeParam = GetOutputSizeParam(state, options, outputVideoCodec).TrimEnd('"'); - var index = outputSizeParam.IndexOf("deinterlace", StringComparison.OrdinalIgnoreCase); + var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase); if (index != -1) { outputSizeParam = "," + outputSizeParam.Substring(index); } else { - index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); + index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase); if (index != -1) { outputSizeParam = "," + outputSizeParam.Substring(index); } else { - index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); + index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); if (index != -1) { outputSizeParam = "," + outputSizeParam.Substring(index); } + else + { + index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); + if (index != -1) + { + outputSizeParam = "," + outputSizeParam.Substring(index); + } + } } } } var videoSizeParam = string.Empty; + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); // Setup subtitle scaling if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) @@ -1601,8 +1600,9 @@ namespace MediaBrowser.Controller.MediaEncoding videoSizeParam += ",hwupload=extra_hw_frames=64"; } - // For VAAPI - if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + // For VAAPI and CUVID decoder + if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + || (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) { var videoStream = state.VideoStream; var inputWidth = videoStream?.Width; @@ -1628,8 +1628,6 @@ namespace MediaBrowser.Controller.MediaEncoding ? 0 : state.SubtitleStream.Index; - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); - // 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}\""; @@ -1641,7 +1639,8 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]format=nv12|vaapi,hwupload{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""; + outputSizeParam = outputSizeParam.TrimStart(','); + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""; } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first @@ -1652,31 +1651,8 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - var videoStream = state.VideoStream; - var codec = videoStream.Codec.ToLowerInvariant(); - - // Assert 10-bit hardware VAAPI decodable - if (!string.IsNullOrEmpty(videoStream.PixelFormat) - && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 - && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))) - { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload,format=p010le,format=nv12{3}[base];[base][sub]overlay\""; - } - - // Assert 8-bit hardware VAAPI decodable - else if (!string.IsNullOrEmpty(videoStream.PixelFormat) - && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) == -1) - { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwdownload,format=nv12{3}[base];[base][sub]overlay\""; - } - else - { - outputSizeParam = outputSizeParam.TrimStart(','); - - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; - } + outputSizeParam = outputSizeParam.TrimStart(','); + retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; } else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) @@ -1696,14 +1672,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } - var output = string.Empty; - - if (allowTimeStampCopy) - { - output += " -copyts"; - } - - output += string.Format( + return string.Format( CultureInfo.InvariantCulture, retStr, mapPrefix, @@ -1711,8 +1680,6 @@ namespace MediaBrowser.Controller.MediaEncoding state.VideoStream.Index, outputSizeParam, videoSizeParam); - - return output; } private (int? width, int? height) GetFixedOutputSize( @@ -1774,7 +1741,7 @@ namespace MediaBrowser.Controller.MediaEncoding var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || ((string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)) + if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs) && width.HasValue && height.HasValue) { @@ -1804,7 +1771,7 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add(string.Format(CultureInfo.InvariantCulture, "scale_{0}=format=nv12", vaapi_or_qsv)); } } - else if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 + else if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 && width.HasValue && height.HasValue) { @@ -2008,8 +1975,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetOutputSizeParam( EncodingJobInfo state, EncodingOptions options, - string outputVideoCodec, - bool allowTimeStampCopy = true) + string outputVideoCodec) { // http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/ @@ -2046,10 +2012,10 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && !options.EnableHardwareEncoding) { var codec = videoStream.Codec.ToLowerInvariant(); + var pixelFormat = videoStream.PixelFormat.ToLowerInvariant(); // Assert 10-bit hardware VAAPI decodable - if (!string.IsNullOrEmpty(videoStream.PixelFormat) - && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 + if ((pixelFormat ?? string.Empty).IndexOf("p10", StringComparison.OrdinalIgnoreCase) != -1 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))) @@ -2060,8 +2026,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Assert 8-bit hardware VAAPI decodable - else if (!string.IsNullOrEmpty(videoStream.PixelFormat) - && videoStream.PixelFormat.IndexOf("p10", StringComparison.OrdinalIgnoreCase) == -1) + else if ((pixelFormat ?? string.Empty).IndexOf("p10", StringComparison.OrdinalIgnoreCase) == -1) { filters.Add("hwdownload"); filters.Add("format=nv12"); @@ -2077,13 +2042,18 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv")); + if (!hasTextSubs) + { + filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv")); + } } } // Add software deinterlace filter before scaling filter - if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) - && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) + 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))) { var inputFramerate = videoStream?.RealFrameRate; @@ -2130,11 +2100,6 @@ namespace MediaBrowser.Controller.MediaEncoding { filters.Add("hwmap"); } - - if (allowTimeStampCopy) - { - output += " -copyts"; - } } if (filters.Count > 0) @@ -2311,7 +2276,7 @@ namespace MediaBrowser.Controller.MediaEncoding { inputModifier += " " + videoDecoder; - if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1) + if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) { var videoStream = state.VideoStream; var inputWidth = videoStream?.Width; @@ -2320,7 +2285,7 @@ namespace MediaBrowser.Controller.MediaEncoding var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); - if ((videoDecoder ?? string.Empty).IndexOf("_cuvid", StringComparison.OrdinalIgnoreCase) != -1 + if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 && width.HasValue && height.HasValue) { From 0e9d9a7897fdb30b5cbfd270362eb35d31b24e94 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 29 Mar 2020 04:27:49 +0800 Subject: [PATCH 4/6] fix the incorrect HLS time while using hw encoders --- .../Playback/Hls/DynamicHlsService.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 734d907746..c6748d7127 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -927,35 +927,41 @@ namespace MediaBrowser.Api.Playback.Hls } else { + var gopArg = string.Empty; var keyFrameArg = string.Format( CultureInfo.InvariantCulture, " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"", GetStartNumber(state) * state.SegmentLength, state.SegmentLength); - if (state.TargetFramerate.HasValue) + + var framerate = state.VideoStream?.RealFrameRate; + + if (framerate != null && framerate.HasValue) { // This is to make sure keyframe interval is limited to our segment, // as forcing keyframes is not enough. // Example: we encoded half of desired length, then codec detected // scene cut and inserted a keyframe; next forced keyframe would // be created outside of segment, which breaks seeking. - keyFrameArg += string.Format( + gopArg = string.Format( CultureInfo.InvariantCulture, - " -g {0} -keyint_min {0}", - (int)(state.SegmentLength * state.TargetFramerate) + " -g {0} -keyint_min {0} -sc_threshold 0", + state.SegmentLength * Math.Ceiling(Convert.ToDecimal(framerate)) ); } args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultEncoderPreset()); - // Unable to force key frames to h264_qsv transcode - if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + // Unable to force key frames using these hw encoders, set key frames by GOP + if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase)) { - Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv"); + args += " " + gopArg; } else { - args += " " + keyFrameArg; + args += " " + keyFrameArg + gopArg; } //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; From 0af353404cfbd941cdf7c8f334292db6c38ec74a Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 30 Mar 2020 14:46:05 +0800 Subject: [PATCH 5/6] fix the UTF-16 error while burning ass/ssa subtitles --- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 4 +++- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index c6748d7127..e22934f794 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -946,7 +946,7 @@ namespace MediaBrowser.Api.Playback.Hls gopArg = string.Format( CultureInfo.InvariantCulture, " -g {0} -keyint_min {0} -sc_threshold 0", - state.SegmentLength * Math.Ceiling(Convert.ToDecimal(framerate)) + Math.Ceiling(state.SegmentLength * framerate.Value) ); } @@ -980,6 +980,8 @@ namespace MediaBrowser.Api.Playback.Hls args += EncodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec); } + // -start_at_zero is necessary to use with -ss when seeking, + // otherwise the target position cannot be determined. if (!(state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream)) { args += " -start_at_zero"; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index a4a7595d29..5c2dc927b7 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -730,6 +730,14 @@ 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")) + && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) + || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) + { + charset = ""; + } + _logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path); return charset; From 95c5c086104456f0e4c0a6b98d126be02f6f3cb0 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 31 Mar 2020 04:04:55 +0800 Subject: [PATCH 6/6] minor improvements --- .../Playback/Hls/DynamicHlsService.cs | 14 +++++++------- .../MediaEncoding/EncodingHelper.cs | 18 +++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index e22934f794..4fa930f881 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -942,7 +942,8 @@ namespace MediaBrowser.Api.Playback.Hls // as forcing keyframes is not enough. // Example: we encoded half of desired length, then codec detected // scene cut and inserted a keyframe; next forced keyframe would - // be created outside of segment, which breaks seeking. + // be created outside of segment, which breaks seeking + // -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe gopArg = string.Format( CultureInfo.InvariantCulture, " -g {0} -keyint_min {0} -sc_threshold 0", @@ -968,17 +969,16 @@ namespace MediaBrowser.Api.Playback.Hls var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - // Add resolution params, if specified - if (!hasGraphicalSubs) - { - args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec); - } - // This is for graphical subs if (hasGraphicalSubs) { args += EncodingHelper.GetGraphicalSubtitleParam(state, encodingOptions, codec); } + // Add resolution params, if specified + else + { + args += EncodingHelper.GetOutputSizeParam(state, encodingOptions, codec); + } // -start_at_zero is necessary to use with -ss when seeking, // otherwise the target position cannot be determined. diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5241bc2eba..4e2e441134 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1588,9 +1588,11 @@ namespace MediaBrowser.Controller.MediaEncoding // 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}", + "scale={0}:{1}:force_original_aspect_ratio=decrease", state.VideoStream.Width.Value, state.VideoStream.Height.Value); @@ -1601,6 +1603,8 @@ 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 (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) || (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) { @@ -1613,7 +1617,7 @@ namespace MediaBrowser.Controller.MediaEncoding { videoSizeParam = string.Format( CultureInfo.InvariantCulture, - "scale={0}:{1}", + "scale={0}:{1}:force_original_aspect_ratio=decrease", width.Value, height.Value); } @@ -1741,7 +1745,7 @@ namespace MediaBrowser.Controller.MediaEncoding var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs) + if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs) && width.HasValue && height.HasValue) { @@ -2043,7 +2047,7 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { if (!hasTextSubs) - { + { filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv")); } } @@ -2051,9 +2055,9 @@ namespace MediaBrowser.Controller.MediaEncoding // 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))) + && !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))) { var inputFramerate = videoStream?.RealFrameRate;