Add full HWA transcoding pipeline for RKMPP

Signed-off-by: nyanmisaka <nst799610810@gmail.com>
This commit is contained in:
nyanmisaka 2024-01-29 19:46:17 +08:00
parent 52da00c3c7
commit e62dab627e

View File

@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private const string VaapiAlias = "va"; private const string VaapiAlias = "va";
private const string D3d11vaAlias = "dx11"; private const string D3d11vaAlias = "dx11";
private const string VideotoolboxAlias = "vt"; private const string VideotoolboxAlias = "vt";
private const string RkmppAlias = "rk";
private const string OpenclAlias = "ocl"; private const string OpenclAlias = "ocl";
private const string CudaAlias = "cu"; private const string CudaAlias = "cu";
private const string DrmAlias = "dr"; private const string DrmAlias = "dr";
@ -161,6 +162,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{ "vaapi", hwEncoder + "_vaapi" }, { "vaapi", hwEncoder + "_vaapi" },
{ "videotoolbox", hwEncoder + "_videotoolbox" }, { "videotoolbox", hwEncoder + "_videotoolbox" },
{ "v4l2m2m", hwEncoder + "_v4l2m2m" }, { "v4l2m2m", hwEncoder + "_v4l2m2m" },
{ "rkmpp", hwEncoder + "_rkmpp" },
}; };
if (!string.IsNullOrEmpty(hwType) if (!string.IsNullOrEmpty(hwType)
@ -217,6 +219,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("hwupload_vaapi"); && _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() private bool IsOpenclFullSupported()
{ {
return _mediaEncoder.SupportsHwaccel("opencl") return _mediaEncoder.SupportsHwaccel("opencl")
@ -696,6 +706,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return codec.ToLowerInvariant(); 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) private string GetVideoToolboxDeviceArgs(string alias)
{ {
alias ??= VideotoolboxAlias; alias ??= VideotoolboxAlias;
@ -1056,6 +1074,33 @@ namespace MediaBrowser.Controller.MediaEncoding
// no videotoolbox hw filter. // no videotoolbox hw filter.
args.Append(GetVideoToolboxDeviceArgs(VideotoolboxAlias)); 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)) if (!string.IsNullOrEmpty(vidDecoder))
{ {
@ -1472,8 +1517,10 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase) if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h264_nvenc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h264_amf", 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_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc_nvenc", 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_qsv", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "av1_nvenc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "av1_nvenc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "av1_amf", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "av1_amf", StringComparison.OrdinalIgnoreCase)
@ -1913,20 +1960,22 @@ namespace MediaBrowser.Controller.MediaEncoding
profile = "constrained_baseline"; 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) if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_qsv", 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.Contains("baseline", StringComparison.OrdinalIgnoreCase))
{ {
profile = "baseline"; 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) if ((string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_qsv", 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_vaapi", StringComparison.OrdinalIgnoreCase)) || string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoEncoder, "h264_rkmpp", StringComparison.OrdinalIgnoreCase))
&& profile.Contains("high", StringComparison.OrdinalIgnoreCase)) && profile.Contains("high", StringComparison.OrdinalIgnoreCase))
{ {
profile = "high"; profile = "high";
@ -2010,6 +2059,11 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -level " + level; 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)) else if (!string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
{ {
param += " -level " + level; param += " -level " + level;
@ -2828,6 +2882,48 @@ namespace MediaBrowser.Controller.MediaEncoding
return (outputWidth, outputHeight); 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( public static string GetHwScaleFilter(
string hwScaleSuffix, string hwScaleSuffix,
string videoFormat, string videoFormat,
@ -4910,6 +5006,237 @@ namespace MediaBrowser.Controller.MediaEncoding
return (newfilters, swFilterChain.SubFilters, swFilterChain.OverlayFilters); 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> /// <summary>
/// Gets the parameter of video processing filters. /// Gets the parameter of video processing filters.
/// </summary> /// </summary>
@ -4956,6 +5283,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
(mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec); (mainFilters, subFilters, overlayFilters) = GetAppleVidFilterChain(state, options, outputVideoCodec);
} }
else if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
{
(mainFilters, subFilters, overlayFilters) = GetRkmppVidFilterChain(state, options, outputVideoCodec);
}
else else
{ {
(mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec); (mainFilters, subFilters, overlayFilters) = GetSwVidFilterChain(state, options, outputVideoCodec);
@ -5087,18 +5418,21 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase) if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase) || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv422p", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase)) || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
{ {
return 8; return 8;
} }
if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase) if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv422p10le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase)) || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
{ {
return 10; return 10;
} }
if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase) if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv422p12le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase)) || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
{ {
return 12; return 12;
@ -5151,7 +5485,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase) || string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase)
|| string.Equals(videoStream.Codec, "av1", 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)) if (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
@ -5178,6 +5517,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth); return GetVideotoolboxVidDecoder(state, options, videoStream, bitDepth);
} }
if (string.Equals(options.HardwareAccelerationType, "rkmpp", StringComparison.OrdinalIgnoreCase))
{
return GetRkmppVidDecoder(state, options, videoStream, bitDepth);
}
} }
var whichCodec = videoStream.Codec; var whichCodec = videoStream.Codec;
@ -5243,6 +5587,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return null; return null;
} }
if (string.Equals(decoderSuffix, "rkmpp", StringComparison.OrdinalIgnoreCase))
{
return null;
}
return isCodecAvailable ? (" -c:v " + decoderName) : null; return isCodecAvailable ? (" -c:v " + decoderName) : null;
} }
@ -5265,6 +5614,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported(); var isCudaSupported = (isLinux || isWindows) && IsCudaFullSupported();
var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.SupportsHwaccel("qsv"); var isQsvSupported = (isLinux || isWindows) && _mediaEncoder.SupportsHwaccel("qsv");
var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox"); var isVideotoolboxSupported = isMacOS && _mediaEncoder.SupportsHwaccel("videotoolbox");
var isRkmppSupported = isLinux && IsRkmppFullSupported();
var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase); var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparison.OrdinalIgnoreCase);
var ffmpegVersion = _mediaEncoder.EncoderVersion; var ffmpegVersion = _mediaEncoder.EncoderVersion;
@ -5367,6 +5717,14 @@ namespace MediaBrowser.Controller.MediaEncoding
return " -hwaccel videotoolbox" + (outputHwSurface ? " -hwaccel_output_format videotoolbox_vld" : string.Empty); 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; return null;
} }
@ -5673,6 +6031,102 @@ namespace MediaBrowser.Controller.MediaEncoding
return null; 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> /// <summary>
/// Gets the number of threads. /// Gets the number of threads.
/// </summary> /// </summary>