|
@@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
private const string VaapiAlias = "va";
|
|
|
private const string D3d11vaAlias = "dx11";
|
|
|
private const string VideotoolboxAlias = "vt";
|
|
|
+ private const string RkmppAlias = "rk";
|
|
|
private const string OpenclAlias = "ocl";
|
|
|
private const string CudaAlias = "cu";
|
|
|
private const string DrmAlias = "dr";
|
|
@@ -161,6 +162,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{ "vaapi", hwEncoder + "_vaapi" },
|
|
|
{ "videotoolbox", hwEncoder + "_videotoolbox" },
|
|
|
{ "v4l2m2m", hwEncoder + "_v4l2m2m" },
|
|
|
+ { "rkmpp", hwEncoder + "_rkmpp" },
|
|
|
};
|
|
|
|
|
|
if (!string.IsNullOrEmpty(hwType)
|
|
@@ -217,6 +219,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
&& _mediaEncoder.SupportsFilter("hwupload_vaapi");
|
|
|
}
|
|
|
|
|
|
+ private bool IsRkmppFullSupported()
|
|
|
+ {
|
|
|
+ return _mediaEncoder.SupportsHwaccel("rkmpp")
|
|
|
+ && _mediaEncoder.SupportsFilter("scale_rkrga")
|
|
|
+ && _mediaEncoder.SupportsFilter("vpp_rkrga")
|
|
|
+ && _mediaEncoder.SupportsFilter("overlay_rkrga");
|
|
|
+ }
|
|
|
+
|
|
|
private bool IsOpenclFullSupported()
|
|
|
{
|
|
|
return _mediaEncoder.SupportsHwaccel("opencl")
|
|
@@ -696,6 +706,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return codec.ToLowerInvariant();
|
|
|
}
|
|
|
|
|
|
+ private string GetRkmppDeviceArgs(string alias)
|
|
|
+ {
|
|
|
+ alias ??= RkmppAlias;
|
|
|
+
|
|
|
+ // device selection in rk is not supported.
|
|
|
+ return " -init_hw_device rkmpp=" + alias;
|
|
|
+ }
|
|
|
+
|
|
|
private string GetVideoToolboxDeviceArgs(string alias)
|
|
|
{
|
|
|
alias ??= VideotoolboxAlias;
|
|
@@ -835,30 +853,25 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
|
public string GetGraphicalSubCanvasSize(EncodingJobInfo state)
|
|
|
{
|
|
|
- // DVBSUB and DVDSUB use the fixed canvas size 720x576
|
|
|
+ // DVBSUB uses the fixed canvas size 720x576
|
|
|
if (state.SubtitleStream is not null
|
|
|
&& state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
|
|
|
&& !state.SubtitleStream.IsTextSubtitleStream
|
|
|
- && !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase)
|
|
|
- && !string.Equals(state.SubtitleStream.Codec, "DVDSUB", StringComparison.OrdinalIgnoreCase))
|
|
|
+ && !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
- var inW = state.VideoStream?.Width;
|
|
|
- var inH = state.VideoStream?.Height;
|
|
|
- var reqW = state.BaseRequest.Width;
|
|
|
- var reqH = state.BaseRequest.Height;
|
|
|
- var reqMaxW = state.BaseRequest.MaxWidth;
|
|
|
- var reqMaxH = state.BaseRequest.MaxHeight;
|
|
|
-
|
|
|
- // setup a relative small canvas_size for overlay_qsv/vaapi to reduce transfer overhead
|
|
|
- var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, 1080);
|
|
|
+ var subtitleWidth = state.SubtitleStream?.Width;
|
|
|
+ var subtitleHeight = state.SubtitleStream?.Height;
|
|
|
|
|
|
- if (overlayW.HasValue && overlayH.HasValue)
|
|
|
+ if (subtitleWidth.HasValue
|
|
|
+ && subtitleHeight.HasValue
|
|
|
+ && subtitleWidth.Value > 0
|
|
|
+ && subtitleHeight.Value > 0)
|
|
|
{
|
|
|
return string.Format(
|
|
|
CultureInfo.InvariantCulture,
|
|
|
" -canvas_size {0}x{1}",
|
|
|
- overlayW.Value,
|
|
|
- overlayH.Value);
|
|
|
+ subtitleWidth.Value,
|
|
|
+ subtitleHeight.Value);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1061,6 +1074,33 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
// no videotoolbox hw filter.
|
|
|
args.Append(GetVideoToolboxDeviceArgs(VideotoolboxAlias));
|
|
|
}
|
|
|
+ else if (string.Equals(optHwaccelType, "rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ if (!isLinux || !_mediaEncoder.SupportsHwaccel("rkmpp"))
|
|
|
+ {
|
|
|
+ return string.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ var isRkmppDecoder = vidDecoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
|
|
|
+ var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
|
|
|
+ if (!isRkmppDecoder && !isRkmppEncoder)
|
|
|
+ {
|
|
|
+ return string.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ args.Append(GetRkmppDeviceArgs(RkmppAlias));
|
|
|
+
|
|
|
+ var filterDevArgs = string.Empty;
|
|
|
+ var doOclTonemap = isHwTonemapAvailable && IsOpenclFullSupported();
|
|
|
+
|
|
|
+ if (doOclTonemap && !isRkmppDecoder)
|
|
|
+ {
|
|
|
+ args.Append(GetOpenclDeviceArgs(0, null, RkmppAlias, OpenclAlias));
|
|
|
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
|
|
|
+ }
|
|
|
+
|
|
|
+ args.Append(filterDevArgs);
|
|
|
+ }
|
|
|
|
|
|
if (!string.IsNullOrEmpty(vidDecoder))
|
|
|
{
|
|
@@ -1477,8 +1517,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "h264_amf", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(codec, "h264_rkmpp", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "hevc_qsv", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(codec, "hevc_rkmpp", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "av1_qsv", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "av1_nvenc", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(codec, "av1_amf", StringComparison.OrdinalIgnoreCase)
|
|
@@ -1918,20 +1960,22 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
profile = "constrained_baseline";
|
|
|
}
|
|
|
|
|
|
- // libx264, h264_qsv and h264_nvenc does not support Constrained Baseline profile, force Baseline in this case.
|
|
|
+ // libx264, h264_{qsv,nvenc,rkmpp} does not support Constrained Baseline profile, force Baseline in this case.
|
|
|
if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
|
|
- || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
|
|
|
+ || string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
&& profile.Contains("baseline", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
profile = "baseline";
|
|
|
}
|
|
|
|
|
|
- // libx264, h264_qsv, h264_nvenc and h264_vaapi does not support Constrained High profile, force High in this case.
|
|
|
+ // libx264, h264_{qsv,nvenc,vaapi,rkmpp} does not support Constrained High profile, force High in this case.
|
|
|
if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|
|
|
- || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
|
|
|
+ || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
&& profile.Contains("high", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
profile = "high";
|
|
@@ -2015,6 +2059,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
param += " -level " + level;
|
|
|
}
|
|
|
}
|
|
|
+ else if (string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoEncoder, "hevc_rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ param += " -level " + level;
|
|
|
+ }
|
|
|
else if (!string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
param += " -level " + level;
|
|
@@ -2833,6 +2882,48 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return (outputWidth, outputHeight);
|
|
|
}
|
|
|
|
|
|
+ public static bool IsScaleRatioSupported(
|
|
|
+ int? videoWidth,
|
|
|
+ int? videoHeight,
|
|
|
+ int? requestedWidth,
|
|
|
+ int? requestedHeight,
|
|
|
+ int? requestedMaxWidth,
|
|
|
+ int? requestedMaxHeight,
|
|
|
+ double? maxScaleRatio)
|
|
|
+ {
|
|
|
+ var (outWidth, outHeight) = GetFixedOutputSize(
|
|
|
+ videoWidth,
|
|
|
+ videoHeight,
|
|
|
+ requestedWidth,
|
|
|
+ requestedHeight,
|
|
|
+ requestedMaxWidth,
|
|
|
+ requestedMaxHeight);
|
|
|
+
|
|
|
+ if (!videoWidth.HasValue
|
|
|
+ || !videoHeight.HasValue
|
|
|
+ || !outWidth.HasValue
|
|
|
+ || !outHeight.HasValue
|
|
|
+ || !maxScaleRatio.HasValue
|
|
|
+ || (maxScaleRatio.Value < 1.0f))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var minScaleRatio = 1.0f / maxScaleRatio;
|
|
|
+ var scaleRatioW = (double)outWidth / (double)videoWidth;
|
|
|
+ var scaleRatioH = (double)outHeight / (double)videoHeight;
|
|
|
+
|
|
|
+ if (scaleRatioW < minScaleRatio
|
|
|
+ || scaleRatioW > maxScaleRatio
|
|
|
+ || scaleRatioH < minScaleRatio
|
|
|
+ || scaleRatioH > maxScaleRatio)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
public static string GetHwScaleFilter(
|
|
|
string hwScaleSuffix,
|
|
|
string videoFormat,
|
|
@@ -2877,7 +2968,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return string.Empty;
|
|
|
}
|
|
|
|
|
|
- public static string GetCustomSwScaleFilter(
|
|
|
+ public static string GetGraphicalSubPreProcessFilters(
|
|
|
int? videoWidth,
|
|
|
int? videoHeight,
|
|
|
int? requestedWidth,
|
|
@@ -2897,7 +2988,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
return string.Format(
|
|
|
CultureInfo.InvariantCulture,
|
|
|
- "scale=s={0}x{1}:flags=fast_bilinear",
|
|
|
+ @"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);
|
|
|
}
|
|
@@ -2913,7 +3004,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
int? requestedHeight,
|
|
|
int? requestedMaxWidth,
|
|
|
int? requestedMaxHeight,
|
|
|
- int? framerate)
|
|
|
+ float? framerate)
|
|
|
{
|
|
|
var reqTicks = state.BaseRequest.StartTimeTicks ?? 0;
|
|
|
var startTime = TimeSpan.FromTicks(reqTicks).ToString(@"hh\\\:mm\\\:ss\\\.fff", CultureInfo.InvariantCulture);
|
|
@@ -2932,7 +3023,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
"alphasrc=s={0}x{1}:r={2}:start='{3}'",
|
|
|
outWidth.Value,
|
|
|
outHeight.Value,
|
|
|
- framerate ?? 10,
|
|
|
+ framerate ?? 25,
|
|
|
reqTicks > 0 ? startTime : 0);
|
|
|
}
|
|
|
|
|
@@ -3340,9 +3431,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
}
|
|
|
else if (hasGraphicalSubs)
|
|
|
{
|
|
|
- // [0:s]scale=s=1280x720
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
}
|
|
|
|
|
@@ -3504,15 +3594,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- // scale=s=1280x720,format=yuva420p,hwupload
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=yuva420p");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
+ var framerate = state.VideoStream?.RealFrameRate;
|
|
|
+ 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, hasAssSubs ? 10 : 5);
|
|
|
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
|
|
|
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=yuva420p");
|
|
@@ -3527,8 +3619,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
}
|
|
|
}
|
|
@@ -3702,15 +3794,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- // scale=s=1280x720,format=yuva420p,hwupload
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=yuva420p");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
+ var framerate = state.VideoStream?.RealFrameRate;
|
|
|
+ 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, hasAssSubs ? 10 : 5);
|
|
|
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, subFramerate);
|
|
|
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=yuva420p");
|
|
@@ -3727,8 +3821,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
}
|
|
|
}
|
|
@@ -3938,16 +4032,18 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- // scale,format=bgra,hwupload
|
|
|
- // overlay_qsv can handle overlay scaling,
|
|
|
- // add a dummy scale filter to pair with -canvas_size.
|
|
|
- subFilters.Add("scale=flags=fast_bilinear");
|
|
|
+ // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=bgra");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
+ var framerate = state.VideoStream?.RealFrameRate;
|
|
|
+ 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, hasAssSubs ? 10 : 5);
|
|
|
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, subFramerate);
|
|
|
var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=bgra");
|
|
@@ -3973,8 +4069,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
}
|
|
|
}
|
|
@@ -4158,12 +4254,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- subFilters.Add("scale=flags=fast_bilinear");
|
|
|
+ // overlay_qsv can handle overlay scaling, setup a smaller height to reduce transfer overhead
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=bgra");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, hasAssSubs ? 10 : 5);
|
|
|
+ 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 subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=bgra");
|
|
@@ -4189,8 +4290,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
}
|
|
|
}
|
|
@@ -4425,12 +4526,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- subFilters.Add("scale=flags=fast_bilinear");
|
|
|
+ // overlay_vaapi can handle overlay scaling, setup a smaller height to reduce transfer overhead
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, 1080);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=bgra");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, 1080, hasAssSubs ? 10 : 5);
|
|
|
+ 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 subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=bgra");
|
|
@@ -4454,8 +4560,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
|
|
|
if (isVaapiEncoder)
|
|
@@ -4599,14 +4705,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- // scale=s=1280x720,format=bgra,hwupload
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
subFilters.Add("format=bgra");
|
|
|
}
|
|
|
else if (hasTextSubs)
|
|
|
{
|
|
|
- var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, hasAssSubs ? 10 : 5);
|
|
|
+ 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 subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
subFilters.Add(alphaSrcFilter);
|
|
|
subFilters.Add("format=bgra");
|
|
@@ -4815,8 +4923,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
if (hasGraphicalSubs)
|
|
|
{
|
|
|
- var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
- subFilters.Add(subSwScaleFilter);
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
|
|
|
if (isVaapiEncoder)
|
|
@@ -4898,6 +5006,237 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return (newfilters, swFilterChain.SubFilters, swFilterChain.OverlayFilters);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the parameter of Rockchip RKMPP/RKRGA filter chain.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="state">Encoding state.</param>
|
|
|
+ /// <param name="options">Encoding options.</param>
|
|
|
+ /// <param name="vidEncoder">Video encoder to use.</param>
|
|
|
+ /// <returns>The tuple contains three lists: main, sub and overlay filters.</returns>
|
|
|
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetRkmppVidFilterChain(
|
|
|
+ EncodingJobInfo state,
|
|
|
+ EncodingOptions options,
|
|
|
+ string vidEncoder)
|
|
|
+ {
|
|
|
+ if (!string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return (null, null, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ var isLinux = OperatingSystem.IsLinux();
|
|
|
+ var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
|
|
|
+ var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
|
|
|
+ var isSwEncoder = !vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
|
|
|
+ var isRkmppOclSupported = isLinux && IsRkmppFullSupported() && IsOpenclFullSupported();
|
|
|
+
|
|
|
+ if ((isSwDecoder && isSwEncoder)
|
|
|
+ || !isRkmppOclSupported
|
|
|
+ || !_mediaEncoder.SupportsFilter("alphasrc"))
|
|
|
+ {
|
|
|
+ return GetSwVidFilterChain(state, options, vidEncoder);
|
|
|
+ }
|
|
|
+
|
|
|
+ // prefered rkmpp + rkrga + opencl filters pipeline
|
|
|
+ if (isRkmppOclSupported)
|
|
|
+ {
|
|
|
+ return GetRkmppVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (null, null, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetRkmppVidFiltersPrefered(
|
|
|
+ EncodingJobInfo state,
|
|
|
+ EncodingOptions options,
|
|
|
+ string vidDecoder,
|
|
|
+ string vidEncoder)
|
|
|
+ {
|
|
|
+ var inW = state.VideoStream?.Width;
|
|
|
+ var inH = state.VideoStream?.Height;
|
|
|
+ var reqW = state.BaseRequest.Width;
|
|
|
+ var reqH = state.BaseRequest.Height;
|
|
|
+ var reqMaxW = state.BaseRequest.MaxWidth;
|
|
|
+ var reqMaxH = state.BaseRequest.MaxHeight;
|
|
|
+ var threeDFormat = state.MediaSource.Video3DFormat;
|
|
|
+
|
|
|
+ var isRkmppDecoder = vidDecoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
|
|
|
+ var isRkmppEncoder = vidEncoder.Contains("rkmpp", StringComparison.OrdinalIgnoreCase);
|
|
|
+ var isSwDecoder = !isRkmppDecoder;
|
|
|
+ var isSwEncoder = !isRkmppEncoder;
|
|
|
+ var isDrmInDrmOut = isRkmppDecoder && isRkmppEncoder;
|
|
|
+
|
|
|
+ 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 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));
|
|
|
+
|
|
|
+ /* Make main filters for video stream */
|
|
|
+ var mainFilters = new List<string>();
|
|
|
+
|
|
|
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, doOclTonemap));
|
|
|
+
|
|
|
+ if (isSwDecoder)
|
|
|
+ {
|
|
|
+ // INPUT sw surface(memory)
|
|
|
+ // sw deint
|
|
|
+ if (doDeintH2645)
|
|
|
+ {
|
|
|
+ var swDeintFilter = GetSwDeinterlaceFilter(state, options);
|
|
|
+ mainFilters.Add(swDeintFilter);
|
|
|
+ }
|
|
|
+
|
|
|
+ var outFormat = doOclTonemap ? "yuv420p10le" : (hasGraphicalSubs ? "yuv420p" : "nv12");
|
|
|
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ if (!string.IsNullOrEmpty(swScaleFilter))
|
|
|
+ {
|
|
|
+ swScaleFilter += ":flags=fast_bilinear";
|
|
|
+ }
|
|
|
+
|
|
|
+ // sw scale
|
|
|
+ mainFilters.Add(swScaleFilter);
|
|
|
+ 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=opencl");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (isRkmppDecoder)
|
|
|
+ {
|
|
|
+ // INPUT rkmpp/drm surface(gem/dma-heap)
|
|
|
+
|
|
|
+ var isFullAfbcPipeline = isDrmInDrmOut && !doOclTonemap;
|
|
|
+ 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);
|
|
|
+
|
|
|
+ if (!hasSubs
|
|
|
+ || !isFullAfbcPipeline
|
|
|
+ || !string.IsNullOrEmpty(hwScaleFilter2))
|
|
|
+ {
|
|
|
+ // try enabling AFBC to save DDR bandwidth
|
|
|
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isFullAfbcPipeline)
|
|
|
+ {
|
|
|
+ hwScaleFilter += ":afbc=1";
|
|
|
+ }
|
|
|
+
|
|
|
+ // hw scale
|
|
|
+ mainFilters.Add(hwScaleFilter);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (doOclTonemap && isRkmppDecoder)
|
|
|
+ {
|
|
|
+ // map from rkmpp/drm to opencl via drm-opencl interop.
|
|
|
+ mainFilters.Add("hwmap=derive_device=opencl:mode=read");
|
|
|
+ }
|
|
|
+
|
|
|
+ // ocl tonemap
|
|
|
+ if (doOclTonemap)
|
|
|
+ {
|
|
|
+ var tonemapFilter = GetHwTonemapFilter(options, "opencl", "nv12");
|
|
|
+ // enable tradeoffs for performance
|
|
|
+ if (!string.IsNullOrEmpty(tonemapFilter))
|
|
|
+ {
|
|
|
+ tonemapFilter += ":tradeoff=1";
|
|
|
+ }
|
|
|
+
|
|
|
+ mainFilters.Add(tonemapFilter);
|
|
|
+ }
|
|
|
+
|
|
|
+ var memoryOutput = false;
|
|
|
+ var isUploadForOclTonemap = isSwDecoder && doOclTonemap;
|
|
|
+ if ((isRkmppDecoder && isSwEncoder) || isUploadForOclTonemap)
|
|
|
+ {
|
|
|
+ memoryOutput = true;
|
|
|
+
|
|
|
+ // OUTPUT nv12 surface(memory)
|
|
|
+ mainFilters.Add("hwdownload");
|
|
|
+ mainFilters.Add("format=nv12");
|
|
|
+ }
|
|
|
+
|
|
|
+ // OUTPUT nv12 surface(memory)
|
|
|
+ if (isSwDecoder && isRkmppEncoder)
|
|
|
+ {
|
|
|
+ memoryOutput = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memoryOutput)
|
|
|
+ {
|
|
|
+ // text subtitles
|
|
|
+ if (hasTextSubs)
|
|
|
+ {
|
|
|
+ var textSubtitlesFilter = GetTextSubtitlesFilter(state, false, false);
|
|
|
+ mainFilters.Add(textSubtitlesFilter);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isDrmInDrmOut)
|
|
|
+ {
|
|
|
+ if (doOclTonemap)
|
|
|
+ {
|
|
|
+ // 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("format=drm_prime");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Make sub and overlay filters for subtitle stream */
|
|
|
+ var subFilters = new List<string>();
|
|
|
+ var overlayFilters = new List<string>();
|
|
|
+ if (isDrmInDrmOut)
|
|
|
+ {
|
|
|
+ if (hasSubs)
|
|
|
+ {
|
|
|
+ if (hasGraphicalSubs)
|
|
|
+ {
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
+ subFilters.Add("format=bgra");
|
|
|
+ }
|
|
|
+ else if (hasTextSubs)
|
|
|
+ {
|
|
|
+ var framerate = state.VideoStream?.RealFrameRate;
|
|
|
+ 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 subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
|
|
|
+ subFilters.Add(alphaSrcFilter);
|
|
|
+ subFilters.Add("format=bgra");
|
|
|
+ subFilters.Add(subTextSubtitlesFilter);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (memoryOutput)
|
|
|
+ {
|
|
|
+ if (hasGraphicalSubs)
|
|
|
+ {
|
|
|
+ var subPreProcFilters = GetGraphicalSubPreProcessFilters(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
|
|
+ subFilters.Add(subPreProcFilters);
|
|
|
+ overlayFilters.Add("overlay=eof_action=pass:repeatlast=0");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (mainFilters, subFilters, overlayFilters);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the parameter of video processing filters.
|
|
|
/// </summary>
|
|
@@ -4944,6 +5283,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
(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);
|
|
@@ -5075,18 +5418,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|
|
|
if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoStream.PixelFormat, "yuv422p", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
return 8;
|
|
|
}
|
|
|
|
|
|
if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoStream.PixelFormat, "yuv422p10le", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
return 10;
|
|
|
}
|
|
|
|
|
|
if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoStream.PixelFormat, "yuv422p12le", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
return 12;
|
|
@@ -5139,7 +5485,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
|| string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)
|
|
|
|| string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase)))
|
|
|
{
|
|
|
- return null;
|
|
|
+ // One exception is that RKMPP decoder can handle H.264 High 10.
|
|
|
+ if (!(string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)
|
|
|
+ && string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase)))
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
|
|
@@ -5166,6 +5517,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
{
|
|
|
return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth);
|
|
|
}
|
|
|
+
|
|
|
+ if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetRkmppVidDecoder(state, options, videoStream, bitDepth);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
var whichCodec = videoStream.Codec;
|
|
@@ -5231,6 +5587,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ if (string.Equals(decoderSuffix, "rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
return isCodecAvailable ? (" -c:v " + decoderName) : null;
|
|
|
}
|
|
|
|
|
@@ -5253,6 +5614,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported();
|
|
|
var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.SupportsHwaccel("qsv");
|
|
|
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox");
|
|
|
+ var isRkmppSupported = isLinux && IsRkmppFullSupported();
|
|
|
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
|
var ffmpegVersion = _mediaEncoder.EncoderVersion;
|
|
@@ -5355,6 +5717,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty);
|
|
|
}
|
|
|
|
|
|
+ // Rockchip rkmpp
|
|
|
+ if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase)
|
|
|
+ && isRkmppSupported
|
|
|
+ && isCodecAvailable)
|
|
|
+ {
|
|
|
+ return " -hwaccel rkmpp" + (outputHwSurface ? " -hwaccel_output_format drm_prime" : string.Empty);
|
|
|
+ }
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
|
|
@@ -5661,6 +6031,102 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ public string GetRkmppVidDecoder(EncodingJobInfo state, EncodingOptions options, MediaStream videoStream, int bitDepth)
|
|
|
+ {
|
|
|
+ var isLinux = OperatingSystem.IsLinux();
|
|
|
+
|
|
|
+ if (!isLinux
|
|
|
+ || !string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var inW = state.VideoStream?.Width;
|
|
|
+ var inH = state.VideoStream?.Height;
|
|
|
+ var reqW = state.BaseRequest.Width;
|
|
|
+ var reqH = state.BaseRequest.Height;
|
|
|
+ var reqMaxW = state.BaseRequest.MaxWidth;
|
|
|
+ var reqMaxH = state.BaseRequest.MaxHeight;
|
|
|
+
|
|
|
+ // rkrga RGA2e supports range from 1/16 to 16
|
|
|
+ if (!IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 16.0f))
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var isRkmppOclSupported = IsRkmppFullSupported() && IsOpenclFullSupported();
|
|
|
+ var hwSurface = isRkmppOclSupported
|
|
|
+ && _mediaEncoder.SupportsFilter("alphasrc");
|
|
|
+
|
|
|
+ // rkrga RGA3 supports range from 1/8 to 8
|
|
|
+ var isAfbcSupported = hwSurface && IsScaleRatioSupported(inW, inH, reqW, reqH, reqMaxW, reqMaxH, 8.0f);
|
|
|
+
|
|
|
+ // TODO: add more 8/10bit and 4:2:2 formats for Rkmpp after finishing the ffcheck tool
|
|
|
+ var is8bitSwFormatsRkmpp = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
|
|
|
+ var is10bitSwFormatsRkmpp = string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase);
|
|
|
+ var is8_10bitSwFormatsRkmpp = is8bitSwFormatsRkmpp || is10bitSwFormatsRkmpp;
|
|
|
+
|
|
|
+ // nv15 and nv20 are bit-stream only formats
|
|
|
+ if (is10bitSwFormatsRkmpp && !hwSurface)
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is8bitSwFormatsRkmpp)
|
|
|
+ {
|
|
|
+ if (string.Equals(videoStream.Codec, "mpeg1video", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetHwaccelType(state, options, "mpeg1video", bitDepth, hwSurface);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "mpeg2video", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetHwaccelType(state, options, "mpeg2video", bitDepth, hwSurface);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetHwaccelType(state, options, "mpeg4", bitDepth, hwSurface);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "vp8", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetHwaccelType(state, options, "vp8", bitDepth, hwSurface);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is8_10bitSwFormatsRkmpp)
|
|
|
+ {
|
|
|
+ if (string.Equals(videoStream.Codec, "avc", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoStream.Codec, "h264", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ var accelType = GetHwaccelType(state, options, "h264", bitDepth, hwSurface);
|
|
|
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase)
|
|
|
+ || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ var accelType = GetHwaccelType(state, options, "hevc", bitDepth, hwSurface);
|
|
|
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ var accelType = GetHwaccelType(state, options, "vp9", bitDepth, hwSurface);
|
|
|
+ return accelType + ((!string.IsNullOrEmpty(accelType) && isAfbcSupported) ? " -afbc rga" : string.Empty);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.Equals(videoStream.Codec, "av1", StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetHwaccelType(state, options, "av1", bitDepth, hwSurface);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the number of threads.
|
|
|
/// </summary>
|