@ -451,11 +451,13 @@ namespace MediaBrowser.Controller.MediaEncoding
var arg = new StringBuilder ( ) ;
var videoDecoder = GetHardwareAcceleratedVideoDecoder ( state , encodingOptions ) ? ? string . Empty ;
var outputVideoCodec = GetVideoEncoder ( state , encodingOptions ) ? ? string . Empty ;
var isSwDecoder = string . IsNullOrEmpty ( videoDecoder ) ;
var isD3d11vaDecoder = videoDecoder . IndexOf ( "d3d11va" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isVaapiDecoder = videoDecoder . IndexOf ( "vaapi" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isVaapiEncoder = outputVideoCodec . IndexOf ( "vaapi" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isQsvDecoder = videoDecoder . IndexOf ( "qsv" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isQsvEncoder = outputVideoCodec . IndexOf ( "qsv" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isNv en cHevcDecoder = videoDecoder . IndexOf ( "hevc_cuvid" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isNv d ecHevcDecoder = videoDecoder . IndexOf ( "hevc_cuvid" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isWindows = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
var isLinux = RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) ;
var isMacOS = RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ;
@ -517,11 +519,12 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if ( state . IsVideoRequest
& & string . Equals ( encodingOptions . HardwareAccelerationType , "nvenc" , StringComparison . OrdinalIgnoreCase ) )
& & ( string . Equals ( encodingOptions . HardwareAccelerationType , "nvenc" , StringComparison . OrdinalIgnoreCase ) & & isNvdecHevcDecoder | | isSwDecoder )
| | ( string . Equals ( encodingOptions . HardwareAccelerationType , "amf" , StringComparison . OrdinalIgnoreCase ) & & isD3d11vaDecoder | | isSwDecoder ) )
{
var isColorDepth10 = IsColorDepth10 ( state ) ;
if ( is NvencHevcDecoder & & is ColorDepth10
if ( is ColorDepth10
& & _mediaEncoder . SupportsHwaccel ( "opencl" )
& & encodingOptions . EnableTonemapping
& & ! string . IsNullOrEmpty ( state . VideoStream . VideoRange )
@ -880,6 +883,19 @@ namespace MediaBrowser.Controller.MediaEncoding
param + = "-quality speed" ;
break ;
}
var videoStream = state . VideoStream ;
var isColorDepth10 = IsColorDepth10 ( state ) ;
if ( isColorDepth10
& & _mediaEncoder . SupportsHwaccel ( "opencl" )
& & encodingOptions . EnableTonemapping
& & ! string . IsNullOrEmpty ( videoStream . VideoRange )
& & videoStream . VideoRange . Contains ( "HDR" , StringComparison . OrdinalIgnoreCase ) )
{
// Enhance workload when tone mapping with AMF on some APUs
param + = " -preanalysis true" ;
}
}
else if ( string . Equals ( videoEncoder , "libvpx" , StringComparison . OrdinalIgnoreCase ) ) // webm
{
@ -1023,19 +1039,19 @@ namespace MediaBrowser.Controller.MediaEncoding
& & ! string . Equals ( videoEncoder , "h264_qsv" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( videoEncoder , "h264_vaapi" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( videoEncoder , "h264_nvenc" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( videoEncoder , "h264_amf" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( videoEncoder , "h264_v4l2m2m" , StringComparison . OrdinalIgnoreCase ) )
{
param = "-pix_fmt yuv420p " + param ;
}
if ( string . Equals ( videoEncoder , "h264_nvenc" , StringComparison . OrdinalIgnoreCase ) )
if ( string . Equals ( videoEncoder , "h264_nvenc" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( videoEncoder , "h264_amf" , StringComparison . OrdinalIgnoreCase ) )
{
var videoDecoder = GetHardwareAcceleratedVideoDecoder ( state , encodingOptions ) ? ? string . Empty ;
var videoStream = state . VideoStream ;
var isColorDepth10 = IsColorDepth10 ( state ) ;
if ( videoDecoder . IndexOf ( "hevc_cuvid" , StringComparison . OrdinalIgnoreCase ) ! = - 1
& & isColorDepth10
if ( isColorDepth10
& & _mediaEncoder . SupportsHwaccel ( "opencl" )
& & encodingOptions . EnableTonemapping
& & ! string . IsNullOrEmpty ( videoStream . VideoRange )
@ -1651,47 +1667,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputSizeParam = ReadOnlySpan < char > . Empty ;
var request = state . BaseRequest ;
outputSizeParam = GetOutputSizeParam ( state , options , outputVideoCodec ) . TrimEnd ( '"' ) ;
// All possible beginning of video filters
// Don't break the order
string [ ] beginOfOutputSizeParam = new [ ]
{
// for tonemap_opencl
"hwupload,tonemap_opencl" ,
// hwupload=extra_hw_frames=64,vpp_qsv (for overlay_qsv on linux)
"hwupload=extra_hw_frames" ,
// vpp_qsv
"vpp" ,
// hwdownload,format=p010le (hardware decode + software encode for vaapi)
"hwdownload" ,
// format=nv12|vaapi,hwupload,scale_vaapi
"format" ,
// bwdif,scale=expr
"bwdif" ,
// yadif,scale=expr
"yadif" ,
// scale=expr
"scale"
} ;
var index = - 1 ;
foreach ( var param in beginOfOutputSizeParam )
{
index = outputSizeParam . IndexOf ( param , StringComparison . OrdinalIgnoreCase ) ;
if ( index ! = - 1 )
{
outputSizeParam = outputSizeParam . Slice ( index ) ;
break ;
}
}
outputSizeParam = GetOutputSizeParamInternal ( state , options , outputVideoCodec ) ;
var videoSizeParam = string . Empty ;
var videoDecoder = GetHardwareAcceleratedVideoDecoder ( state , options ) ? ? string . Empty ;
@ -2083,10 +2059,19 @@ namespace MediaBrowser.Controller.MediaEncoding
return string . Format ( CultureInfo . InvariantCulture , filter , widthParam , heightParam ) ;
}
public string GetOutputSizeParam (
EncodingJobInfo state ,
EncodingOptions options ,
string outputVideoCodec )
{
string filters = GetOutputSizeParamInternal ( state , options , outputVideoCodec ) ;
return string . IsNullOrEmpty ( filters ) ? string . Empty : " -vf \"" + filters + "\"" ;
}
/// <summary>
/// If we're going to put a fixed size on the command line, this will calculate it.
/// </summary>
public string GetOutputSizeParam (
public string GetOutputSizeParam Internal (
EncodingJobInfo state ,
EncodingOptions options ,
string outputVideoCodec )
@ -2102,6 +2087,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var inputHeight = videoStream ? . Height ;
var threeDFormat = state . MediaSource . Video3DFormat ;
var isSwDecoder = string . IsNullOrEmpty ( videoDecoder ) ;
var isD3d11vaDecoder = videoDecoder . IndexOf ( "d3d11va" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isVaapiDecoder = videoDecoder . IndexOf ( "vaapi" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isVaapiH264Encoder = outputVideoCodec . IndexOf ( "h264_vaapi" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
var isQsvH264Encoder = outputVideoCodec . IndexOf ( "h264_qsv" , StringComparison . OrdinalIgnoreCase ) ! = - 1 ;
@ -2117,47 +2104,77 @@ namespace MediaBrowser.Controller.MediaEncoding
// If double rate deinterlacing is enabled and the input framerate is 30fps or below, otherwise the output framerate will be too high for many devices
var doubleRateDeinterlace = options . DeinterlaceDoubleRate & & ( videoStream ? . RealFrameRate ? ? 60 ) < = 30 ;
// Currently only with the use of NVENC decoder can we get a decent performance.
// Currently only the HEVC/H265 format is supported.
// NVIDIA Pascal and Turing or higher are recommended.
if ( isNvdecHevcDecoder & & isColorDepth10
& & _mediaEncoder . SupportsHwaccel ( "opencl" )
& & options . EnableTonemapping
& & ! string . IsNullOrEmpty ( videoStream . VideoRange )
& & videoStream . VideoRange . Contains ( "HDR" , StringComparison . OrdinalIgnoreCase ) )
{
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}" ;
var isScalingInAdvance = false ;
var isDeinterlaceH264 = state . DeInterlace ( "h264" , true ) | | state . DeInterlace ( "avc" , true ) ;
var isDeinterlaceHevc = state . DeInterlace ( "h265" , true ) | | state . DeInterlace ( "hevc" , true ) ;
if ( options . TonemappingParam ! = 0 )
if ( ( string . Equals ( options . HardwareAccelerationType , "nvenc" , StringComparison . OrdinalIgnoreCase ) & & isNvdecHevcDecoder | | isSwDecoder )
| | ( string . Equals ( options . HardwareAccelerationType , "amf" , StringComparison . OrdinalIgnoreCase ) & & isD3d11vaDecoder | | isSwDecoder ) )
{
// Currently only with the use of NVENC decoder can we get a decent performance.
// Currently only the HEVC/H265 format is supported with NVDEC decoder.
// NVIDIA Pascal and Turing or higher are recommended.
// AMD Polaris and Vega or higher are recommended.
if ( isColorDepth10
& & _mediaEncoder . SupportsHwaccel ( "opencl" )
& & options . EnableTonemapping
& & ! string . IsNullOrEmpty ( videoStream . VideoRange )
& & videoStream . VideoRange . Contains ( "HDR" , StringComparison . OrdinalIgnoreCase ) )
{
parameters + = ":param={4}" ;
}
var parameters = "tonemap_opencl=format=nv12:primaries=bt709:transfer=bt709:matrix=bt709:tonemap={0}:desat={1}:threshold={2}:peak={3}" ;
if ( ! string . Equals ( options . TonemappingRange , "auto" , StringComparison . OrdinalIgnoreCase ) )
{
parameters + = ":range={5}" ;
}
if ( options . TonemappingParam ! = 0 )
{
parameters + = ":param={4 }";
}
// Upload the HDR10 or HLG data to the OpenCL device,
// use tonemap_opencl filter for tone mapping,
// and then download the SDR data to memory.
filters . Add ( "hwupload" ) ;
filters . Add (
string . Format (
CultureInfo . InvariantCulture ,
parameters ,
options . TonemappingAlgorithm ,
options . TonemappingDesat ,
options . TonemappingThreshold ,
options . TonemappingPeak ,
options . TonemappingParam ,
options . TonemappingRange ) ) ;
filters . Add ( "hwdownload" ) ;
if ( hasGraphicalSubs | | state . DeInterlace ( "h265" , true ) | | state . DeInterlace ( "hevc" , true )
| | string . Equals ( outputVideoCodec , "libx264" , StringComparison . OrdinalIgnoreCase ) )
{
filters . Add ( "format=nv12" ) ;
if ( ! string . Equals ( options . TonemappingRange , "auto" , StringComparison . OrdinalIgnoreCase ) )
{
parameters + = ":range={5}" ;
}
if ( isSwDecoder | | isD3d11vaDecoder )
{
isScalingInAdvance = true ;
// Add zscale filter before tone mapping filter for performance.
var ( width , height ) = GetFixedOutputSize ( inputWidth , inputHeight , request . Width , request . Height , request . MaxWidth , request . MaxHeight ) ;
if ( width . HasValue & & height . HasValue )
{
filters . Add (
string . Format (
CultureInfo . InvariantCulture ,
"zscale=s={0}x{1}" ,
width . Value ,
height . Value ) ) ;
}
// Convert to hardware pixel format p010 when using SW decoder.
filters . Add ( "format=p010" ) ;
}
// Upload the HDR10 or HLG data to the OpenCL device,
// use tonemap_opencl filter for tone mapping,
// and then download the SDR data to memory.
filters . Add ( "hwupload" ) ;
filters . Add (
string . Format (
CultureInfo . InvariantCulture ,
parameters ,
options . TonemappingAlgorithm ,
options . TonemappingDesat ,
options . TonemappingThreshold ,
options . TonemappingPeak ,
options . TonemappingParam ,
options . TonemappingRange ) ) ;
filters . Add ( "hwdownload" ) ;
if ( isLibX264Encoder
| | hasGraphicalSubs
| | ( isNvdecHevcDecoder & & isDeinterlaceHevc )
| | ( ! isNvdecHevcDecoder & & isDeinterlaceH264 | | isDeinterlaceHevc ) )
{
filters . Add ( "format=nv12" ) ;
}
}
}
@ -2202,7 +2219,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add hardware deinterlace filter before scaling filter
if ( state. DeInterlace ( "h264" , true ) | | state . DeInterlace ( "avc" , true ) )
if ( isDeinterlaceH264 )
{
if ( isVaapiH264Encoder )
{
@ -2215,10 +2232,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add software deinterlace filter before scaling filter
if ( ( state . DeInterlace ( "h264" , true )
| | state . DeInterlace ( "avc" , true )
| | state . DeInterlace ( "h265" , true )
| | state . DeInterlace ( "hevc" , true ) )
if ( ( isDeinterlaceH264 | | isDeinterlaceHevc )
& & ! isVaapiH264Encoder
& & ! isQsvH264Encoder
& & ! isNvdecH264Decoder )
@ -2242,7 +2256,21 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Add scaling filter: scale_*=format=nv12 or scale_*=w=*:h=*:format=nv12 or scale=expr
filters . AddRange ( GetScalingFilters ( state , inputWidth , inputHeight , threeDFormat , videoDecoder , outputVideoCodec , request . Width , request . Height , request . MaxWidth , request . MaxHeight ) ) ;
if ( ! isScalingInAdvance )
{
filters . AddRange (
GetScalingFilters (
state ,
inputWidth ,
inputHeight ,
threeDFormat ,
videoDecoder ,
outputVideoCodec ,
request . Width ,
request . Height ,
request . MaxWidth ,
request . MaxHeight ) ) ;
}
// Add parameters to use VAAPI with burn-in text subtitles (GH issue #642)
if ( isVaapiH264Encoder )
@ -2275,7 +2303,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
output + = string . Format (
CultureInfo . InvariantCulture ,
" -vf \" {0}\" ",
" {0}",
string . Join ( "," , filters ) ) ;
}
@ -3068,21 +3096,31 @@ namespace MediaBrowser.Controller.MediaEncoding
var isWindows8orLater = Environment . OSVersion . Version . Major > 6 | | ( Environment . OSVersion . Version . Major = = 6 & & Environment . OSVersion . Version . Minor > 1 ) ;
var isDxvaSupported = _mediaEncoder . SupportsHwaccel ( "dxva2" ) | | _mediaEncoder . SupportsHwaccel ( "d3d11va" ) ;
if ( ( isDxvaSupported | | IsVaapiSupported ( state ) ) & & options . HardwareDecodingCodecs . Contains ( videoCodec , StringComparer . OrdinalIgnoreCase ) )
if ( string . Equals ( options . HardwareAccelerationType , "amf" , StringComparison . OrdinalIgnoreCase ) )
{
if ( isLinux )
// Currently there is no AMF decoder on Linux, only have h264 encoder.
if ( isDxvaSupported & & options . HardwareDecodingCodecs . Contains ( videoCodec , StringComparer . OrdinalIgnoreCase ) )
{
return "-hwaccel vaapi" ;
}
if ( isWindows & & isWindows8orLater )
{
return "-hwaccel d3d11va" ;
}
if ( isWindows & & isWindows8orLater )
{
return "-hwaccel d3d11va" ;
if ( isWindows & & ! isWindows8orLater )
{
return "-hwaccel dxva2" ;
}
}
}
if ( isWindows & & ! isWindows8orLater )
if ( string . Equals ( options . HardwareAccelerationType , "vaapi" , StringComparison . OrdinalIgnoreCase ) )
{
if ( IsVaapiSupported ( state ) & & options . HardwareDecodingCodecs . Contains ( videoCodec , StringComparer . OrdinalIgnoreCase ) )
{
return "-hwaccel dxva2" ;
if ( isLinux )
{
return "-hwaccel vaapi" ;
}
}
}