aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs')
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs1181
1 files changed, 696 insertions, 485 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 24cd141dc..c120e08fa 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary>
public const string ValidationRegex = @"^[a-zA-Z0-9\-\._,|]{0,40}$";
+ private const string _defaultMjpegEncoder = "mjpeg";
+
private const string QsvAlias = "qs";
private const string VaapiAlias = "va";
private const string D3d11vaAlias = "dx11";
@@ -69,11 +71,14 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly Version _minFFmpegAdvancedTonemapMode = new Version(7, 0, 1);
private readonly Version _minFFmpegAlteredVaVkInterop = new Version(7, 0, 1);
private readonly Version _minFFmpegQsvVppTonemapOption = new Version(7, 0, 1);
+ private readonly Version _minFFmpegQsvVppOutRangeOption = new Version(7, 0, 1);
+ private readonly Version _minFFmpegVaapiDeviceVendorId = new Version(7, 0, 1);
+ private readonly Version _minFFmpegQsvVppScaleModeOption = new Version(6, 0);
private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled);
- private static readonly string[] _videoProfilesH264 = new[]
- {
+ private static readonly string[] _videoProfilesH264 =
+ [
"ConstrainedBaseline",
"Baseline",
"Extended",
@@ -82,20 +87,20 @@ namespace MediaBrowser.Controller.MediaEncoding
"ProgressiveHigh",
"ConstrainedHigh",
"High10"
- };
+ ];
- private static readonly string[] _videoProfilesH265 = new[]
- {
+ private static readonly string[] _videoProfilesH265 =
+ [
"Main",
"Main10"
- };
+ ];
- private static readonly string[] _videoProfilesAv1 = new[]
- {
+ private static readonly string[] _videoProfilesAv1 =
+ [
"Main",
"High",
"Professional",
- };
+ ];
private static readonly HashSet<string> _mp4ContainerNames = new(StringComparer.OrdinalIgnoreCase)
{
@@ -107,8 +112,8 @@ namespace MediaBrowser.Controller.MediaEncoding
"m4v",
};
- private static readonly string[] _legacyTonemapModes = new[] { "max", "rgb" };
- private static readonly string[] _advancedTonemapModes = new[] { "lum", "itp" };
+ private static readonly TonemappingMode[] _legacyTonemapModes = [TonemappingMode.max, TonemappingMode.rgb];
+ private static readonly TonemappingMode[] _advancedTonemapModes = [TonemappingMode.lum, TonemappingMode.itp];
// Set max transcoding channels for encoders that can't handle more than a set amount of channels
// AAC, FLAC, ALAC, libopus, libvorbis encoders all support at least 8 channels
@@ -123,23 +128,23 @@ namespace MediaBrowser.Controller.MediaEncoding
{ "truehd", 6 },
};
- private static readonly string _defaultMjpegEncoder = "mjpeg";
- private static readonly Dictionary<string, string> _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase)
+ private static readonly Dictionary<HardwareAccelerationType, string> _mjpegCodecMap = new()
{
- { "vaapi", _defaultMjpegEncoder + "_vaapi" },
- { "qsv", _defaultMjpegEncoder + "_qsv" },
- { "videotoolbox", _defaultMjpegEncoder + "_videotoolbox" }
+ { HardwareAccelerationType.vaapi, _defaultMjpegEncoder + "_vaapi" },
+ { HardwareAccelerationType.qsv, _defaultMjpegEncoder + "_qsv" },
+ { HardwareAccelerationType.videotoolbox, _defaultMjpegEncoder + "_videotoolbox" },
+ { HardwareAccelerationType.rkmpp, _defaultMjpegEncoder + "_rkmpp" }
};
- public static readonly string[] LosslessAudioCodecs = new string[]
- {
+ public static readonly string[] LosslessAudioCodecs =
+ [
"alac",
"ape",
"flac",
"mlp",
"truehd",
"wavpack"
- };
+ ];
public EncodingHelper(
IApplicationPaths appPaths,
@@ -176,18 +181,18 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var hwType = encodingOptions.HardwareAccelerationType;
- var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ var codecMap = new Dictionary<HardwareAccelerationType, string>()
{
- { "amf", hwEncoder + "_amf" },
- { "nvenc", hwEncoder + "_nvenc" },
- { "qsv", hwEncoder + "_qsv" },
- { "vaapi", hwEncoder + "_vaapi" },
- { "videotoolbox", hwEncoder + "_videotoolbox" },
- { "v4l2m2m", hwEncoder + "_v4l2m2m" },
- { "rkmpp", hwEncoder + "_rkmpp" },
+ { HardwareAccelerationType.amf, hwEncoder + "_amf" },
+ { HardwareAccelerationType.nvenc, hwEncoder + "_nvenc" },
+ { HardwareAccelerationType.qsv, hwEncoder + "_qsv" },
+ { HardwareAccelerationType.vaapi, hwEncoder + "_vaapi" },
+ { HardwareAccelerationType.videotoolbox, hwEncoder + "_videotoolbox" },
+ { HardwareAccelerationType.v4l2m2m, hwEncoder + "_v4l2m2m" },
+ { HardwareAccelerationType.rkmpp, hwEncoder + "_rkmpp" },
};
- if (!string.IsNullOrEmpty(hwType)
+ if (hwType != HardwareAccelerationType.none
&& encodingOptions.EnableHardwareEncoding
&& codecMap.TryGetValue(hwType, out var preferredEncoder)
&& _mediaEncoder.SupportsEncoder(preferredEncoder))
@@ -205,7 +210,15 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var hwType = encodingOptions.HardwareAccelerationType;
- if (!string.IsNullOrEmpty(hwType)
+ // Only Intel has VA-API MJPEG encoder
+ if (hwType == HardwareAccelerationType.vaapi
+ && !(_mediaEncoder.IsVaapiDeviceInteliHD
+ || _mediaEncoder.IsVaapiDeviceInteli965))
+ {
+ return _defaultMjpegEncoder;
+ }
+
+ if (hwType != HardwareAccelerationType.none
&& encodingOptions.EnableHardwareEncoding
&& _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder)
&& _mediaEncoder.SupportsEncoder(preferredEncoder))
@@ -297,7 +310,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (state.VideoStream is null
|| !options.EnableTonemapping
- || GetVideoColorBitDepth(state) != 10
+ || GetVideoColorBitDepth(state) < 10
|| !_mediaEncoder.SupportsFilter("tonemapx"))
{
return false;
@@ -310,13 +323,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (state.VideoStream is null
|| !options.EnableTonemapping
- || GetVideoColorBitDepth(state) != 10)
+ || GetVideoColorBitDepth(state) < 10)
{
return false;
}
- if (string.Equals(state.VideoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
- && state.VideoStream.VideoRange == VideoRange.HDR
+ if (state.VideoStream.VideoRange == VideoRange.HDR
&& state.VideoStream.VideoRangeType == VideoRangeType.DOVI)
{
// Only native SW decoder and HW accelerator can parse dovi rpu.
@@ -353,7 +365,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (state.VideoStream is null
|| !options.EnableVppTonemapping
- || GetVideoColorBitDepth(state) != 10)
+ || GetVideoColorBitDepth(state) < 10)
{
return false;
}
@@ -361,7 +373,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// prefer 'tonemap_vaapi' over 'vpp_qsv' on Linux for supporting Gen9/KBLx.
// 'vpp_qsv' requires VPL, which is only supported on Gen12/TGLx and newer.
if (OperatingSystem.IsWindows()
- && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)
+ && options.HardwareAccelerationType == HardwareAccelerationType.qsv
&& _mediaEncoder.EncoderVersion < _minFFmpegQsvVppTonemapOption)
{
return false;
@@ -376,7 +388,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (state.VideoStream is null
|| !options.EnableVideoToolboxTonemapping
- || GetVideoColorBitDepth(state) != 10)
+ || GetVideoColorBitDepth(state) < 10)
{
return false;
}
@@ -387,6 +399,25 @@ namespace MediaBrowser.Controller.MediaEncoding
&& state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.HDR10Plus or VideoRangeType.DOVIWithHDR10 or VideoRangeType.DOVIWithHLG;
}
+ private bool IsVideoStreamHevcRext(EncodingJobInfo state)
+ {
+ var videoStream = state.VideoStream;
+ if (videoStream is null)
+ {
+ return false;
+ }
+
+ return string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
+ && (string.Equals(videoStream.Profile, "Rext", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p10le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv422p12le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase));
+ }
+
/// <summary>
/// Gets the name of the output video codec.
/// </summary>
@@ -851,13 +882,15 @@ namespace MediaBrowser.Controller.MediaEncoding
options);
}
- private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string srcDeviceAlias, string alias)
+ private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string vendorId, string srcDeviceAlias, string alias)
{
alias ??= VaapiAlias;
+ var haveVendorId = !string.IsNullOrEmpty(vendorId)
+ && _mediaEncoder.EncoderVersion >= _minFFmpegVaapiDeviceVendorId;
- // 'renderNodePath' has higher priority than 'kernelDriver'
+ // Priority: 'renderNodePath' > 'vendorId' > 'kernelDriver'
var driverOpts = string.IsNullOrEmpty(renderNodePath)
- ? (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver)
+ ? (haveVendorId ? $",vendor_id={vendorId}" : (string.IsNullOrEmpty(kernelDriver) ? string.Empty : $",kernel_driver={kernelDriver}"))
: renderNodePath;
// 'driver' behaves similarly to env LIBVA_DRIVER_NAME
@@ -892,7 +925,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (OperatingSystem.IsLinux())
{
// derive qsv from vaapi device
- return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias;
+ return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", "0x8086", null, VaapiAlias) + arg + "@" + VaapiAlias;
}
if (OperatingSystem.IsWindows())
@@ -921,7 +954,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// DVBSUB uses the fixed canvas size 720x576
if (state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& !state.SubtitleStream.IsTextSubtitleStream
&& !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
{
@@ -971,7 +1004,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
var isHwTonemapAvailable = IsHwTonemapAvailable(state, options);
- if (string.Equals(optHwaccelType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ if (optHwaccelType == HardwareAccelerationType.vaapi)
{
if (!isLinux || !_mediaEncoder.SupportsHwaccel("vaapi"))
{
@@ -987,14 +1020,14 @@ namespace MediaBrowser.Controller.MediaEncoding
if (_mediaEncoder.IsVaapiDeviceInteliHD)
{
- args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, null, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, null, null, VaapiAlias));
}
else if (_mediaEncoder.IsVaapiDeviceInteli965)
{
// Only override i965 since it has lower priority than iHD in libva lookup.
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME", "i965");
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME_JELLYFIN", "i965");
- args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, null, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, null, null, VaapiAlias));
}
var filterDevArgs = string.Empty;
@@ -1018,7 +1051,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier)
{
args.Append(GetDrmDeviceArgs(options.VaapiDevice, DrmAlias));
- args.Append(GetVaapiDeviceArgs(null, null, null, DrmAlias, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(null, null, null, null, DrmAlias, VaapiAlias));
args.Append(GetVulkanDeviceArgs(0, null, DrmAlias, VulkanAlias));
// libplacebo wants an explicitly set vulkan filter device.
@@ -1026,7 +1059,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else
{
- args.Append(GetVaapiDeviceArgs(options.VaapiDevice, null, null, null, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(options.VaapiDevice, null, null, null, null, VaapiAlias));
filterDevArgs = GetFilterHwDeviceArgs(VaapiAlias);
if (doOclTonemap)
@@ -1045,7 +1078,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(filterDevArgs);
}
- else if (string.Equals(optHwaccelType, "qsv", StringComparison.OrdinalIgnoreCase))
+ else if (optHwaccelType == HardwareAccelerationType.qsv)
{
if ((!isLinux && !isWindows) || !_mediaEncoder.SupportsHwaccel("qsv"))
{
@@ -1080,7 +1113,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(filterDevArgs);
}
- else if (string.Equals(optHwaccelType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ else if (optHwaccelType == HardwareAccelerationType.nvenc)
{
if ((!isLinux && !isWindows) || !IsCudaFullSupported())
{
@@ -1099,7 +1132,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(GetCudaDeviceArgs(0, CudaAlias))
.Append(GetFilterHwDeviceArgs(CudaAlias));
}
- else if (string.Equals(optHwaccelType, "amf", StringComparison.OrdinalIgnoreCase))
+ else if (optHwaccelType == HardwareAccelerationType.amf)
{
if (!isWindows || !_mediaEncoder.SupportsHwaccel("d3d11va"))
{
@@ -1124,7 +1157,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(filterDevArgs);
}
- else if (string.Equals(optHwaccelType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ else if (optHwaccelType == HardwareAccelerationType.videotoolbox)
{
if (!isMacOS || !_mediaEncoder.SupportsHwaccel("videotoolbox"))
{
@@ -1141,7 +1174,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// videotoolbox hw filter does not require device selection
args.Append(GetVideoToolboxDeviceArgs(VideotoolboxAlias));
}
- else if (string.Equals(optHwaccelType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ else if (optHwaccelType == HardwareAccelerationType.rkmpp)
{
if (!isLinux || !_mediaEncoder.SupportsHwaccel("rkmpp"))
{
@@ -1220,7 +1253,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// sub2video for external graphical subtitles
if (state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& !state.SubtitleStream.IsTextSubtitleStream
&& state.SubtitleStream.IsExternal)
{
@@ -1414,6 +1447,148 @@ namespace MediaBrowser.Controller.MediaEncoding
return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
}
+ private string GetEncoderParam(EncoderPreset? preset, EncoderPreset defaultPreset, EncodingOptions encodingOptions, string videoEncoder, bool isLibX265)
+ {
+ var param = string.Empty;
+ var encoderPreset = preset ?? defaultPreset;
+ if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265)
+ {
+ var presetString = encoderPreset switch
+ {
+ EncoderPreset.auto => EncoderPreset.veryfast.ToString().ToLowerInvariant(),
+ _ => encoderPreset.ToString().ToLowerInvariant()
+ };
+
+ param += " -preset " + presetString;
+
+ int encodeCrf = encodingOptions.H264Crf;
+ if (isLibX265)
+ {
+ encodeCrf = encodingOptions.H265Crf;
+ }
+
+ if (encodeCrf >= 0 && encodeCrf <= 51)
+ {
+ param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ string defaultCrf = "23";
+ if (isLibX265)
+ {
+ defaultCrf = "28";
+ }
+
+ param += " -crf " + defaultCrf;
+ }
+ }
+ else if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase))
+ {
+ // Default to use the recommended preset 10.
+ // Omit presets < 5, which are too slow for on the fly encoding.
+ // https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md
+ param += encoderPreset switch
+ {
+ EncoderPreset.veryslow => " -preset 5",
+ EncoderPreset.slower => " -preset 6",
+ EncoderPreset.slow => " -preset 7",
+ EncoderPreset.medium => " -preset 8",
+ EncoderPreset.fast => " -preset 9",
+ EncoderPreset.faster => " -preset 10",
+ EncoderPreset.veryfast => " -preset 11",
+ EncoderPreset.superfast => " -preset 12",
+ EncoderPreset.ultrafast => " -preset 13",
+ _ => " -preset 10"
+ };
+ }
+ else if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "av1_vaapi", StringComparison.OrdinalIgnoreCase))
+ {
+ // -compression_level is not reliable on AMD.
+ if (_mediaEncoder.IsVaapiDeviceInteliHD)
+ {
+ param += encoderPreset switch
+ {
+ EncoderPreset.veryslow => " -compression_level 1",
+ EncoderPreset.slower => " -compression_level 2",
+ EncoderPreset.slow => " -compression_level 3",
+ EncoderPreset.medium => " -compression_level 4",
+ EncoderPreset.fast => " -compression_level 5",
+ EncoderPreset.faster => " -compression_level 6",
+ EncoderPreset.veryfast => " -compression_level 7",
+ EncoderPreset.superfast => " -compression_level 7",
+ EncoderPreset.ultrafast => " -compression_level 7",
+ _ => string.Empty
+ };
+ }
+ }
+ else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) // h264 (h264_qsv)
+ || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) // hevc (hevc_qsv)
+ || string.Equals(videoEncoder, "av1_qsv", StringComparison.OrdinalIgnoreCase)) // av1 (av1_qsv)
+ {
+ EncoderPreset[] valid_presets = [EncoderPreset.veryslow, EncoderPreset.slower, EncoderPreset.slow, EncoderPreset.medium, EncoderPreset.fast, EncoderPreset.faster, EncoderPreset.veryfast];
+
+ param += " -preset " + (valid_presets.Contains(encoderPreset) ? encoderPreset : EncoderPreset.veryfast).ToString().ToLowerInvariant();
+ }
+ else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
+ || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc)
+ || string.Equals(videoEncoder, "av1_nvenc", StringComparison.OrdinalIgnoreCase) // av1 (av1_nvenc)
+ )
+ {
+ param += encoderPreset switch
+ {
+ EncoderPreset.veryslow => " -preset p7",
+ EncoderPreset.slower => " -preset p6",
+ EncoderPreset.slow => " -preset p5",
+ EncoderPreset.medium => " -preset p4",
+ EncoderPreset.fast => " -preset p3",
+ EncoderPreset.faster => " -preset p2",
+ _ => " -preset p1"
+ };
+ }
+ else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // h264 (h264_amf)
+ || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) // hevc (hevc_amf)
+ || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase) // av1 (av1_amf)
+ )
+ {
+ param += encoderPreset switch
+ {
+ EncoderPreset.veryslow => " -quality quality",
+ EncoderPreset.slower => " -quality quality",
+ EncoderPreset.slow => " -quality quality",
+ EncoderPreset.medium => " -quality balanced",
+ _ => " -quality speed"
+ };
+
+ if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase))
+ {
+ param += " -header_insertion_mode gop";
+ }
+
+ if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase))
+ {
+ param += " -gops_per_idr 1";
+ }
+ }
+ else if (string.Equals(videoEncoder, "h264_videotoolbox", StringComparison.OrdinalIgnoreCase) // h264 (h264_videotoolbox)
+ || string.Equals(videoEncoder, "hevc_videotoolbox", StringComparison.OrdinalIgnoreCase) // hevc (hevc_videotoolbox)
+ )
+ {
+ param += encoderPreset switch
+ {
+ EncoderPreset.veryslow => " -prio_speed 0",
+ EncoderPreset.slower => " -prio_speed 0",
+ EncoderPreset.slow => " -prio_speed 0",
+ EncoderPreset.medium => " -prio_speed 0",
+ _ => " -prio_speed 1"
+ };
+ }
+
+ return param;
+ }
+
public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level)
{
if (double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel))
@@ -1508,13 +1683,15 @@ namespace MediaBrowser.Controller.MediaEncoding
setPtsParam);
}
- var mediaPath = state.MediaPath ?? string.Empty;
+ var subtitlePath = _subtitleEncoder.GetSubtitleFilePath(
+ state.SubtitleStream,
+ state.MediaSource,
+ CancellationToken.None).GetAwaiter().GetResult();
return string.Format(
CultureInfo.InvariantCulture,
- "subtitles=f='{0}':si={1}{2}{3}{4}{5}",
- _mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
- state.InternalSubtitleStreamOffset.ToString(CultureInfo.InvariantCulture),
+ "subtitles=f='{0}'{1}{2}{3}{4}",
+ _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
alphaParam,
sub2videoParam,
fontParam,
@@ -1534,7 +1711,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (maxrate.HasValue && state.VideoStream is not null)
{
- var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
+ var contentRate = state.VideoStream.ReferenceFrameRate;
if (contentRate.HasValue && contentRate.Value > maxrate.Value)
{
@@ -1626,7 +1803,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="encodingOptions">Encoding options.</param>
/// <param name="defaultPreset">Default present to use for encoding.</param>
/// <returns>Video bitrate.</returns>
- public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset)
+ public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, EncoderPreset defaultPreset)
{
var param = string.Empty;
@@ -1641,7 +1818,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// https://github.com/intel/media-driver/issues/1456
var enableWaFori915Hang = false;
- if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ var hardwareAccelerationType = encodingOptions.HardwareAccelerationType;
+
+ if (hardwareAccelerationType == HardwareAccelerationType.vaapi)
{
var isIntelVaapiDriver = _mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965;
@@ -1654,7 +1833,7 @@ namespace MediaBrowser.Controller.MediaEncoding
intelLowPowerHwEncoding = encodingOptions.EnableIntelLowPowerHevcHwEncoder && isIntelVaapiDriver;
}
}
- else if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ else if (hardwareAccelerationType == HardwareAccelerationType.qsv)
{
if (OperatingSystem.IsLinux())
{
@@ -1701,204 +1880,10 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -async_depth 1";
}
- var isVc1 = string.Equals(state.VideoStream?.Codec, "vc1", StringComparison.OrdinalIgnoreCase);
var isLibX265 = string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase);
+ var encodingPreset = encodingOptions.EncoderPreset;
- if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265)
- {
- if (!string.IsNullOrEmpty(encodingOptions.EncoderPreset))
- {
- param += " -preset " + encodingOptions.EncoderPreset;
- }
- else
- {
- param += " -preset " + defaultPreset;
- }
-
- int encodeCrf = encodingOptions.H264Crf;
- if (isLibX265)
- {
- encodeCrf = encodingOptions.H265Crf;
- }
-
- if (encodeCrf >= 0 && encodeCrf <= 51)
- {
- param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- string defaultCrf = "23";
- if (isLibX265)
- {
- defaultCrf = "28";
- }
-
- param += " -crf " + defaultCrf;
- }
- }
- else if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase))
- {
- // Default to use the recommended preset 10.
- // Omit presets < 5, which are too slow for on the fly encoding.
- // https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md
- param += encodingOptions.EncoderPreset switch
- {
- "veryslow" => " -preset 5",
- "slower" => " -preset 6",
- "slow" => " -preset 7",
- "medium" => " -preset 8",
- "fast" => " -preset 9",
- "faster" => " -preset 10",
- "veryfast" => " -preset 11",
- "superfast" => " -preset 12",
- "ultrafast" => " -preset 13",
- _ => " -preset 10"
- };
- }
- else if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoEncoder, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoEncoder, "av1_vaapi", StringComparison.OrdinalIgnoreCase))
- {
- // -compression_level is not reliable on AMD.
- if (_mediaEncoder.IsVaapiDeviceInteliHD)
- {
- param += encodingOptions.EncoderPreset switch
- {
- "veryslow" => " -compression_level 1",
- "slower" => " -compression_level 2",
- "slow" => " -compression_level 3",
- "medium" => " -compression_level 4",
- "fast" => " -compression_level 5",
- "faster" => " -compression_level 6",
- "veryfast" => " -compression_level 7",
- "superfast" => " -compression_level 7",
- "ultrafast" => " -compression_level 7",
- _ => string.Empty
- };
- }
- }
- else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) // h264 (h264_qsv)
- || string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase) // hevc (hevc_qsv)
- || string.Equals(videoEncoder, "av1_qsv", StringComparison.OrdinalIgnoreCase)) // av1 (av1_qsv)
- {
- string[] valid_presets = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" };
-
- if (valid_presets.Contains(encodingOptions.EncoderPreset, StringComparison.OrdinalIgnoreCase))
- {
- param += " -preset " + encodingOptions.EncoderPreset;
- }
- else
- {
- param += " -preset veryfast";
- }
- }
- else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
- || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc)
- || string.Equals(videoEncoder, "av1_nvenc", StringComparison.OrdinalIgnoreCase)) // av1 (av1_nvenc)
- {
- switch (encodingOptions.EncoderPreset)
- {
- case "veryslow":
- param += " -preset p7";
- break;
-
- case "slower":
- param += " -preset p6";
- break;
-
- case "slow":
- param += " -preset p5";
- break;
-
- case "medium":
- param += " -preset p4";
- break;
-
- case "fast":
- param += " -preset p3";
- break;
-
- case "faster":
- param += " -preset p2";
- break;
-
- case "veryfast":
- case "superfast":
- case "ultrafast":
- param += " -preset p1";
- break;
-
- default:
- param += " -preset p1";
- break;
- }
- }
- else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) // h264 (h264_amf)
- || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase) // hevc (hevc_amf)
- || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase)) // av1 (av1_amf)
- {
- switch (encodingOptions.EncoderPreset)
- {
- case "veryslow":
- case "slower":
- case "slow":
- param += " -quality quality";
- break;
-
- case "medium":
- param += " -quality balanced";
- break;
-
- case "fast":
- case "faster":
- case "veryfast":
- case "superfast":
- case "ultrafast":
- param += " -quality speed";
- break;
-
- default:
- param += " -quality speed";
- break;
- }
-
- if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoEncoder, "av1_amf", StringComparison.OrdinalIgnoreCase))
- {
- param += " -header_insertion_mode gop";
- }
-
- if (string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase))
- {
- param += " -gops_per_idr 1";
- }
- }
- else if (string.Equals(videoEncoder, "h264_videotoolbox", StringComparison.OrdinalIgnoreCase) // h264 (h264_videotoolbox)
- || string.Equals(videoEncoder, "hevc_videotoolbox", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_videotoolbox)
- {
- switch (encodingOptions.EncoderPreset)
- {
- case "veryslow":
- case "slower":
- case "slow":
- case "medium":
- param += " -prio_speed 0";
- break;
-
- case "fast":
- case "faster":
- case "veryfast":
- case "superfast":
- case "ultrafast":
- param += " -prio_speed 1";
- break;
-
- default:
- param += " -prio_speed 1";
- break;
- }
- }
-
+ param += GetEncoderParam(encodingPreset, defaultPreset, encodingOptions, videoEncoder, isLibX265);
param += GetVideoBitrateParam(state, videoEncoder);
var framerate = GetFramerateParam(state);
@@ -1915,7 +1900,26 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault() ?? string.Empty;
- profile = WhiteSpaceRegex().Replace(profile, string.Empty);
+ profile = WhiteSpaceRegex().Replace(profile, string.Empty).ToLowerInvariant();
+
+ var videoProfiles = Array.Empty<string>();
+ if (string.Equals("h264", targetVideoCodec, StringComparison.OrdinalIgnoreCase))
+ {
+ videoProfiles = _videoProfilesH264;
+ }
+ else if (string.Equals("hevc", targetVideoCodec, StringComparison.OrdinalIgnoreCase))
+ {
+ videoProfiles = _videoProfilesH265;
+ }
+ else if (string.Equals("av1", targetVideoCodec, StringComparison.OrdinalIgnoreCase))
+ {
+ videoProfiles = _videoProfilesAv1;
+ }
+
+ if (!videoProfiles.Contains(profile, StringComparison.OrdinalIgnoreCase))
+ {
+ profile = string.Empty;
+ }
// We only transcode to HEVC 8-bit for now, force Main Profile.
if (profile.Contains("main10", StringComparison.OrdinalIgnoreCase)
@@ -2218,7 +2222,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var requestedFramerate = request.MaxFramerate ?? request.Framerate;
if (requestedFramerate.HasValue)
{
- var videoFrameRate = videoStream.AverageFrameRate ?? videoStream.RealFrameRate;
+ var videoFrameRate = videoStream.ReferenceFrameRate;
if (!videoFrameRate.HasValue || videoFrameRate.Value > requestedFramerate.Value)
{
@@ -2415,7 +2419,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return 1;
}
- private static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
+ public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
{
var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
@@ -2439,6 +2443,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
scaleFactor = Math.Max(scaleFactor, 2);
}
+ else if (bitrate >= 30000000)
+ {
+ // Don't scale beyond 30Mbps, it is hardly visually noticeable for most codecs with our prefer speed encoding
+ // and will cause extremely high bitrate to be used for av1->h264 transcoding that will overload clients and encoders
+ scaleFactor = 1;
+ }
return Convert.ToInt32(scaleFactor * bitrate);
}
@@ -2569,7 +2579,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive;
- if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !isCopyingTimestamps)
+ if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state) && !isCopyingTimestamps)
{
var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds;
@@ -2770,7 +2780,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.AudioStream.IsExternal)
{
bool hasExternalGraphicsSubs = state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& state.SubtitleStream.IsExternal
&& !state.SubtitleStream.IsTextSubtitleStream;
int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
@@ -3014,6 +3024,8 @@ namespace MediaBrowser.Controller.MediaEncoding
public static string GetGraphicalSubPreProcessFilters(
int? videoWidth,
int? videoHeight,
+ int? subtitleWidth,
+ int? subtitleHeight,
int? requestedWidth,
int? requestedHeight,
int? requestedMaxWidth,
@@ -3027,16 +3039,37 @@ namespace MediaBrowser.Controller.MediaEncoding
requestedMaxWidth,
requestedMaxHeight);
- if (outWidth.HasValue && outHeight.HasValue)
+ if (!outWidth.HasValue
+ || !outHeight.HasValue
+ || outWidth.Value <= 0
+ || outHeight.Value <= 0)
{
- return string.Format(
- CultureInfo.InvariantCulture,
- @"scale,scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}",
- outWidth.Value,
- outHeight.Value);
+ return string.Empty;
}
- return string.Empty;
+ // Automatically add padding based on subtitle input
+ var filters = @"scale,scale=-1:{1}:fast_bilinear,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}";
+
+ if (subtitleWidth.HasValue
+ && subtitleHeight.HasValue
+ && subtitleWidth.Value > 0
+ && subtitleHeight.Value > 0)
+ {
+ var videoDar = (double)outWidth.Value / outHeight.Value;
+ var subtitleDar = (double)subtitleWidth.Value / subtitleHeight.Value;
+
+ // No need to add padding when DAR is the same -> 1080p PGSSUB on 2160p video
+ if (Math.Abs(videoDar - subtitleDar) < 0.01f)
+ {
+ filters = @"scale,scale={0}:{1}:fast_bilinear";
+ }
+ }
+
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ filters,
+ outWidth.Value,
+ outHeight.Value);
}
public static string GetAlphaSrcFilter(
@@ -3234,21 +3267,20 @@ namespace MediaBrowser.Controller.MediaEncoding
public static string GetSwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options)
{
- var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.AverageFrameRate <= 30;
+ var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.ReferenceFrameRate <= 30;
return string.Format(
CultureInfo.InvariantCulture,
"{0}={1}:-1:0",
- string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase) ? "bwdif" : "yadif",
+ options.DeinterlaceMethod.ToString().ToLowerInvariant(),
doubleRateDeint ? "1" : "0");
}
public string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix)
{
- var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30;
+ var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30;
if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase))
{
- var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase)
- && _mediaEncoder.SupportsFilter("bwdif_cuda");
+ var useBwdif = options.DeinterlaceMethod == DeinterlaceMethod.bwdif && _mediaEncoder.SupportsFilter("bwdif_cuda");
return string.Format(
CultureInfo.InvariantCulture,
@@ -3272,16 +3304,19 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase))
{
+ var useBwdif = options.DeinterlaceMethod == DeinterlaceMethod.bwdif && _mediaEncoder.SupportsFilter("bwdif_videotoolbox");
+
return string.Format(
CultureInfo.InvariantCulture,
- "yadif_videotoolbox={0}:-1:0",
+ "{0}_videotoolbox={1}:-1:0",
+ useBwdif ? "bwdif" : "yadif",
doubleRateDeint ? "1" : "0");
}
return string.Empty;
}
- public string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat)
+ private string GetHwTonemapFilter(EncodingOptions options, string hwTonemapSuffix, string videoFormat, bool forceFullRange)
{
if (string.IsNullOrEmpty(hwTonemapSuffix))
{
@@ -3289,7 +3324,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var args = string.Empty;
- var algorithm = options.TonemappingAlgorithm;
+ var algorithm = options.TonemappingAlgorithm.ToString().ToLowerInvariant();
+ var mode = options.TonemappingMode.ToString().ToLowerInvariant();
+ var range = forceFullRange ? TonemappingRange.pc : options.TonemappingRange;
+ var rangeString = range.ToString().ToLowerInvariant();
if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase))
{
@@ -3324,10 +3362,10 @@ namespace MediaBrowser.Controller.MediaEncoding
args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}";
var useLegacyTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode
- && _legacyTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+ && _legacyTonemapModes.Contains(options.TonemappingMode);
var useAdvancedTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegAdvancedTonemapMode
- && _advancedTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+ && _advancedTonemapModes.Contains(options.TonemappingMode);
if (useLegacyTonemapModes || useAdvancedTonemapModes)
{
@@ -3339,8 +3377,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args += ":param={6}";
}
- if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase)
- || string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
+ if (range == TonemappingRange.tv || range == TonemappingRange.pc)
{
args += ":range={7}";
}
@@ -3354,12 +3391,12 @@ namespace MediaBrowser.Controller.MediaEncoding
algorithm,
options.TonemappingPeak,
options.TonemappingDesat,
- options.TonemappingMode,
+ mode,
options.TonemappingParam,
- options.TonemappingRange);
+ rangeString);
}
- public string GetLibplaceboFilter(
+ private string GetLibplaceboFilter(
EncodingOptions options,
string videoFormat,
bool doTonemap,
@@ -3368,7 +3405,8 @@ namespace MediaBrowser.Controller.MediaEncoding
int? requestedWidth,
int? requestedHeight,
int? requestedMaxWidth,
- int? requestedMaxHeight)
+ int? requestedMaxHeight,
+ bool forceFullRange)
{
var (outWidth, outHeight) = GetFixedOutputSize(
videoWidth,
@@ -3391,24 +3429,24 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doTonemap)
{
var algorithm = options.TonemappingAlgorithm;
+ var algorithmString = "clip";
var mode = options.TonemappingMode;
- var range = options.TonemappingRange;
+ var range = forceFullRange ? TonemappingRange.pc : options.TonemappingRange;
- if (string.Equals(algorithm, "bt2390", StringComparison.OrdinalIgnoreCase))
+ if (algorithm == TonemappingAlgorithm.bt2390)
{
- algorithm = "bt.2390";
+ algorithmString = "bt.2390";
}
- else if (string.Equals(algorithm, "none", StringComparison.OrdinalIgnoreCase))
+ else if (algorithm != TonemappingAlgorithm.none)
{
- algorithm = "clip";
+ algorithmString = algorithm.ToString().ToLowerInvariant();
}
- tonemapArg = ":tonemapping=" + algorithm + ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
+ tonemapArg = $":tonemapping={algorithmString}:peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
- if (string.Equals(range, "tv", StringComparison.OrdinalIgnoreCase)
- || string.Equals(range, "pc", StringComparison.OrdinalIgnoreCase))
+ if (range == TonemappingRange.tv || range == TonemappingRange.pc)
{
- tonemapArg += ":range=" + range;
+ tonemapArg += ":range=" + range.ToString().ToLowerInvariant();
}
}
@@ -3463,7 +3501,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doToneMap = IsSwTonemapAvailable(state, options);
var requireDoviReshaping = doToneMap && state.VideoStream.VideoRangeType == VideoRangeType.DOVI;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -3512,8 +3550,8 @@ namespace MediaBrowser.Controller.MediaEncoding
tonemapArgs += $":param={options.TonemappingParam}";
}
- if (string.Equals(options.TonemappingRange, "tv", StringComparison.OrdinalIgnoreCase)
- || string.Equals(options.TonemappingRange, "pc", StringComparison.OrdinalIgnoreCase))
+ var range = options.TonemappingRange;
+ if (range == TonemappingRange.tv || range == TonemappingRange.pc)
{
tonemapArgs += $":range={options.TonemappingRange}";
}
@@ -3537,7 +3575,9 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3557,7 +3597,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.nvenc)
{
return (null, null, null);
}
@@ -3596,20 +3636,23 @@ namespace MediaBrowser.Controller.MediaEncoding
var isNvencEncoder = vidEncoder.Contains("nvenc", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isNvencEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isCuInCuOut = isNvDecoder && isNvencEncoder;
- var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30;
+ var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30;
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doCuTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -3662,7 +3705,8 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add($"transpose_cuda=dir={tranposeDir}");
}
- var outFormat = doCuTonemap ? string.Empty : "yuv420p";
+ var isRext = IsVideoStreamHevcRext(state);
+ var outFormat = doCuTonemap ? (isRext ? "p010" : string.Empty) : "yuv420p";
var hwScaleFilter = GetHwScaleFilter("scale", "cuda", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// hw scale
mainFilters.Add(hwScaleFilter);
@@ -3671,7 +3715,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// hw tonemap
if (doCuTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "cuda", "yuv420p");
+ var tonemapFilter = GetHwTonemapFilter(options, "cuda", "yuv420p", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -3713,7 +3757,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3738,7 +3782,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3759,7 +3803,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.amf)
{
return (null, null, null);
}
@@ -3800,6 +3844,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isAmfEncoder = vidEncoder.Contains("amf", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isAmfEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isDxInDxOut = isD3d11vaDecoder && isAmfEncoder;
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
@@ -3807,12 +3852,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -3877,7 +3924,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// hw tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -3927,7 +3974,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3954,7 +4001,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3975,7 +4022,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.qsv)
{
return (null, null, null);
}
@@ -4036,6 +4083,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isHwDecoder = isD3d11vaDecoder || isQsvDecoder;
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isQsvEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isQsvInQsvOut = isHwDecoder && isQsvEncoder;
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
@@ -4045,12 +4093,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var doOclTonemap = !doVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVppTonemap || doOclTonemap;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -4076,6 +4126,12 @@ namespace MediaBrowser.Controller.MediaEncoding
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (isMjpegEncoder && !doOclTonemap)
+ {
+ // sw decoder + hw mjpeg encoder
+ swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc";
+ }
+
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add($"format={outFormat}");
@@ -4090,6 +4146,12 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isD3d11vaDecoder || isQsvDecoder)
{
+ var isRext = IsVideoStreamHevcRext(state);
+ var twoPassVppTonemap = isRext;
+ var doVppFullRangeOut = isMjpegEncoder
+ && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption;
+ var doVppScaleModeHq = isMjpegEncoder
+ && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppScaleModeOption;
var doVppProcamp = false;
var procampParams = string.Empty;
if (doVppTonemap)
@@ -4099,34 +4161,39 @@ namespace MediaBrowser.Controller.MediaEncoding
&& options.VppTonemappingBrightness <= 100)
{
procampParams += $":brightness={options.VppTonemappingBrightness}";
- doVppProcamp = true;
+ twoPassVppTonemap = doVppProcamp = true;
}
if (options.VppTonemappingContrast > 1
&& options.VppTonemappingContrast <= 10)
{
procampParams += $":contrast={options.VppTonemappingContrast}";
- doVppProcamp = true;
+ twoPassVppTonemap = doVppProcamp = true;
}
procampParams += doVppProcamp ? ":procamp=1:async_depth=2" : string.Empty;
}
- var outFormat = doOclTonemap ? (doVppTranspose ? "p010" : string.Empty) : "nv12";
- outFormat = (doVppTonemap && doVppProcamp) ? "p010" : outFormat;
+ var outFormat = doOclTonemap ? ((doVppTranspose || isRext) ? "p010" : string.Empty) : "nv12";
+ outFormat = twoPassVppTonemap ? "p010" : outFormat;
var swapOutputWandH = doVppTranspose && swapWAndH;
- var hwScalePrefix = (doVppTranspose || doVppTonemap) ? "vpp" : "scale";
- var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("vpp", "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTranspose)
{
hwScaleFilter += $":transpose={tranposeDir}";
}
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder)
+ {
+ hwScaleFilter += (doVppFullRangeOut && !doOclTonemap) ? ":out_range=pc" : string.Empty;
+ hwScaleFilter += doVppScaleModeHq ? ":scale_mode=hq" : string.Empty;
+ }
+
if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap)
{
- hwScaleFilter += doVppProcamp ? procampParams : ":tonemap=1";
+ hwScaleFilter += doVppProcamp ? procampParams : (twoPassVppTonemap ? string.Empty : ":tonemap=1");
}
if (isD3d11vaDecoder)
@@ -4150,7 +4217,7 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(hwScaleFilter);
// hw tonemap(w/ procamp)
- if (doVppTonemap && doVppProcamp)
+ if (doVppTonemap && twoPassVppTonemap)
{
mainFilters.Add("vpp_qsv=tonemap=1:format=nv12:async_depth=2");
}
@@ -4171,7 +4238,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// hw tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -4223,7 +4290,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4259,7 +4326,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4288,6 +4355,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isHwDecoder = isVaapiDecoder || isQsvDecoder;
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isQsvEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isQsvInQsvOut = isHwDecoder && isQsvEncoder;
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
@@ -4297,12 +4365,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -4328,6 +4398,12 @@ namespace MediaBrowser.Controller.MediaEncoding
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (isMjpegEncoder && !doOclTonemap)
+ {
+ // sw decoder + hw mjpeg encoder
+ swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc";
+ }
+
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add($"format={outFormat}");
@@ -4343,6 +4419,11 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (isVaapiDecoder || isQsvDecoder)
{
var hwFilterSuffix = isVaapiDecoder ? "vaapi" : "qsv";
+ var isRext = IsVideoStreamHevcRext(state);
+ var doVppFullRangeOut = isMjpegEncoder
+ && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppOutRangeOption;
+ var doVppScaleModeHq = isMjpegEncoder
+ && _mediaEncoder.EncoderVersion >= _minFFmpegQsvVppScaleModeOption;
// INPUT vaapi/qsv surface(vram)
// hw deint
@@ -4358,9 +4439,9 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
}
- var outFormat = doOclTonemap ? ((isQsvDecoder && doVppTranspose) ? "p010" : string.Empty) : "nv12";
+ var outFormat = doTonemap ? (((isQsvDecoder && doVppTranspose) || isRext) ? "p010" : string.Empty) : "nv12";
var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH;
- var hwScalePrefix = (isQsvDecoder && doVppTranspose) ? "vpp" : "scale";
+ var hwScalePrefix = isQsvDecoder ? "vpp" : "scale";
var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
if (!string.IsNullOrEmpty(hwScaleFilter) && isQsvDecoder && doVppTranspose)
@@ -4368,6 +4449,12 @@ namespace MediaBrowser.Controller.MediaEncoding
hwScaleFilter += $":transpose={tranposeDir}";
}
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder)
+ {
+ hwScaleFilter += ((isQsvDecoder && !doVppFullRangeOut) || doOclTonemap) ? string.Empty : ":out_range=pc";
+ hwScaleFilter += isQsvDecoder ? (doVppScaleModeHq ? ":scale_mode=hq" : string.Empty) : ":mode=hq";
+ }
+
// allocate extra pool sizes for vaapi vpp scale
if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder)
{
@@ -4388,7 +4475,7 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add("format=vaapi");
}
- var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
if (isQsvDecoder)
@@ -4408,7 +4495,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// ocl tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -4469,7 +4556,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4504,7 +4591,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4525,7 +4612,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.vaapi)
{
return (null, null, null);
}
@@ -4599,6 +4686,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isVaapiEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isVaInVaOut = isVaapiDecoder && isVaapiEncoder;
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
@@ -4608,12 +4696,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -4639,6 +4729,12 @@ namespace MediaBrowser.Controller.MediaEncoding
var outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (isMjpegEncoder && !doOclTonemap)
+ {
+ // sw decoder + hw mjpeg encoder
+ swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc";
+ }
+
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add($"format={outFormat}");
@@ -4653,6 +4749,8 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isVaapiDecoder)
{
+ var isRext = IsVideoStreamHevcRext(state);
+
// INPUT vaapi surface(vram)
// hw deint
if (doDeintH2645)
@@ -4667,9 +4765,15 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
}
- var outFormat = doTonemap ? string.Empty : "nv12";
+ var outFormat = doTonemap ? (isRext ? "p010" : string.Empty) : "nv12";
var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder)
+ {
+ hwScaleFilter += doOclTonemap ? string.Empty : ":out_range=pc";
+ hwScaleFilter += ":mode=hq";
+ }
+
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
{
@@ -4683,7 +4787,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// vaapi vpp tonemap
if (doVaVppTonemap && isVaapiDecoder)
{
- var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "vaapi", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -4696,7 +4800,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// ocl tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -4755,7 +4859,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4788,7 +4892,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -4820,13 +4924,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isVaapiEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doVkTonemap = IsVulkanHwTonemapAvailable(state, options);
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -4915,6 +5020,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// hw scale
var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", "nv12", false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder && !doVkTonemap)
+ {
+ hwScaleFilter += ":out_range=pc:mode=hq";
+ }
+
mainFilters.Add(hwScaleFilter);
}
}
@@ -4935,7 +5046,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// vk libplacebo
if (doVkTonemap || hasSubs)
{
- var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, isMjpegEncoder);
mainFilters.Add(libplaceboFilter);
mainFilters.Add("format=vulkan");
}
@@ -4980,7 +5091,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5048,6 +5161,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !isVaapiEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isVaInVaOut = isVaapiDecoder && isVaapiEncoder;
var isi965Driver = _mediaEncoder.IsVaapiDeviceInteli965;
var isAmdDriver = _mediaEncoder.IsVaapiDeviceAmd;
@@ -5057,7 +5171,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -5084,6 +5198,12 @@ namespace MediaBrowser.Controller.MediaEncoding
outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (isMjpegEncoder && !doOclTonemap)
+ {
+ // sw decoder + hw mjpeg encoder
+ swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc";
+ }
+
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add("format=" + outFormat);
@@ -5109,6 +5229,12 @@ namespace MediaBrowser.Controller.MediaEncoding
outFormat = doOclTonemap ? string.Empty : "nv12";
var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isMjpegEncoder)
+ {
+ hwScaleFilter += doOclTonemap ? string.Empty : ":out_range=pc";
+ hwScaleFilter += ":mode=hq";
+ }
+
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
{
@@ -5137,7 +5263,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// ocl tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -5203,7 +5329,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -5229,7 +5357,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox)
{
return (null, null, null);
}
@@ -5261,6 +5389,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var inW = state.VideoStream?.Width;
var inH = state.VideoStream?.Height;
@@ -5303,7 +5432,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var hwScaleFilter = GetHwScaleFilter("scale", "vt", scaleFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -5342,7 +5471,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// Metal tonemap
if (doMetalTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "videotoolbox", "nv12");
+ var tonemapFilter = GetHwTonemapFilter(options, "videotoolbox", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -5354,7 +5483,9 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5418,7 +5549,7 @@ namespace MediaBrowser.Controller.MediaEncoding
EncodingOptions options,
string vidEncoder)
{
- if (!string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ if (options.HardwareAccelerationType != HardwareAccelerationType.rkmpp)
{
return (null, null, null);
}
@@ -5463,19 +5594,25 @@ namespace MediaBrowser.Controller.MediaEncoding
var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
var isSwDecoder = !isRkmppDecoder;
var isSwEncoder = !isRkmppEncoder;
+ var isMjpegEncoder = vidEncoder.Contains("mjpeg", StringComparison.OrdinalIgnoreCase);
var isDrmInDrmOut = isRkmppDecoder && isRkmppEncoder;
+ var isEncoderSupportAfbc = isRkmppEncoder
+ && (vidEncoder.Contains("h264", StringComparison.OrdinalIgnoreCase)
+ || vidEncoder.Contains("hevc", StringComparison.OrdinalIgnoreCase));
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream != null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var subW = state.SubtitleStream?.Width;
+ var subH = state.SubtitleStream?.Height;
var rotation = state.VideoStream?.Rotation ?? 0;
var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
@@ -5501,6 +5638,12 @@ namespace MediaBrowser.Controller.MediaEncoding
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ if (isMjpegEncoder && !doOclTonemap)
+ {
+ // sw decoder + hw mjpeg encoder
+ swScaleFilter = string.IsNullOrEmpty(swScaleFilter) ? "scale=out_range=pc" : $"{swScaleFilter}:out_range=pc";
+ }
+
if (!string.IsNullOrEmpty(swScaleFilter))
{
swScaleFilter += ":flags=fast_bilinear";
@@ -5522,18 +5665,26 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// INPUT rkmpp/drm surface(gem/dma-heap)
- var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap;
+ var isFullAfbcPipeline = isEncoderSupportAfbc && isDrmInDrmOut && !doOclTonemap;
var swapOutputWandH = doRkVppTranspose && swapWAndH;
- var outFormat = doOclTonemap ? "p010" : "nv12";
- var hwScalePrefix = doRkVppTranspose ? "vpp" : "scale";
- var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
- var hwScaleFilter2 = GetHwScaleFilter(hwScalePrefix, "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var outFormat = doOclTonemap ? "p010" : (isMjpegEncoder ? "bgra" : "nv12"); // RGA only support full range in rgb fmts
+ var hwScaleFilter = GetHwScaleFilter("vpp", "rkrga", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var doScaling = GetHwScaleFilter("vpp", "rkrga", string.Empty, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
if (!hasSubs
|| doRkVppTranspose
|| !isFullAfbcPipeline
- || !string.IsNullOrEmpty(hwScaleFilter2))
+ || !string.IsNullOrEmpty(doScaling))
{
+ // RGA3 hardware only support (1/8 ~ 8) scaling in each blit operation,
+ // but in Trickplay there's a case: (3840/320 == 12), enable 2pass for it
+ if (!string.IsNullOrEmpty(doScaling)
+ && !IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 8.0f))
+ {
+ var hwScaleFilterFirstPass = $"scale_rkrga=w=iw/7.9:h=ih/7.9:format={outFormat}:afbc=1";
+ mainFilters.Add(hwScaleFilterFirstPass);
+ }
+
if (!string.IsNullOrEmpty(hwScaleFilter) && doRkVppTranspose)
{
hwScaleFilter += $":transpose={tranposeDir}";
@@ -5553,19 +5704,13 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doOclTonemap && isRkmppDecoder)
{
// map from rkmpp/drm to opencl via drm-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl:mode=read");
+ mainFilters.Add("hwmap=derive_device=opencl");
}
// ocl tonemap
if (doOclTonemap)
{
- var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
- // enable tradeoffs for performance
- if (!string.IsNullOrEmpty(tonemapFilter))
- {
- tonemapFilter += ":tradeoff=1";
- }
-
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12", isMjpegEncoder);
mainFilters.Add(tonemapFilter);
}
@@ -5602,7 +5747,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT drm(nv12) surface(gem/dma-heap)
// reverse-mapping via drm-opencl interop.
- mainFilters.Add("hwmap=derive_device=rkmpp:mode=write:reverse=1");
+ mainFilters.Add("hwmap=derive_device=rkmpp:reverse=1");
mainFilters.Add("format=drm_prime");
}
}
@@ -5616,7 +5761,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5636,14 +5781,20 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add("hwupload=derive_device=rkmpp");
// try enabling AFBC to save DDR bandwidth
- overlayFilters.Add("overlay_rkrga=eof_action=pass:repeatlast=0:format=nv12:afbc=1");
+ var hwOverlayFilter = "overlay_rkrga=eof_action=pass:repeatlast=0:format=nv12";
+ if (isEncoderSupportAfbc)
+ {
+ hwOverlayFilter += ":afbc=1";
+ }
+
+ overlayFilters.Add(hwOverlayFilter);
}
}
else if (memoryOutput)
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, subW, subH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -5670,7 +5821,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -5678,38 +5829,20 @@ namespace MediaBrowser.Controller.MediaEncoding
List<string> subFilters;
List<string> overlayFilters;
- if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
- {
- (mainFilters, subFilters, overlayFilters) = GetVaapiVidFilterChain(state, options, outputVideoCodec);
- }
- else if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
- {
- (mainFilters, subFilters, overlayFilters) = GetIntelVidFilterChain(state, options, outputVideoCodec);
- }
- else if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
- {
- (mainFilters, subFilters, overlayFilters) = GetNvidiaVidFilterChain(state, options, outputVideoCodec);
- }
- else if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
- {
- (mainFilters, subFilters, overlayFilters) = GetAmdVidFilterChain(state, options, outputVideoCodec);
- }
- else if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ (mainFilters, subFilters, overlayFilters) = options.HardwareAccelerationType switch
{
- (mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec);
- }
- else if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
- {
- (mainFilters, subFilters, overlayFilters) = GetRkmppVidFilterChain(state, options, outputVideoCodec);
- }
- else
- {
- (mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec);
- }
+ HardwareAccelerationType.vaapi => GetVaapiVidFilterChain(state, options, outputVideoCodec),
+ HardwareAccelerationType.amf => GetAmdVidFilterChain(state, options, outputVideoCodec),
+ HardwareAccelerationType.qsv => GetIntelVidFilterChain(state, options, outputVideoCodec),
+ HardwareAccelerationType.nvenc => GetNvidiaVidFilterChain(state, options, outputVideoCodec),
+ HardwareAccelerationType.videotoolbox => GetAppleVidFilterChain(state, options, outputVideoCodec),
+ HardwareAccelerationType.rkmpp => GetRkmppVidFilterChain(state, options, outputVideoCodec),
+ _ => GetSwVidFilterChain(state, options, outputVideoCodec),
+ };
- mainFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter));
- subFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter));
- overlayFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter));
+ mainFilters?.RemoveAll(string.IsNullOrEmpty);
+ subFilters?.RemoveAll(string.IsNullOrEmpty);
+ overlayFilters?.RemoveAll(string.IsNullOrEmpty);
var framerate = GetFramerateParam(state);
if (framerate.HasValue)
@@ -5889,7 +6022,9 @@ namespace MediaBrowser.Controller.MediaEncoding
return null;
}
- if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(options.HardwareAccelerationType))
+ var hardwareAccelerationType = options.HardwareAccelerationType;
+
+ if (!string.IsNullOrEmpty(videoStream.Codec) && hardwareAccelerationType != HardwareAccelerationType.none)
{
var bitDepth = GetVideoColorBitDepth(state);
@@ -5901,10 +6036,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase)))
{
// RKMPP has H.264 Hi10P decoder
- bool hasHardwareHi10P = string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase);
+ bool hasHardwareHi10P = hardwareAccelerationType == HardwareAccelerationType.rkmpp;
// VideoToolbox on Apple Silicon has H.264 Hi10P mode enabled after macOS 14.6
- if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ if (hardwareAccelerationType == HardwareAccelerationType.videotoolbox)
{
var ver = Environment.OSVersion.Version;
var arch = RuntimeInformation.OSArchitecture;
@@ -5921,50 +6056,23 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
- {
- return GetQsvHwVidDecoder(state, options, videoStream, bitDepth);
- }
-
- if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
- {
- return GetNvdecVidDecoder(state, options, videoStream, bitDepth);
- }
-
- if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
- {
- return GetAmfVidDecoder(state, options, videoStream, bitDepth);
- }
-
- if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
- {
- return GetVaapiVidDecoder(state, options, videoStream, bitDepth);
- }
-
- if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ var decoder = hardwareAccelerationType switch
{
- return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth);
- }
+ HardwareAccelerationType.vaapi => GetVaapiVidDecoder(state, options, videoStream, bitDepth),
+ HardwareAccelerationType.amf => GetAmfVidDecoder(state, options, videoStream, bitDepth),
+ HardwareAccelerationType.qsv => GetQsvHwVidDecoder(state, options, videoStream, bitDepth),
+ HardwareAccelerationType.nvenc => GetNvdecVidDecoder(state, options, videoStream, bitDepth),
+ HardwareAccelerationType.videotoolbox => GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth),
+ HardwareAccelerationType.rkmpp => GetRkmppVidDecoder(state, options, videoStream, bitDepth),
+ _ => string.Empty
+ };
- if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(decoder))
{
- return GetRkmppVidDecoder(state, options, videoStream, bitDepth);
+ return decoder;
}
}
- var whichCodec = videoStream.Codec;
- if (string.Equals(whichCodec, "avc", StringComparison.OrdinalIgnoreCase))
- {
- whichCodec = "h264";
- }
- else if (string.Equals(whichCodec, "h265", StringComparison.OrdinalIgnoreCase))
- {
- whichCodec = "hevc";
- }
-
- // Avoid a second attempt if no hardware acceleration is being used
- options.HardwareDecodingCodecs = Array.FindAll(options.HardwareDecodingCodecs, val => !string.Equals(val, whichCodec, StringComparison.OrdinalIgnoreCase));
-
// leave blank so ffmpeg will decide
return null;
}
@@ -5988,7 +6096,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var decoderName = decoderPrefix + '_' + decoderSuffix;
var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoderName) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
- if (bitDepth == 10 && isCodecAvailable)
+
+ // VideoToolbox decoders have built-in SW fallback
+ if (bitDepth == 10
+ && isCodecAvailable
+ && (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox))
{
if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
&& options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase)
@@ -6044,6 +6156,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox");
var isRkmppSupported = isLinux && IsRkmppFullSupported();
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
+ var hardwareAccelerationType = options.HardwareAccelerationType;
var ffmpegVersion = _mediaEncoder.EncoderVersion;
@@ -6063,17 +6176,40 @@ namespace MediaBrowser.Controller.MediaEncoding
&& ffmpegVersion >= _minFFmpegDisplayRotationOption;
var stripRotationDataArgs = stripRotationData ? " -display_rotation 0" : string.Empty;
- if (bitDepth == 10 && isCodecAvailable)
+ // VideoToolbox decoders have built-in SW fallback
+ if (isCodecAvailable
+ && (options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox))
{
if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
- && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase)
- && !options.EnableDecodingColorDepth10Hevc)
+ && options.HardwareDecodingCodecs.Contains("hevc", StringComparison.OrdinalIgnoreCase))
{
- return null;
+ if (IsVideoStreamHevcRext(state))
+ {
+ if (bitDepth <= 10 && !options.EnableDecodingColorDepth10HevcRext)
+ {
+ return null;
+ }
+
+ if (bitDepth == 12 && !options.EnableDecodingColorDepth12HevcRext)
+ {
+ return null;
+ }
+
+ if (hardwareAccelerationType == HardwareAccelerationType.vaapi
+ && !_mediaEncoder.IsVaapiDeviceInteliHD)
+ {
+ return null;
+ }
+ }
+ else if (bitDepth == 10 && !options.EnableDecodingColorDepth10Hevc)
+ {
+ return null;
+ }
}
if (string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase)
&& options.HardwareDecodingCodecs.Contains("vp9", StringComparison.OrdinalIgnoreCase)
+ && bitDepth == 10
&& !options.EnableDecodingColorDepth10Vp9)
{
return null;
@@ -6081,7 +6217,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Intel qsv/d3d11va/vaapi
- if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ if (hardwareAccelerationType == HardwareAccelerationType.qsv)
{
if (options.PreferSystemNativeHwDecoder)
{
@@ -6107,7 +6243,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Nvidia cuda
- if (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ if (hardwareAccelerationType == HardwareAccelerationType.nvenc)
{
if (isCudaSupported && isCodecAvailable)
{
@@ -6124,7 +6260,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Amd d3d11va
- if (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
+ if (hardwareAccelerationType == HardwareAccelerationType.amf)
{
if (isD3d11Supported && isCodecAvailable)
{
@@ -6134,7 +6270,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Vaapi
- if (string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)
+ if (hardwareAccelerationType == HardwareAccelerationType.vaapi
&& isVaapiSupported
&& isCodecAvailable)
{
@@ -6143,7 +6279,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Apple videotoolbox
- if (string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)
+ if (hardwareAccelerationType == HardwareAccelerationType.videotoolbox
&& isVideotoolboxSupported
&& isCodecAvailable)
{
@@ -6151,7 +6287,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Rockchip rkmpp
- if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)
+ if (hardwareAccelerationType == HardwareAccelerationType.rkmpp
&& isRkmppSupported
&& isCodecAvailable)
{
@@ -6167,7 +6303,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isLinux = OperatingSystem.IsLinux();
if ((!isWindows && !isLinux)
- || !string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.qsv)
{
return null;
}
@@ -6185,6 +6321,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var is8bitSwFormatsQsv = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
var is8_10bitSwFormatsQsv = is8bitSwFormatsQsv || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is8_10_12bitSwFormatsQsv = is8_10bitSwFormatsQsv
+ || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
// TODO: add more 8/10bit and 4:4:4 formats for Qsv after finishing the ffcheck tool
if (is8bitSwFormatsQsv)
@@ -6213,12 +6357,6 @@ namespace MediaBrowser.Controller.MediaEncoding
if (is8_10bitSwFormatsQsv)
{
- if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase))
- {
- return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "qsv", "hevc", bitDepth);
- }
-
if (string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase))
{
return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface) + GetHwDecoderName(options, "vp9", "qsv", "vp9", bitDepth);
@@ -6230,13 +6368,22 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ if (is8_10_12bitSwFormatsQsv)
+ {
+ if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "qsv", "hevc", bitDepth);
+ }
+ }
+
return null;
}
public string GetNvdecVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
{
if ((!OperatingSystem.IsWindows() && !OperatingSystem.IsLinux())
- || !string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.nvenc)
{
return null;
}
@@ -6245,6 +6392,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var is8bitSwFormatsNvdec = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
var is8_10bitSwFormatsNvdec = is8bitSwFormatsNvdec || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is8_10_12bitSwFormatsNvdec = is8_10bitSwFormatsNvdec
+ || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
// TODO: add more 8/10/12bit and 4:4:4 formats for Nvdec after finishing the ffcheck tool
if (is8bitSwFormatsNvdec)
@@ -6278,12 +6430,6 @@ namespace MediaBrowser.Controller.MediaEncoding
if (is8_10bitSwFormatsNvdec)
{
- if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
- || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
- {
- return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "cuvid", "hevc", bitDepth);
- }
-
if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface) + GetHwDecoderName(options, "vp9", "cuvid", "vp9", bitDepth);
@@ -6295,13 +6441,22 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ if (is8_10_12bitSwFormatsNvdec)
+ {
+ if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface) + GetHwDecoderName(options, "hevc", "cuvid", "hevc", bitDepth);
+ }
+ }
+
return null;
}
public string GetAmfVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
{
if (!OperatingSystem.IsWindows()
- || !string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.amf)
{
return null;
}
@@ -6357,7 +6512,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetVaapiVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
{
if (!OperatingSystem.IsLinux()
- || !string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.vaapi)
{
return null;
}
@@ -6369,6 +6524,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var is8bitSwFormatsVaapi = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
var is8_10bitSwFormatsVaapi = is8bitSwFormatsVaapi || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is8_10_12bitSwFormatsVaapi = is8_10bitSwFormatsVaapi
+ || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
if (is8bitSwFormatsVaapi)
{
@@ -6396,12 +6559,6 @@ namespace MediaBrowser.Controller.MediaEncoding
if (is8_10bitSwFormatsVaapi)
{
- if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
- || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
- {
- return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface);
- }
-
if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
return GetHwaccelType(state, options, "vp9", bitDepth, hwSurface);
@@ -6413,13 +6570,22 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ if (is8_10_12bitSwFormatsVaapi)
+ {
+ if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+ {
+ return GetHwaccelType(state, options, "hevc", bitDepth, hwSurface);
+ }
+ }
+
return null;
}
public string GetVideotoolboxVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
{
if (!OperatingSystem.IsMacOS()
- || !string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.videotoolbox)
{
return null;
}
@@ -6427,6 +6593,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var is8bitSwFormatsVt = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|| string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
+ var is8_10_12bitSwFormatsVt = is8_10bitSwFormatsVt
+ || string.Equals("yuv422p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv420p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv422p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("yuv444p12le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
// The related patches make videotoolbox hardware surface working is only available in jellyfin-ffmpeg 7.0.1 at the moment.
bool useHwSurface = (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface) && IsVideoToolboxFullSupported();
@@ -6447,15 +6621,18 @@ namespace MediaBrowser.Controller.MediaEncoding
return GetHwaccelType(state, options, "h264", bitDepth, useHwSurface);
}
- if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
- || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
- return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface);
+ return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface);
}
+ }
- if (string.Equals("vp9", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (is8_10_12bitSwFormatsVt)
+ {
+ if (string.Equals("hevc", videoStream.Codec, StringComparison.OrdinalIgnoreCase)
+ || string.Equals("h265", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
- return GetHwaccelType(state, options, "vp9", bitDepth, useHwSurface);
+ return GetHwaccelType(state, options, "hevc", bitDepth, useHwSurface);
}
}
@@ -6467,7 +6644,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isLinux = OperatingSystem.IsLinux();
if (!isLinux
- || !string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
+ || options.HardwareAccelerationType != HardwareAccelerationType.rkmpp)
{
return null;
}
@@ -6680,7 +6857,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(state.InputVideoSync))
{
- inputModifier += " -vsync " + state.InputVideoSync;
+ inputModifier += GetVideoSyncOption(state.InputVideoSync, _mediaEncoder.EncoderVersion);
}
if (state.ReadInputAtNativeFramerate && state.InputProtocol != MediaProtocol.Rtsp)
@@ -6731,7 +6908,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.IsVideoRequest)
{
- if (!string.IsNullOrEmpty(state.InputContainer) && state.VideoType == VideoType.VideoFile && string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
+ if (!string.IsNullOrEmpty(state.InputContainer) && state.VideoType == VideoType.VideoFile && encodingOptions.HardwareAccelerationType != HardwareAccelerationType.none)
{
var inputFormat = GetInputFormat(state.InputContainer);
if (!string.IsNullOrEmpty(inputFormat))
@@ -6847,7 +7024,7 @@ namespace MediaBrowser.Controller.MediaEncoding
state.SupportedAudioCodecs = supportedAudioCodecsList.ToArray();
- request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToAudioCodec(i))
+ request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(_mediaEncoder.CanEncodeToAudioCodec)
?? state.SupportedAudioCodecs.FirstOrDefault();
}
@@ -6973,7 +7150,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return " -codec:s:0 " + codec + " -disposition:s:0 default";
}
- public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string defaultPreset)
+ public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, EncoderPreset defaultPreset)
{
// Get the output codec name
var videoCodec = GetVideoEncoder(state, encodingOptions);
@@ -7024,7 +7201,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultPreset)
+ public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, EncoderPreset defaultPreset)
{
var args = "-codec:v:0 " + videoCodec;
@@ -7065,7 +7242,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args += keyFrameArg;
- var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state);
var hasCopyTs = false;
@@ -7103,7 +7280,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(state.OutputVideoSync))
{
- args += " -vsync " + state.OutputVideoSync;
+ args += GetVideoSyncOption(state.OutputVideoSync, _mediaEncoder.EncoderVersion);
}
args += GetOutputFFlags(state);
@@ -7270,5 +7447,39 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
}
+
+ private static bool ShouldEncodeSubtitle(EncodingJobInfo state)
+ {
+ return state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ || (state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding && !IsCopyCodec(state.OutputVideoCodec));
+ }
+
+ public static string GetVideoSyncOption(string videoSync, Version encoderVersion)
+ {
+ if (string.IsNullOrEmpty(videoSync))
+ {
+ return string.Empty;
+ }
+
+ if (encoderVersion >= new Version(5, 1))
+ {
+ if (int.TryParse(videoSync, CultureInfo.InvariantCulture, out var vsync))
+ {
+ return vsync switch
+ {
+ -1 => " -fps_mode auto",
+ 0 => " -fps_mode passthrough",
+ 1 => " -fps_mode cfr",
+ 2 => " -fps_mode vfr",
+ _ => string.Empty
+ };
+ }
+
+ return string.Empty;
+ }
+
+ // -vsync is deprecated in FFmpeg 5.1 and will be removed in the future.
+ return $" -vsync {videoSync}";
+ }
}
}