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.cs574
1 files changed, 421 insertions, 153 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index f330ed6ab..24cd141dc 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -65,6 +65,10 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0);
private readonly Version _minFFmpegReadrateOption = new Version(5, 0);
private readonly Version _minFFmpegWorkingVtHwSurface = new Version(7, 0, 1);
+ private readonly Version _minFFmpegDisplayRotationOption = new Version(6, 0);
+ 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 static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled);
@@ -103,6 +107,9 @@ namespace MediaBrowser.Controller.MediaEncoding
"m4v",
};
+ private static readonly string[] _legacyTonemapModes = new[] { "max", "rgb" };
+ private static readonly string[] _advancedTonemapModes = new[] { "lum", "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
private static readonly Dictionary<string, int> _audioTranscodeChannelLookup = new(StringComparer.OrdinalIgnoreCase)
@@ -231,6 +238,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("tonemap_vaapi")
&& _mediaEncoder.SupportsFilter("procamp_vaapi")
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVaapiFrameSync)
+ && _mediaEncoder.SupportsFilter("transpose_vaapi")
&& _mediaEncoder.SupportsFilter("hwupload_vaapi");
}
@@ -248,6 +256,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("scale_opencl")
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapOpenclBt2390)
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayOpenclFrameSync);
+
+ // Let transpose_opencl optional for the time being.
}
private bool IsCudaFullSupported()
@@ -258,6 +268,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TonemapCudaName)
&& _mediaEncoder.SupportsFilter("overlay_cuda")
&& _mediaEncoder.SupportsFilter("hwupload_cuda");
+
+ // Let transpose_cuda optional for the time being.
}
private bool IsVulkanFullSupported()
@@ -265,7 +277,9 @@ namespace MediaBrowser.Controller.MediaEncoding
return _mediaEncoder.SupportsHwaccel("vulkan")
&& _mediaEncoder.SupportsFilter("libplacebo")
&& _mediaEncoder.SupportsFilter("scale_vulkan")
- && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync);
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync)
+ && _mediaEncoder.SupportsFilter("transpose_vulkan")
+ && _mediaEncoder.SupportsFilter("flip_vulkan");
}
private bool IsVideoToolboxFullSupported()
@@ -275,6 +289,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("overlay_videotoolbox")
&& _mediaEncoder.SupportsFilter("tonemap_videotoolbox")
&& _mediaEncoder.SupportsFilter("scale_vt");
+
+ // Let transpose_vt optional for the time being.
}
private bool IsSwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
@@ -282,14 +298,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.VideoStream is null
|| !options.EnableTonemapping
|| GetVideoColorBitDepth(state) != 10
- || !_mediaEncoder.SupportsFilter("tonemapx")
- || !(string.Equals(state.VideoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) || string.Equals(state.VideoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)))
+ || !_mediaEncoder.SupportsFilter("tonemapx"))
{
return false;
}
- return state.VideoStream.VideoRange == VideoRange.HDR
- && state.VideoStream.VideoRangeType is VideoRangeType.HDR10 or VideoRangeType.HLG or VideoRangeType.DOVIWithHDR10 or VideoRangeType.DOVIWithHLG;
+ return state.VideoStream.VideoRange == VideoRange.HDR;
}
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
@@ -335,7 +349,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& GetVideoColorBitDepth(state) == 10;
}
- private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
+ private bool IsIntelVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream is null
|| !options.EnableVppTonemapping
@@ -344,7 +358,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- // Native VPP tonemapping may come to QSV in the future.
+ // 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)
+ && _mediaEncoder.EncoderVersion < _minFFmpegQsvVppTonemapOption)
+ {
+ return false;
+ }
return state.VideoStream.VideoRange == VideoRange.HDR
&& (state.VideoStream.VideoRangeType == VideoRangeType.HDR10
@@ -865,17 +886,23 @@ namespace MediaBrowser.Controller.MediaEncoding
renderNodePath);
}
- private string GetQsvDeviceArgs(string alias)
+ private string GetQsvDeviceArgs(string renderNodePath, string alias)
{
var arg = " -init_hw_device qsv=" + (alias ?? QsvAlias);
if (OperatingSystem.IsLinux())
{
// derive qsv from vaapi device
- return GetVaapiDeviceArgs(null, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias;
+ return GetVaapiDeviceArgs(renderNodePath, "iHD", "i915", null, VaapiAlias) + arg + "@" + VaapiAlias;
}
if (OperatingSystem.IsWindows())
{
+ // on Windows, the deviceIndex is an int
+ if (int.TryParse(renderNodePath, NumberStyles.Integer, CultureInfo.InvariantCulture, out int deviceIndex))
+ {
+ return GetD3d11vaDeviceArgs(deviceIndex, string.Empty, D3d11vaAlias) + arg + "@" + D3d11vaAlias;
+ }
+
// derive qsv from d3d11va device
return GetD3d11vaDeviceArgs(0, "0x8086", D3d11vaAlias) + arg + "@" + D3d11vaAlias;
}
@@ -1035,7 +1062,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- args.Append(GetQsvDeviceArgs(QsvAlias));
+ args.Append(GetQsvDeviceArgs(options.QsvDevice, QsvAlias));
var filterDevArgs = GetFilterHwDeviceArgs(QsvAlias);
// child device used by qsv.
if (_mediaEncoder.SupportsHwaccel("vaapi") || _mediaEncoder.SupportsHwaccel("d3d11va"))
@@ -1147,9 +1174,6 @@ namespace MediaBrowser.Controller.MediaEncoding
args.Append(vidDecoder);
}
- // hw transpose filters should be added manually.
- args.Append(" -noautorotate");
-
return args.ToString().Trim();
}
@@ -1473,7 +1497,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // TODO: Perhaps also use original_size=1920x800 ??
return string.Format(
CultureInfo.InvariantCulture,
"subtitles=f='{0}'{1}{2}{3}{4}{5}",
@@ -1495,7 +1518,6 @@ namespace MediaBrowser.Controller.MediaEncoding
alphaParam,
sub2videoParam,
fontParam,
- // fallbackFontParam,
setPtsParam);
}
@@ -1648,7 +1670,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doOclTonemap = _mediaEncoder.SupportsHwaccel("qsv")
&& IsVaapiSupported(state)
&& IsOpenclFullSupported()
- && !IsVaapiVppTonemapAvailable(state, encodingOptions)
+ && !IsIntelVppTonemapAvailable(state, encodingOptions)
&& IsHwTonemapAvailable(state, encodingOptions);
enableWaFori915Hang = isIntelDecoder && doOclTonemap;
@@ -1769,12 +1791,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += " -preset veryfast";
}
-
- // Only h264_qsv has look_ahead option
- if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- param += " -look_ahead 0";
- }
}
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase) // hevc (hevc_nvenc)
@@ -2061,7 +2077,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
{
- param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none";
+ param += " -x264opts:0 subme=0:me_range=16:rc_lookahead=10:me=hex:open_gop=0";
}
if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@@ -2069,8 +2085,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// libx265 only accept level option in -x265-params.
// level option may cause libx265 to fail.
// libx265 cannot adjust the given level, just throw an error.
- // TODO: set fine tuned params.
- param += " -x265-params:0 no-info=1";
+ param += " -x265-params:0 subme=3:merange=25:rc-lookahead=10:me=star:ctu=32:max-tu-size=32:min-cu-size=16:rskip=2:rskip-edge-threshold=2:no-sao=1:no-strong-intra-smoothing=1:no-scenecut=1:no-open-gop=1:no-info=1";
}
if (string.Equals(videoEncoder, "libsvtav1", StringComparison.OrdinalIgnoreCase)
@@ -2947,8 +2962,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
public static string GetHwScaleFilter(
+ string hwScalePrefix,
string hwScaleSuffix,
string videoFormat,
+ bool swapOutputWandH,
int? videoWidth,
int? videoHeight,
int? requestedWidth,
@@ -2970,8 +2987,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|| !videoHeight.HasValue
|| outHeight.Value != videoHeight.Value;
- var arg1 = isSizeFixed ? ("=w=" + outWidth.Value + ":h=" + outHeight.Value) : string.Empty;
- var arg2 = isFormatFixed ? ("format=" + videoFormat) : string.Empty;
+ var swpOutW = swapOutputWandH ? outHeight.Value : outWidth.Value;
+ var swpOutH = swapOutputWandH ? outWidth.Value : outHeight.Value;
+
+ var arg1 = isSizeFixed ? $"=w={swpOutW}:h={swpOutH}" : string.Empty;
+ var arg2 = isFormatFixed ? $"format={videoFormat}" : string.Empty;
if (isFormatFixed)
{
arg2 = (isSizeFixed ? ':' : '=') + arg2;
@@ -2981,7 +3001,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Format(
CultureInfo.InvariantCulture,
- "scale_{0}{1}{2}",
+ "{0}_{1}{2}{3}",
+ hwScalePrefix ?? "scale",
hwScaleSuffix,
arg1,
arg2);
@@ -3010,7 +3031,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Format(
CultureInfo.InvariantCulture,
- @"scale=-1:{1}:fast_bilinear,scale,crop,pad=max({0}\,iw):max({1}\,ih):(ow-iw)/2:(oh-ih)/2:black@0,crop={0}:{1}",
+ @"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);
}
@@ -3221,14 +3242,18 @@ namespace MediaBrowser.Controller.MediaEncoding
doubleRateDeint ? "1" : "0");
}
- public static string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix)
+ public string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix)
{
var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30;
if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase))
{
+ var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.SupportsFilter("bwdif_cuda");
+
return string.Format(
CultureInfo.InvariantCulture,
- "yadif_cuda={0}:-1:0",
+ "{0}_cuda={1}:-1:0",
+ useBwdif ? "bwdif" : "yadif",
doubleRateDeint ? "1" : "0");
}
@@ -3268,26 +3293,45 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase))
{
- args = "procamp_vaapi=b={1}:c={2},tonemap_vaapi=format={0}:p=bt709:t=bt709:m=bt709:extra_hw_frames=32";
+ var doVaVppProcamp = false;
+ var procampParams = string.Empty;
+ if (options.VppTonemappingBrightness != 0
+ && options.VppTonemappingBrightness >= -100
+ && options.VppTonemappingBrightness <= 100)
+ {
+ procampParams += $"=b={options.VppTonemappingBrightness}";
+ doVaVppProcamp = true;
+ }
+
+ if (options.VppTonemappingContrast > 1
+ && options.VppTonemappingContrast <= 10)
+ {
+ procampParams += doVaVppProcamp ? ":" : "=";
+ procampParams += $"c={options.VppTonemappingContrast}";
+ doVaVppProcamp = true;
+ }
+
+ args = "{0}tonemap_vaapi=format={1}:p=bt709:t=bt709:m=bt709:extra_hw_frames=32";
return string.Format(
CultureInfo.InvariantCulture,
args,
- videoFormat ?? "nv12",
- options.VppTonemappingBrightness,
- options.VppTonemappingContrast);
+ doVaVppProcamp ? $"procamp_vaapi{procampParams}," : string.Empty,
+ videoFormat ?? "nv12");
}
else
{
args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}";
- if (string.Equals(options.TonemappingMode, "max", StringComparison.OrdinalIgnoreCase)
- || string.Equals(options.TonemappingMode, "rgb", StringComparison.OrdinalIgnoreCase))
+ var useLegacyTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode
+ && _legacyTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+
+ var useAdvancedTonemapModes = _mediaEncoder.EncoderVersion >= _minFFmpegAdvancedTonemapMode
+ && _advancedTonemapModes.Contains(options.TonemappingMode, StringComparison.OrdinalIgnoreCase);
+
+ if (useLegacyTonemapModes || useAdvancedTonemapModes)
{
- if (_mediaEncoder.EncoderVersion >= _minFFmpegOclCuTonemapMode)
- {
- args += ":tonemap_mode={5}";
- }
+ args += ":tonemap_mode={5}";
}
if (options.TonemappingParam != 0)
@@ -3359,15 +3403,7 @@ namespace MediaBrowser.Controller.MediaEncoding
algorithm = "clip";
}
- tonemapArg = ":tonemapping=" + algorithm;
-
- if (string.Equals(mode, "max", StringComparison.OrdinalIgnoreCase)
- || string.Equals(mode, "rgb", StringComparison.OrdinalIgnoreCase))
- {
- tonemapArg += ":tonemapping_mode=" + mode;
- }
-
- tonemapArg += ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
+ tonemapArg = ":tonemapping=" + algorithm + ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709";
if (string.Equals(range, "tv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(range, "pc", StringComparison.OrdinalIgnoreCase))
@@ -3384,6 +3420,18 @@ namespace MediaBrowser.Controller.MediaEncoding
tonemapArg);
}
+ public string GetVideoTransposeDirection(EncodingJobInfo state)
+ {
+ return (state.VideoStream?.Rotation ?? 0) switch
+ {
+ 90 => "cclock",
+ 180 => "reversal",
+ -90 => "clock",
+ -180 => "reversal",
+ _ => string.Empty
+ };
+ }
+
/// <summary>
/// Gets the parameter of software filter chain.
/// </summary>
@@ -3413,11 +3461,17 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
var doDeintH2645 = doDeintH264 || doDeintHevc;
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 hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var swapWAndH = Math.Abs(rotation) == 90;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3432,7 +3486,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = isSwDecoder ? "yuv420p" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
if (isVaapiEncoder)
{
outFormat = "nv12";
@@ -3445,11 +3499,13 @@ namespace MediaBrowser.Controller.MediaEncoding
// sw scale
mainFilters.Add(swScaleFilter);
- // sw tonemap <= TODO: finish dovi tone mapping
-
+ // sw tonemap
if (doToneMap)
{
- var tonemapArgs = $"tonemapx=tonemap={options.TonemappingAlgorithm}:desat={options.TonemappingDesat}:peak={options.TonemappingPeak}:t=bt709:m=bt709:p=bt709:format={outFormat}";
+ // tonemapx requires yuv420p10 input for dovi reshaping, let ffmpeg convert the frame when necessary
+ var tonemapFormat = requireDoviReshaping ? "yuv420p" : outFormat;
+
+ var tonemapArgs = $"tonemapx=tonemap={options.TonemappingAlgorithm}:desat={options.TonemappingDesat}:peak={options.TonemappingPeak}:t=bt709:m=bt709:p=bt709:format={tonemapFormat}";
if (options.TonemappingParam != 0)
{
@@ -3481,7 +3537,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3555,6 +3611,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doCuTranspose = !string.IsNullOrEmpty(tranposeDir) && _mediaEncoder.SupportsFilter("transpose_cuda");
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isNvDecoder && doCuTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3571,10 +3634,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doCuTonemap ? "yuv420p10le" : "yuv420p";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// sw => hw
if (doCuTonemap)
@@ -3593,8 +3656,14 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doCuTranspose)
+ {
+ mainFilters.Add($"transpose_cuda=dir={tranposeDir}");
+ }
+
var outFormat = doCuTonemap ? string.Empty : "yuv420p";
- var hwScaleFilter = GetHwScaleFilter("cuda", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "cuda", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// hw scale
mainFilters.Add(hwScaleFilter);
}
@@ -3644,7 +3713,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3654,7 +3723,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3669,7 +3738,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3745,6 +3814,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doOclTranspose = !string.IsNullOrEmpty(tranposeDir)
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.TransposeOpenclReversal);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isD3d11vaDecoder && doOclTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -3761,19 +3838,19 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : "yuv420p";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
// sw => hw
if (doOclTonemap)
{
- mainFilters.Add("hwupload=derive_device=d3d11va:extra_hw_frames=16");
+ mainFilters.Add("hwupload=derive_device=d3d11va:extra_hw_frames=24");
mainFilters.Add("format=d3d11");
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
}
@@ -3781,12 +3858,18 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// INPUT d3d11 surface(vram)
// map from d3d11va to opencl via d3d11-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
// hw deint <= TODO: finsh the 'yadif_opencl' filter
+ // hw transpose
+ if (doOclTranspose)
+ {
+ mainFilters.Add($"transpose_opencl=dir={tranposeDir}");
+ }
+
var outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("opencl", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "opencl", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// hw scale
mainFilters.Add(hwScaleFilter);
}
@@ -3831,7 +3914,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT d3d11(nv12) surface(vram)
// reverse-mapping via d3d11-opencl interop.
- mainFilters.Add("hwmap=derive_device=d3d11va:reverse=1");
+ mainFilters.Add("hwmap=derive_device=d3d11va:mode=write:reverse=1");
mainFilters.Add("format=d3d11");
}
@@ -3844,7 +3927,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=yuva420p");
}
@@ -3854,7 +3937,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=yuva420p,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=yuva420p");
@@ -3863,7 +3946,7 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add("hwupload=derive_device=opencl");
overlayFilters.Add("overlay_opencl=eof_action=pass:repeatlast=0");
- overlayFilters.Add("hwmap=derive_device=d3d11va:reverse=1");
+ overlayFilters.Add("hwmap=derive_device=d3d11va:mode=write:reverse=1");
overlayFilters.Add("format=d3d11");
}
}
@@ -3871,7 +3954,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -3958,7 +4041,9 @@ namespace MediaBrowser.Controller.MediaEncoding
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 doVppTonemap = IsIntelVppTonemapAvailable(state, options);
+ var doOclTonemap = !doVppTonemap && IsHwTonemapAvailable(state, options);
+ var doTonemap = doVppTonemap || doOclTonemap;
var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
@@ -3967,10 +4052,17 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || ((isD3d11vaDecoder || isQsvDecoder) && doVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
- mainFilters.Add(GetOverwriteColorPropertiesParam(state, doOclTonemap));
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, doTonemap));
if (isSwDecoder)
{
@@ -3983,10 +4075,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -3998,8 +4090,44 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isD3d11vaDecoder || isQsvDecoder)
{
- var outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var doVppProcamp = false;
+ var procampParams = string.Empty;
+ if (doVppTonemap)
+ {
+ if (options.VppTonemappingBrightness != 0
+ && options.VppTonemappingBrightness >= -100
+ && options.VppTonemappingBrightness <= 100)
+ {
+ procampParams += $":brightness={options.VppTonemappingBrightness}";
+ doVppProcamp = true;
+ }
+
+ if (options.VppTonemappingContrast > 1
+ && options.VppTonemappingContrast <= 10)
+ {
+ procampParams += $":contrast={options.VppTonemappingContrast}";
+ 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 swapOutputWandH = doVppTranspose && swapWAndH;
+ var hwScalePrefix = (doVppTranspose || doVppTonemap) ? "vpp" : "scale";
+ var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, "qsv", outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doVppTonemap)
+ {
+ hwScaleFilter += doVppProcamp ? procampParams : ":tonemap=1";
+ }
if (isD3d11vaDecoder)
{
@@ -4018,14 +4146,26 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
- // hw scale
+ // hw transpose & scale & tonemap(w/o procamp)
mainFilters.Add(hwScaleFilter);
+
+ // hw tonemap(w/ procamp)
+ if (doVppTonemap && doVppProcamp)
+ {
+ mainFilters.Add("vpp_qsv=tonemap=1:format=nv12:async_depth=2");
+ }
+
+ // force bt709 just in case vpp tonemap is not triggered or using MSDK instead of VPL.
+ if (doVppTonemap)
+ {
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, false));
+ }
}
if (doOclTonemap && isHwDecoder)
{
// map from qsv to opencl via qsv(d3d11)-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// hw tonemap
@@ -4069,7 +4209,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT qsv(nv12) surface(vram)
// reverse-mapping via qsv(d3d11)-opencl interop.
- mainFilters.Add("hwmap=derive_device=qsv:reverse=1");
+ mainFilters.Add("hwmap=derive_device=qsv:mode=write:reverse=1");
mainFilters.Add("format=qsv");
}
@@ -4083,7 +4223,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4093,7 +4233,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4104,9 +4244,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// default to 64 otherwise it will fail on certain iGPU.
subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayQsvFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4119,7 +4259,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4152,7 +4292,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- var doVaVppTonemap = IsVaapiVppTonemapAvailable(state, options);
+ var doVaVppTonemap = IsIntelVppTonemapAvailable(state, options);
var doOclTonemap = !doVaVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
@@ -4164,6 +4304,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || ((isVaapiDecoder || isQsvDecoder) && doVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4180,10 +4327,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -4195,24 +4342,39 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (isVaapiDecoder || isQsvDecoder)
{
+ var hwFilterSuffix = isVaapiDecoder ? "vaapi" : "qsv";
+
// INPUT vaapi/qsv surface(vram)
// hw deint
if (doDeintH2645)
{
- var deintFilter = GetHwDeinterlaceFilter(state, options, isVaapiDecoder ? "vaapi" : "qsv");
+ var deintFilter = GetHwDeinterlaceFilter(state, options, hwFilterSuffix);
mainFilters.Add(deintFilter);
}
- var outFormat = doTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter(isVaapiDecoder ? "vaapi" : "qsv", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ // hw transpose(vaapi vpp)
+ if (isVaapiDecoder && doVppTranspose)
+ {
+ mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
+ }
- // allocate extra pool sizes for vaapi vpp
+ var outFormat = doOclTonemap ? ((isQsvDecoder && doVppTranspose) ? "p010" : string.Empty) : "nv12";
+ var swapOutputWandH = isQsvDecoder && doVppTranspose && swapWAndH;
+ var hwScalePrefix = (isQsvDecoder && doVppTranspose) ? "vpp" : "scale";
+ var hwScaleFilter = GetHwScaleFilter(hwScalePrefix, hwFilterSuffix, outFormat, swapOutputWandH, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
+
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isQsvDecoder && doVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
+ // allocate extra pool sizes for vaapi vpp scale
if (!string.IsNullOrEmpty(hwScaleFilter) && isVaapiDecoder)
{
hwScaleFilter += ":extra_hw_frames=24";
}
- // hw scale
+ // hw transpose(qsv vpp) & scale
mainFilters.Add(hwScaleFilter);
}
@@ -4240,7 +4402,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doOclTonemap && isHwDecoder)
{
// map from qsv to opencl via qsv(vaapi)-opencl interop.
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// ocl tonemap
@@ -4287,7 +4449,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// OUTPUT qsv(nv12) surface(vram)
// reverse-mapping via qsv(vaapi)-opencl interop.
// add extra pool size to avoid the 'cannot allocate memory' error on hevc_qsv.
- mainFilters.Add("hwmap=derive_device=qsv:reverse=1:extra_hw_frames=16");
+ mainFilters.Add("hwmap=derive_device=qsv:mode=write:reverse=1:extra_hw_frames=16");
mainFilters.Add("format=qsv");
}
else if (isVaapiDecoder)
@@ -4307,7 +4469,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4316,7 +4478,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4327,9 +4489,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// default to 64 otherwise it will fail on certain iGPU.
subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayQsvFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4342,7 +4504,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -4441,7 +4603,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
- var doVaVppTonemap = isVaapiDecoder && IsVaapiVppTonemapAvailable(state, options);
+ var doVaVppTonemap = isVaapiDecoder && IsIntelVppTonemapAvailable(state, options);
var doOclTonemap = !doVaVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
@@ -4453,6 +4615,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVaVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isVaapiDecoder && doVaVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4469,10 +4638,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -4492,8 +4661,14 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doVaVppTranspose)
+ {
+ mainFilters.Add($"transpose_vaapi=dir={tranposeDir}");
+ }
+
var outFormat = doTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
@@ -4515,7 +4690,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (doOclTonemap && isVaapiDecoder)
{
// map from vaapi to opencl via vaapi-opencl interop(Intel only).
- mainFilters.Add("hwmap=derive_device=opencl");
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
}
// ocl tonemap
@@ -4529,7 +4704,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// OUTPUT vaapi(nv12) surface(vram)
// reverse-mapping via vaapi-opencl interop.
- mainFilters.Add("hwmap=derive_device=vaapi:reverse=1");
+ mainFilters.Add("hwmap=derive_device=vaapi:mode=write:reverse=1");
mainFilters.Add("format=vaapi");
}
@@ -4580,7 +4755,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (hasGraphicalSubs)
{
// overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, 1080);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4589,7 +4764,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, 1080, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4598,9 +4773,9 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters.Add("hwupload=derive_device=vaapi");
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var (overlayW, overlayH) = GetFixedOutputSize(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
- ? (":w=" + overlayW.Value + ":h=" + overlayH.Value)
+ ? $":w={overlayW.Value}:h={overlayH.Value}"
: string.Empty;
var overlayVaapiFilter = string.Format(
CultureInfo.InvariantCulture,
@@ -4613,7 +4788,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -4658,6 +4833,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVkTranspose = isVaapiDecoder && !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isVaapiDecoder && doVkTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4682,7 +4864,7 @@ namespace MediaBrowser.Controller.MediaEncoding
else
{
// sw scale
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(swScaleFilter);
mainFilters.Add("format=nv12");
}
@@ -4690,11 +4872,37 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (isVaapiDecoder)
{
// INPUT vaapi surface(vram)
- if (doVkTonemap || hasSubs)
+ if (doVkTranspose || doVkTonemap || hasSubs)
{
// map from vaapi to vulkan/drm via interop (Polaris/gfx8+).
- mainFilters.Add("hwmap=derive_device=vulkan");
- mainFilters.Add("format=vulkan");
+ if (_mediaEncoder.EncoderVersion >= _minFFmpegAlteredVaVkInterop)
+ {
+ if (doVkTranspose || !_mediaEncoder.IsVaapiDeviceSupportVulkanDrmModifier)
+ {
+ // disable the indirect va-drm-vk mapping since it's no longer reliable.
+ mainFilters.Add("hwmap=derive_device=drm");
+ mainFilters.Add("format=drm_prime");
+ mainFilters.Add("hwmap=derive_device=vulkan");
+ mainFilters.Add("format=vulkan");
+
+ // workaround for libplacebo using the imported vulkan frame on gfx8.
+ if (!_mediaEncoder.IsVaapiDeviceSupportVulkanDrmModifier)
+ {
+ mainFilters.Add("scale_vulkan");
+ }
+ }
+ else if (doVkTonemap || hasSubs)
+ {
+ // non ad-hoc libplacebo also accepts drm_prime direct input.
+ mainFilters.Add("hwmap=derive_device=drm");
+ mainFilters.Add("format=drm_prime");
+ }
+ }
+ else // legacy va-vk mapping that works only in jellyfin-ffmpeg6
+ {
+ mainFilters.Add("hwmap=derive_device=vulkan");
+ mainFilters.Add("format=vulkan");
+ }
}
else
{
@@ -4706,16 +4914,30 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// hw scale
- var hwScaleFilter = GetHwScaleFilter("vaapi", "nv12", inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", "nv12", false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(hwScaleFilter);
}
}
+ // vk transpose
+ if (doVkTranspose)
+ {
+ if (string.Equals(tranposeDir, "reversal", StringComparison.OrdinalIgnoreCase))
+ {
+ mainFilters.Add("flip_vulkan");
+ }
+ else
+ {
+ mainFilters.Add($"transpose_vulkan=dir={tranposeDir}");
+ }
+ }
+
// vk libplacebo
if (doVkTonemap || hasSubs)
{
- var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var libplaceboFilter = GetLibplaceboFilter(options, "bgra", doVkTonemap, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
mainFilters.Add(libplaceboFilter);
+ mainFilters.Add("format=vulkan");
}
if (doVkTonemap && !hasSubs)
@@ -4758,7 +4980,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -4767,7 +4989,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -4839,6 +5061,11 @@ namespace MediaBrowser.Controller.MediaEncoding
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var swapWAndH = Math.Abs(rotation) == 90 && isSwDecoder;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -4856,7 +5083,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
outFormat = doOclTonemap ? "yuv420p10le" : "nv12";
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
// sw scale
mainFilters.Add(swScaleFilter);
mainFilters.Add("format=" + outFormat);
@@ -4880,7 +5107,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
outFormat = doOclTonemap ? string.Empty : "nv12";
- var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var hwScaleFilter = GetHwScaleFilter("scale", "vaapi", outFormat, false, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
// allocate extra pool sizes for vaapi vpp
if (!string.IsNullOrEmpty(hwScaleFilter))
@@ -4976,7 +5203,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
@@ -5007,13 +5234,15 @@ namespace MediaBrowser.Controller.MediaEncoding
return (null, null, null);
}
+ // ReSharper disable once InconsistentNaming
var isMacOS = OperatingSystem.IsMacOS();
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
+ var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
var isVtFullSupported = isMacOS && IsVideoToolboxFullSupported();
// legacy videotoolbox pipeline (disable hw filters)
- if (!isVtEncoder
+ if (!(isVtEncoder || isVtDecoder)
|| !isVtFullSupported
|| !_mediaEncoder.SupportsFilter("alphasrc"))
{
@@ -5030,6 +5259,9 @@ namespace MediaBrowser.Controller.MediaEncoding
string vidDecoder,
string vidEncoder)
{
+ var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
+ var isVtDecoder = vidDecoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
+
var inW = state.VideoStream?.Width;
var inH = state.VideoStream?.Height;
var reqW = state.BaseRequest.Width;
@@ -5038,9 +5270,6 @@ namespace MediaBrowser.Controller.MediaEncoding
var reqMaxH = state.BaseRequest.MaxHeight;
var threeDFormat = state.MediaSource.Video3DFormat;
- var isVtEncoder = vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase);
- var isVtDecoder = vidDecoder.Contains("videotoolbox", 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;
@@ -5048,6 +5277,13 @@ namespace MediaBrowser.Controller.MediaEncoding
var doMetalTonemap = !doVtTonemap && IsHwTonemapAvailable(state, options);
var usingHwSurface = isVtDecoder && (_mediaEncoder.EncoderVersion >= _minFFmpegWorkingVtHwSurface);
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doVtTranspose = !string.IsNullOrEmpty(tranposeDir) && _mediaEncoder.SupportsFilter("transpose_vt");
+ var swapWAndH = Math.Abs(rotation) == 90 && doVtTranspose;
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
var scaleFormat = string.Empty;
// Use P010 for Metal tone mapping, otherwise force an 8bit output.
if (!string.Equals(state.VideoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase))
@@ -5065,7 +5301,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- var hwScaleFilter = GetHwScaleFilter("vt", scaleFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ 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 hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
@@ -5074,12 +5310,6 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
- if (!isVtEncoder)
- {
- // should not happen.
- return (null, null, null);
- }
-
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -5090,6 +5320,12 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters.Add(deintFilter);
}
+ // hw transpose
+ if (doVtTranspose)
+ {
+ mainFilters.Add($"transpose_vt=dir={tranposeDir}");
+ }
+
if (doVtTonemap)
{
const string VtTonemapArgs = "color_matrix=bt709:color_primaries=bt709:color_transfer=bt709";
@@ -5118,7 +5354,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5127,7 +5363,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var framerate = state.VideoStream?.RealFrameRate;
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -5140,6 +5376,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (usingHwSurface)
{
+ if (!isVtEncoder)
+ {
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
+
return (mainFilters, subFilters, overlayFilters);
}
@@ -5153,6 +5395,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// this will pass-through automatically if in/out format matches.
mainFilters.Insert(0, "hwupload");
mainFilters.Insert(0, "format=nv12|p010le|videotoolbox_vld");
+
+ if (!isVtEncoder)
+ {
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
}
return (mainFilters, subFilters, overlayFilters);
@@ -5229,6 +5477,13 @@ namespace MediaBrowser.Controller.MediaEncoding
&& (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+ var rotation = state.VideoStream?.Rotation ?? 0;
+ var tranposeDir = rotation == 0 ? string.Empty : GetVideoTransposeDirection(state);
+ var doRkVppTranspose = !string.IsNullOrEmpty(tranposeDir);
+ var swapWAndH = Math.Abs(rotation) == 90 && (isSwDecoder || (isRkmppDecoder && doRkVppTranspose));
+ var swpInW = swapWAndH ? inH : inW;
+ var swpInH = swapWAndH ? inW : inH;
+
/* Make main filters for video stream */
var mainFilters = new List<string>();
@@ -5245,7 +5500,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
- var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, swpInW, swpInH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
if (!string.IsNullOrEmpty(swScaleFilter))
{
swScaleFilter += ":flags=fast_bilinear";
@@ -5253,7 +5508,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// sw scale
mainFilters.Add(swScaleFilter);
- mainFilters.Add("format=" + outFormat);
+ mainFilters.Add($"format={outFormat}");
// keep video at memory except ocl tonemap,
// since the overhead caused by hwupload >>> using sw filter.
@@ -5268,21 +5523,29 @@ namespace MediaBrowser.Controller.MediaEncoding
// INPUT rkmpp/drm surface(gem/dma-heap)
var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap;
+ var swapOutputWandH = doRkVppTranspose && swapWAndH;
var outFormat = doOclTonemap ? "p010" : "nv12";
- var hwScaleFilter = GetHwScaleFilter("rkrga", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
- var hwScaleFilter2 = GetHwScaleFilter("rkrga", string.Empty, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ 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);
if (!hasSubs
+ || doRkVppTranspose
|| !isFullAfbcPipeline
|| !string.IsNullOrEmpty(hwScaleFilter2))
{
+ if (!string.IsNullOrEmpty(hwScaleFilter) && doRkVppTranspose)
+ {
+ hwScaleFilter += $":transpose={tranposeDir}";
+ }
+
// try enabling AFBC to save DDR bandwidth
if (!string.IsNullOrEmpty(hwScaleFilter) && isFullAfbcPipeline)
{
hwScaleFilter += ":afbc=1";
}
- // hw scale
+ // hw transpose & scale
mainFilters.Add(hwScaleFilter);
}
}
@@ -5353,7 +5616,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
subFilters.Add("format=bgra");
}
@@ -5363,7 +5626,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var subFramerate = hasAssSubs ? Math.Min(framerate ?? 25, 60) : 10;
// alphasrc=s=1280x720:r=10:start=0,format=bgra,subtitles,hwupload
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
+ var alphaSrcFilter = GetAlphaSrcFilter(state, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
subFilters.Add(alphaSrcFilter);
subFilters.Add("format=bgra");
@@ -5380,7 +5643,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (hasGraphicalSubs)
{
- var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
subFilters.Add(subPreProcFilters);
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
}
@@ -5795,6 +6058,11 @@ namespace MediaBrowser.Controller.MediaEncoding
// Disable the extra internal copy in nvdec. We already handle it in filter chain.
var nvdecNoInternalCopy = ffmpegVersion >= _minFFmpegHwaUnsafeOutput;
+ // Strip the display rotation side data from the transposed fmp4 output stream.
+ var stripRotationData = (state.VideoStream?.Rotation ?? 0) != 0
+ && ffmpegVersion >= _minFFmpegDisplayRotationOption;
+ var stripRotationDataArgs = stripRotationData ? " -display_rotation 0" : string.Empty;
+
if (bitDepth == 10 && isCodecAvailable)
{
if (string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
@@ -5819,13 +6087,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isVaapiSupported && isCodecAvailable)
{
- return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
+ return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
if (isD3d11Supported && isCodecAvailable)
{
- return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
+ return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11 -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + " -threads 2" + (isAv1 ? " -c:v av1" : string.Empty);
}
}
@@ -5833,7 +6101,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isQsvSupported && isCodecAvailable)
{
- return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_output_format qsv" : string.Empty);
+ return " -hwaccel qsv" + (outputHwSurface ? " -hwaccel_output_format qsv -noautorotate" + stripRotationDataArgs : string.Empty);
}
}
}
@@ -5846,12 +6114,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if (options.EnableEnhancedNvdecDecoder)
{
// set -threads 1 to nvdec decoder explicitly since it doesn't implement threading support.
- return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty)
+ return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda -noautorotate" + stripRotationDataArgs : string.Empty)
+ (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
}
// cuvid decoder doesn't have threading issue.
- return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
+ return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda -noautorotate" + stripRotationDataArgs : string.Empty);
}
}
@@ -5860,7 +6128,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (isD3d11Supported && isCodecAvailable)
{
- return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11" : string.Empty)
+ return " -hwaccel d3d11va" + (outputHwSurface ? " -hwaccel_output_format d3d11 -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
}
@@ -5870,7 +6138,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isVaapiSupported
&& isCodecAvailable)
{
- return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi" : string.Empty)
+ return " -hwaccel vaapi" + (outputHwSurface ? " -hwaccel_output_format vaapi -noautorotate" + stripRotationDataArgs : string.Empty)
+ (profileMismatch ? " -hwaccel_flags +allow_profile_mismatch" : string.Empty) + (isAv1 ? " -c:v av1" : string.Empty);
}
@@ -5879,7 +6147,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isVideotoolboxSupported
&& isCodecAvailable)
{
- return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty);
+ return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty) + " -noautorotate" + stripRotationDataArgs;
}
// Rockchip rkmpp
@@ -5887,7 +6155,7 @@ namespace MediaBrowser.Controller.MediaEncoding
&& isRkmppSupported
&& isCodecAvailable)
{
- return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime" : string.Empty);
+ return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime -noautorotate" + stripRotationDataArgs : string.Empty);
}
return null;