@ -163,7 +163,8 @@ namespace MediaBrowser.Controller.MediaEncoding
private bool IsVaapiFullSupported ( )
{
return _mediaEncoder . SupportsHwaccel ( "vaapi" )
return _mediaEncoder . SupportsHwaccel ( "drm" )
& & _mediaEncoder . SupportsHwaccel ( "vaapi" )
& & _mediaEncoder . SupportsFilter ( "scale_vaapi" )
& & _mediaEncoder . SupportsFilter ( "deinterlace_vaapi" )
& & _mediaEncoder . SupportsFilter ( "tonemap_vaapi" )
@ -713,28 +714,43 @@ namespace MediaBrowser.Controller.MediaEncoding
options ) ;
}
private string GetVaapiDeviceArgs ( string renderNodePath , string driver , string kernelDriver , string alias )
private string GetVaapiDeviceArgs ( string renderNodePath , string driver , string kernelDriver , string srcDeviceAlias , string alias )
{
alias ? ? = VaapiAlias ;
renderNodePath = renderNodePath ? ? "/dev/dri/renderD128" ;
var options = string . IsNullOrEmpty ( driver )
? renderNodePath
: ",driver=" + driver + ( string . IsNullOrEmpty ( kernelDriver ) ? string . Empty : ",kernel_driver=" + kernelDriver ) ;
var driverOpts = string . IsNullOrEmpty ( driver )
? ":" + renderNodePath
: ":,driver=" + driver + ( string . IsNullOrEmpty ( kernelDriver ) ? string . Empty : ",kernel_driver=" + kernelDriver ) ;
var options = string . IsNullOrEmpty ( srcDeviceAlias )
? driverOpts
: "@" + srcDeviceAlias ;
return string . Format (
CultureInfo . InvariantCulture ,
" -init_hw_device vaapi={0} : {1}",
" -init_hw_device vaapi={0} {1}",
alias ,
options ) ;
}
private string GetDrmDeviceArgs ( string renderNodePath , string alias )
{
alias ? ? = DrmAlias ;
renderNodePath = renderNodePath ? ? "/dev/dri/renderD128" ;
return string . Format (
CultureInfo . InvariantCulture ,
" -init_hw_device drm={0}:{1}" ,
alias ,
renderNodePath ) ;
}
private string GetQsvDeviceArgs ( string alias )
{
var arg = " -init_hw_device qsv=" + ( alias ? ? QsvAlias ) ;
if ( OperatingSystem . IsLinux ( ) )
{
// derive qsv from vaapi device
return GetVaapiDeviceArgs ( null , "iHD" , "i915" , VaapiAlias ) + arg + "@" + VaapiAlias ;
return GetVaapiDeviceArgs ( null , "iHD" , "i915" , null , VaapiAlias ) + arg + "@" + VaapiAlias ;
}
if ( OperatingSystem . IsWindows ( ) )
@ -828,21 +844,17 @@ namespace MediaBrowser.Controller.MediaEncoding
if ( _mediaEncoder . IsVaapiDeviceInteliHD )
{
args . Append ( GetVaapiDeviceArgs ( null , "iHD" , null , VaapiAlias ) ) ;
args . Append ( GetVaapiDeviceArgs ( null , "iHD" , null , null , VaapiAlias ) ) ;
}
else if ( _mediaEncoder . IsVaapiDeviceInteli965 )
{
// Only override i965 since it has lower priority than iHD in libva lookup.
Environment . SetEnvironmentVariable ( "LIBVA_DRIVER_NAME" , "i965" ) ;
Environment . SetEnvironmentVariable ( "LIBVA_DRIVER_NAME_JELLYFIN" , "i965" ) ;
args . Append ( GetVaapiDeviceArgs ( null , "i965" , null , VaapiAlias ) ) ;
}
else
{
args . Append ( GetVaapiDeviceArgs ( options . VaapiDevice , null , null , VaapiAlias ) ) ;
args . Append ( GetVaapiDeviceArgs ( null , "i965" , null , null , VaapiAlias ) ) ;
}
var filterDevArgs = GetFilterHwDeviceArgs ( VaapiAlias ) ;
var filterDevArgs = string . Empty ;
var doOclTonemap = isHwTonemapAvailable & & IsOpenclFullSupported ( ) ;
if ( _mediaEncoder . IsVaapiDeviceInteliHD | | _mediaEncoder . IsVaapiDeviceInteli965 )
@ -859,15 +871,24 @@ namespace MediaBrowser.Controller.MediaEncoding
& & _mediaEncoder . IsVaapiDeviceSupportVulkanFmtModifier
& & Environment . OSVersion . Version > = _minKernelVersionAmdVkFmtModifier )
{
args . Append ( GetDrmDeviceArgs ( options . VaapiDevice , DrmAlias ) ) ;
args . Append ( GetVaapiDeviceArgs ( null , null , null , DrmAlias , VaapiAlias ) ) ;
args . Append ( GetVulkanDeviceArgs ( 0 , null , DrmAlias , VulkanAlias ) ) ;
// libplacebo wants an explicitly set vulkan filter device.
args . Append ( GetVulkanDeviceArgs ( 0 , null , VaapiAlias , VulkanAlias ) ) ;
filterDevArgs = GetFilterHwDeviceArgs ( VulkanAlias ) ;
}
else if ( doOclTonemap )
else
{
// ROCm/ROCr OpenCL runtime
args . Append ( GetOpenclDeviceArgs ( 0 , "Advanced Micro Devices" , null , OpenclAlias ) ) ;
filterDevArgs = GetFilterHwDeviceArgs ( OpenclAlias ) ;
args . Append ( GetVaapiDeviceArgs ( options . VaapiDevice , null , null , null , VaapiAlias ) ) ;
filterDevArgs = GetFilterHwDeviceArgs ( VaapiAlias ) ;
if ( doOclTonemap )
{
// ROCm/ROCr OpenCL runtime
args . Append ( GetOpenclDeviceArgs ( 0 , "Advanced Micro Devices" , null , OpenclAlias ) ) ;
filterDevArgs = GetFilterHwDeviceArgs ( OpenclAlias ) ;
}
}
}
else if ( doOclTonemap )
@ -3011,6 +3032,75 @@ namespace MediaBrowser.Controller.MediaEncoding
options . TonemappingRange ) ;
}
public string GetLibplaceboFilter (
EncodingOptions options ,
string videoFormat ,
bool doTonemap ,
int? videoWidth ,
int? videoHeight ,
int? requestedWidth ,
int? requestedHeight ,
int? requestedMaxWidth ,
int? requestedMaxHeight )
{
var ( outWidth , outHeight ) = GetFixedOutputSize (
videoWidth ,
videoHeight ,
requestedWidth ,
requestedHeight ,
requestedMaxWidth ,
requestedMaxHeight ) ;
var isFormatFixed = ! string . IsNullOrEmpty ( videoFormat ) ;
var isSizeFixed = ! videoWidth . HasValue
| | outWidth . Value ! = videoWidth . Value
| | ! videoHeight . HasValue
| | outHeight . Value ! = videoHeight . Value ;
var sizeArg = isSizeFixed ? ( ":w=" + outWidth . Value + ":h=" + outHeight . Value ) : string . Empty ;
var formatArg = isFormatFixed ? ( ":format=" + videoFormat ) : string . Empty ;
var tonemapArg = string . Empty ;
if ( doTonemap )
{
var algorithm = options . TonemappingAlgorithm ;
var mode = options . TonemappingMode ;
var range = options . TonemappingRange ;
if ( string . Equals ( algorithm , "bt2390" , StringComparison . OrdinalIgnoreCase ) )
{
algorithm = "bt.2390" ;
}
else if ( string . Equals ( algorithm , "none" , StringComparison . OrdinalIgnoreCase ) )
{
algorithm = "clip" ;
}
tonemapArg = ":tonemapping=" + algorithm ;
if ( string . Equals ( mode , "max" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( mode , "rgb" , StringComparison . OrdinalIgnoreCase ) )
{
tonemapArg + = ":tonemapping_mode=" + mode ;
}
tonemapArg + = ":peak_detect=0:color_primaries=bt709:color_trc=bt709:colorspace=bt709" ;
if ( string . Equals ( range , "tv" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( range , "pc" , StringComparison . OrdinalIgnoreCase ) )
{
tonemapArg + = ":range=" + range ;
}
}
return string . Format (
CultureInfo . InvariantCulture ,
"libplacebo=upscaler=none:downscaler=none{0}{1}{2}" ,
sizeArg ,
formatArg ,
tonemapArg ) ;
}
/// <summary>
/// Gets the parameter of software filter chain.
/// </summary>
@ -4240,7 +4330,6 @@ namespace MediaBrowser.Controller.MediaEncoding
var isVaapiEncoder = vidEncoder . Contains ( "vaapi" , StringComparison . OrdinalIgnoreCase ) ;
var isSwDecoder = string . IsNullOrEmpty ( vidDecoder ) ;
var isSwEncoder = ! isVaapiEncoder ;
var isVaInVaOut = isVaapiDecoder & & isVaapiEncoder ;
var doDeintH264 = state . DeInterlace ( "h264" , true ) | | state . DeInterlace ( "avc" , true ) ;
var doDeintHevc = state . DeInterlace ( "h265" , true ) | | state . DeInterlace ( "hevc" , true ) ;
@ -4269,99 +4358,81 @@ namespace MediaBrowser.Controller.MediaEncoding
mainFilters . Add ( swDeintFilter ) ;
}
var outFormat = doVkTonemap ? "yuv420p10le" : "nv12" ;
var swScaleFilter = GetSwScaleFilter ( state , options , vidEncoder , inW , inH , threeDFormat , reqW , reqH , reqMaxW , reqMaxH ) ;
// sw scale
mainFilters . Add ( swScaleFilter ) ;
mainFilters . Add ( "format=" + outFormat ) ;
// keep video at memory except vk tonemap,
// since the overhead caused by hwupload >>> using sw filter.
// sw => hw
if ( doVkTonemap )
if ( doVkTonemap | | hasSubs )
{
mainFilters . Add ( "hwupload=derive_device=vaapi" ) ;
mainFilters . Add ( "format=vaapi" ) ;
mainFilters . Add ( "hwmap=derive_device=vulkan" ) ;
// sw => hw
mainFilters . Add ( "hwupload=derive_device=vulkan" ) ;
mainFilters . Add ( "format=vulkan" ) ;
}
else
{
// sw scale
var swScaleFilter = GetSwScaleFilter ( state , options , vidEncoder , inW , inH , threeDFormat , reqW , reqH , reqMaxW , reqMaxH ) ;
mainFilters . Add ( swScaleFilter ) ;
mainFilters . Add ( "format=nv12" ) ;
}
}
else if ( isVaapiDecoder )
{
// INPUT vaapi surface(vram)
// hw deint
if ( doDeintH2645 )
if ( doVkTonemap | | hasSubs )
{
var deintFilter = GetHwDeinterlaceFilter ( state , options , "vaapi" ) ;
mainFilters . Add ( deintFilter ) ;
// map from vaapi to vulkan/drm via interop (Vega/gfx9+).
mainFilters . Add ( "hwmap=derive_device=vulkan" ) ;
mainFilters . Add ( "format=vulkan" ) ;
}
var outFormat = doVkTonemap ? string . Empty : ( hasSubs & & isVaInVaOut ? "bgra" : "nv12" ) ;
var hwScaleFilter = GetHwScaleFilter ( "vaapi" , outFormat , inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
// allocate extra pool sizes for overlay_vulkan
if ( ! string . IsNullOrEmpty ( hwScaleFilter ) & & isVaInVaOut & & hasSubs )
else
{
hwScaleFilter + = ":extra_hw_frames=32" ;
}
// hw scale
mainFilters . Add ( hwScale Filter) ;
}
// hw deint
if ( doDeintH2645 )
{
var deintFilter = GetHwDeinterlaceFilter ( state , options , "vaapi" ) ;
mainFilters . Add ( deint Filter) ;
}
if ( ( isVaapiDecoder & & doVkTonemap ) | | ( isVaInVaOut & & ( doVkTonemap | | hasSubs ) ) )
{
// map from vaapi to vulkan via vaapi-vulkan interop (Vega/gfx9+).
mainFilters . Add ( "hwmap=derive_device=vulkan" ) ;
mainFilters . Add ( "format=vulkan" ) ;
// hw scale
var hwScaleFilter = GetHwScaleFilter ( "vaapi" , "nv12" , inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
mainFilters . Add ( hwScaleFilter ) ;
}
}
// vk tonemap
if ( doVkTonemap )
// vk libplacebo
if ( doVkTonemap | | hasSubs )
{
var outFormat = isVaInVaOut & & hasSubs ? "bgra" : "nv12" ;
var tonemapFilter = GetHwTonemapFilter ( options , "vulkan" , outFormat ) ;
mainFilters . Add ( tonemapFilter ) ;
var libplaceboFilter = GetLibplaceboFilter ( options , "bgra" , doVkTonemap , inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
mainFilters . Add ( libplaceboFilter ) ;
}
if ( doVkTonemap & & isVaInVaOut & & ! hasSubs )
if ( doVkTonemap & & ! hasSubs )
{
// OUTPUT vaapi(nv12/bgra) surface(vram)
// reverse-mapping via vaapi-vulkan interop.
mainFilters . Add ( "hwmap=derive_device=vaapi:reverse=1" ) ;
// OUTPUT vaapi(nv12) surface(vram)
// map from vulkan/drm to vaapi via interop (Vega/gfx9+).
mainFilters . Add ( "hwmap=derive_device=drm" ) ;
mainFilters . Add ( "format=drm_prime" ) ;
mainFilters . Add ( "hwmap=derive_device=vaapi" ) ;
mainFilters . Add ( "format=vaapi" ) ;
}
var memoryOutput = false ;
var isUploadForVkTonemap = isSwDecoder & & doVkTonemap ;
if ( ( isVaapiDecoder & & isSwEncoder ) | | isUploadForVkTonemap )
{
memoryOutput = true ;
// clear the surf->meta_offset and output nv12
mainFilters . Add ( "scale_vaapi=format=nv12" ) ;
// OUTPUT nv12 surface(memory)
mainFilters . Add ( "hwdownload" ) ;
mainFilters . Add ( "format=nv12" ) ;
}
// OUTPUT nv12 surface(memory)
if ( isSwDecoder & & isVaapiEncoder )
{
memoryOutput = true ;
// hw deint
if ( doDeintH2645 )
{
var deintFilter = GetHwDeinterlaceFilter ( state , options , "vaapi" ) ;
mainFilters . Add ( deintFilter ) ;
}
}
if ( memoryOutput )
if ( ! hasSubs )
{
// text subtitles
if ( hasTextSubs )
// OUTPUT nv12 surface(memory)
if ( isSwEncoder & & ( doVkTonemap | | isVaapiDecoder ) )
{
var textSubtitlesFilter = GetTextSubtitlesFilter ( state , false , false ) ;
mainFilters . Add ( textSubtitlesFilter ) ;
mainFilters . Add ( "hwdownload" ) ;
mainFilters . Add ( "format=nv12" ) ;
}
}
if ( memoryOutput & & isVaapiEncoder )
{
if ( ! hasGraphicalSubs )
if ( isSwDecoder & & isVaapiEncoder & & ! doVkTonemap )
{
mainFilters . Add ( "hwupload_vaapi" ) ;
}
@ -4370,55 +4441,53 @@ namespace MediaBrowser.Controller.MediaEncoding
/* Make sub and overlay filters for subtitle stream */
var subFilters = new List < string > ( ) ;
var overlayFilters = new List < string > ( ) ;
if ( isVaInVaOut )
if ( hasSubs )
{
if ( has Subs)
if ( has Graphical Subs)
{
if ( hasGraphicalSubs )
{
// scale=s=1280x720,format=bgra,hwupload
var subSwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( subSwScaleFilter ) ;
subFilters . Add ( "format=bgra" ) ;
}
else if ( hasTextSubs )
{
var alphaSrcFilter = GetAlphaSrcFilter ( state , inW , inH , reqW , reqH , reqMaxW , reqMaxH , hasAssSubs ? 10 : 5 ) ;
var subTextSubtitlesFilter = GetTextSubtitlesFilter ( state , true , true ) ;
subFilters . Add ( alphaSrcFilter ) ;
subFilters . Add ( "format=bgra" ) ;
subFilters . Add ( subTextSubtitlesFilter ) ;
}
// prefer vaapi hwupload to vulkan hwupload,
// Mesa RADV does not support a dedicated transfer queue.
subFilters . Add ( "hwupload=derive_device=vaapi" ) ;
subFilters . Add ( "format=vaapi" ) ;
subFilters . Add ( "hwmap=derive_device=vulkan" ) ;
subFilters . Add ( "format=vulkan" ) ;
// scale=s=1280x720,format=bgra,hwupload
var subSwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( subSwScaleFilter ) ;
subFilters . Add ( "format=bgra" ) ;
}
else if ( hasTextSubs )
{
var alphaSrcFilter = GetAlphaSrcFilter ( state , inW , inH , reqW , reqH , reqMaxW , reqMaxH , hasAssSubs ? 10 : 5 ) ;
var subTextSubtitlesFilter = GetTextSubtitlesFilter ( state , true , true ) ;
subFilters . Add ( alphaSrcFilter ) ;
subFilters . Add ( "format=bgra" ) ;
subFilters . Add ( subTextSubtitlesFilter ) ;
}
overlayFilters . Add ( "overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0" ) ;
subFilters . Add ( "hwupload=derive_device=vulkan" ) ;
subFilters . Add ( "format=vulkan" ) ;
// TODO: figure out why libplacebo can sync without vaSyncSurface VPP support in radeonsi.
overlayFilters . Add ( "libplacebo=format=nv12:apply_filmgrain=0:apply_dolbyvision=0:upscaler=none:downscaler=none:dithering=none" ) ;
overlayFilters . Add ( "overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0" ) ;
// OUTPUT vaapi(nv12/bgra) surface(vram)
// reverse-mapping via vaapi-vulkan interop.
overlayFilters . Add ( "hwmap=derive_device=vaapi:reverse=1" ) ;
overlayFilters . Add ( "format=vaapi" ) ;
if ( isSwEncoder )
{
// OUTPUT nv12 surface(memory)
overlayFilters . Add ( "scale_vulkan=format=nv12" ) ;
overlayFilters . Add ( "hwdownload" ) ;
overlayFilters . Add ( "format=nv12" ) ;
}
}
else if ( memoryOutput )
{
if ( hasGraphicalSubs )
else if ( isVaapiEncoder )
{
var subSwScaleFilter = GetCustomSwScaleFilter ( inW , inH , reqW , reqH , reqMaxW , reqMaxH ) ;
subFilters . Add ( subSwScaleFilter ) ;
overlayFilters . Add ( "overlay=eof_action=pass:shortest=1:repeatlast=0" ) ;
// OUTPUT vaapi(nv12) surface(vram)
// map from vulkan/drm to vaapi via interop (Vega/gfx9+).
overlayFilters . Add ( "hwmap=derive_device=drm" ) ;
overlayFilters . Add ( "format=drm_prime" ) ;
overlayFilters . Add ( "hwmap=derive_device=vaapi" ) ;
overlayFilters . Add ( "format=vaapi" ) ;
if ( isVaapiEncoder )
// clear the surf->meta_offset and output nv12
overlayFilters . Add ( "scale_vaapi=format=nv12" ) ;
// hw deint
if ( doDeintH2645 )
{
overlayFilters . Add ( "hwupload_vaapi" ) ;
var deintFilter = GetHwDeinterlaceFilter ( state , options , "vaapi" ) ;
overlayFilters . Add ( deintFilter ) ;
}
}
}