@ -559,6 +559,12 @@ namespace MediaBrowser.Controller.MediaEncoding
if ( string . Equals ( codec , "aac" , StringComparison . OrdinalIgnoreCase ) )
if ( string . Equals ( codec , "aac" , StringComparison . OrdinalIgnoreCase ) )
{
{
// Use Apple's aac encoder if available as it provides best audio quality
if ( _mediaEncoder . SupportsEncoder ( "aac_at" ) )
{
return "aac_at" ;
}
// Use libfdk_aac for better audio quality if using custom build of FFmpeg which has fdk_aac support
// Use libfdk_aac for better audio quality if using custom build of FFmpeg which has fdk_aac support
if ( _mediaEncoder . SupportsEncoder ( "libfdk_aac" ) )
if ( _mediaEncoder . SupportsEncoder ( "libfdk_aac" ) )
{
{
@ -2814,6 +2820,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{
{
return "deinterlace_qsv=mode=2" ;
return "deinterlace_qsv=mode=2" ;
}
}
else if ( hwDeintSuffix . Contains ( "videotoolbox" , StringComparison . OrdinalIgnoreCase ) )
{
return string . Format (
CultureInfo . InvariantCulture ,
"yadif_videotoolbox={0}:-1:0" ,
doubleRateDeint ? "1" : "0" ) ;
}
return string . Empty ;
return string . Empty ;
}
}
@ -4450,6 +4463,75 @@ namespace MediaBrowser.Controller.MediaEncoding
return ( mainFilters , subFilters , overlayFilters ) ;
return ( mainFilters , subFilters , overlayFilters ) ;
}
}
/// <summary>
/// Gets the parameter of Apple VideoToolBox 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 ) GetAppleVidFilterChain (
EncodingJobInfo state ,
EncodingOptions options ,
string vidEncoder )
{
if ( ! string . Equals ( options . HardwareAccelerationType , "videotoolbox" , StringComparison . OrdinalIgnoreCase ) )
{
return ( null , null , null ) ;
}
var swFilterChain = GetSwVidFilterChain ( state , options , vidEncoder ) ;
if ( ! options . EnableHardwareEncoding )
{
return swFilterChain ;
}
if ( _mediaEncoder . EncoderVersion . CompareTo ( new Version ( "5.0.0" ) ) < 0 )
{
// All features used here requires ffmpeg 5.0 or later, fallback to software filters if using an old ffmpeg
return swFilterChain ;
}
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 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 newfilters = new List < string > ( ) ;
var noOverlay = swFilterChain . OverlayFilters . Count = = 0 ;
var supportsHwDeint = _mediaEncoder . SupportsFilter ( "yadif_videotoolbox" ) ;
// fallback to software filters if we are using filters not supported by hardware yet.
var useHardwareFilters = noOverlay & & ( ! doDeintH2645 | | supportsHwDeint ) ;
if ( ! useHardwareFilters )
{
return swFilterChain ;
}
// ffmpeg cannot use videotoolbox to scale
var swScaleFilter = GetSwScaleFilter ( state , options , vidEncoder , inW , inH , threeDFormat , reqW , reqH , reqMaxW , reqMaxH ) ;
newfilters . Add ( swScaleFilter ) ;
// hwupload on videotoolbox encoders can automatically convert AVFrame into its CVPixelBuffer equivalent
// videotoolbox will automatically convert the CVPixelBuffer to a pixel format the encoder supports, so we don't have to set a pixel format explicitly here
// This will reduce CPU usage significantly on UHD videos with 10 bit colors because we bypassed the ffmpeg pixel format conversion
newfilters . Add ( "hwupload" ) ;
if ( doDeintH2645 )
{
var deintFilter = GetHwDeinterlaceFilter ( state , options , "videotoolbox" ) ;
newfilters . Add ( deintFilter ) ;
}
return ( newfilters , swFilterChain . SubFilters , swFilterChain . OverlayFilters ) ;
}
/// <summary>
/// <summary>
/// Gets the parameter of video processing filters.
/// Gets the parameter of video processing filters.
/// </summary>
/// </summary>
@ -4492,6 +4574,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
{
( mainFilters , subFilters , overlayFilters ) = GetAmdVidFilterChain ( state , options , outputVideoCodec ) ;
( mainFilters , subFilters , overlayFilters ) = GetAmdVidFilterChain ( state , options , outputVideoCodec ) ;
}
}
else if ( string . Equals ( options . HardwareAccelerationType , "videotoolbox" , StringComparison . OrdinalIgnoreCase ) )
{
( mainFilters , subFilters , overlayFilters ) = GetAppleVidFilterChain ( state , options , outputVideoCodec ) ;
}
else
else
{
{
( mainFilters , subFilters , overlayFilters ) = GetSwVidFilterChain ( state , options , outputVideoCodec ) ;
( mainFilters , subFilters , overlayFilters ) = GetSwVidFilterChain ( state , options , outputVideoCodec ) ;