@ -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 Get CustomSwScaleFilter (
public static string Get GraphicalSubPreProcessFilters (
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 ,
in t? framerate )
floa t? 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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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 sub SwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub SwScaleFilter ) ;
var sub PreProcFilters = GetGraphicalSubPreProcessFilters ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( sub PreProcFilters ) ;
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>