@ -303,15 +303,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
return job . Options . CpuCoreLimit ? ? 0 ;
}
protected string GetInputModifier ( EncodingJob job , bool genPts = true )
protected string GetInputModifier ( EncodingJob state , bool genPts = true )
{
var inputModifier = string . Empty ;
var probeSize = GetProbeSizeArgument ( job ) ;
var probeSize = GetProbeSizeArgument ( state ) ;
inputModifier + = " " + probeSize ;
inputModifier = inputModifier . Trim ( ) ;
var userAgentParam = GetUserAgentParam ( job ) ;
var userAgentParam = GetUserAgentParam ( state ) ;
if ( ! string . IsNullOrWhiteSpace ( userAgentParam ) )
{
@ -320,35 +320,43 @@ namespace MediaBrowser.MediaEncoding.Encoder
inputModifier = inputModifier . Trim ( ) ;
inputModifier + = " " + GetFastSeekCommandLineParameter ( job . Options ) ;
inputModifier + = " " + GetFastSeekCommandLineParameter ( state . Options ) ;
inputModifier = inputModifier . Trim ( ) ;
if ( job . IsVideoRequest & & genPts )
if ( state . IsVideoRequest & & genPts )
{
inputModifier + = " -fflags +genpts" ;
}
if ( ! string . IsNullOrEmpty ( job . InputAudioSync ) )
if ( ! string . IsNullOrEmpty ( state . InputAudioSync ) )
{
inputModifier + = " -async " + job . InputAudioSync ;
inputModifier + = " -async " + state . InputAudioSync ;
}
if ( ! string . IsNullOrEmpty ( job . InputVideoSync ) )
if ( ! string . IsNullOrEmpty ( state . InputVideoSync ) )
{
inputModifier + = " -vsync " + job . InputVideoSync ;
inputModifier + = " -vsync " + state . InputVideoSync ;
}
if ( job . ReadInputAtNativeFramerate )
if ( state . ReadInputAtNativeFramerate )
{
inputModifier + = " -re" ;
}
var videoDecoder = GetVideoDecoder ( job ) ;
var videoDecoder = GetVideoDecoder ( state ) ;
if ( ! string . IsNullOrWhiteSpace ( videoDecoder ) )
{
inputModifier + = " " + videoDecoder ;
}
//if (state.IsVideoRequest)
//{
// if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
// {
// //inputModifier += " -noaccurate_seek";
// }
//}
return inputModifier ;
}
@ -392,11 +400,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
return null ;
}
private string GetUserAgentParam ( EncodingJob job )
private string GetUserAgentParam ( EncodingJob state )
{
string useragent = null ;
job . RemoteHttpHeaders . TryGetValue ( "User-Agent" , out useragent ) ;
state . RemoteHttpHeaders . TryGetValue ( "User-Agent" , out useragent ) ;
if ( ! string . IsNullOrWhiteSpace ( useragent ) )
{
@ -409,31 +417,31 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the probe size argument.
/// </summary>
/// <param name=" job">The job .</param>
/// <param name=" state">The state .</param>
/// <returns>System.String.</returns>
private string GetProbeSizeArgument ( EncodingJob job )
private string GetProbeSizeArgument ( EncodingJob state )
{
if ( job . PlayableStreamFileNames . Count > 0 )
if ( state . PlayableStreamFileNames . Count > 0 )
{
return MediaEncoder . GetProbeSizeArgument ( job . PlayableStreamFileNames . ToArray ( ) , job . InputProtocol ) ;
return MediaEncoder . GetProbeSizeArgument ( state . PlayableStreamFileNames . ToArray ( ) , state . InputProtocol ) ;
}
return MediaEncoder . GetProbeSizeArgument ( new [ ] { job. MediaPath } , job . InputProtocol ) ;
return MediaEncoder . GetProbeSizeArgument ( new [ ] { state. MediaPath } , state . InputProtocol ) ;
}
/// <summary>
/// Gets the fast seek command line parameter.
/// </summary>
/// <param name=" options">The options .</param>
/// <param name=" request">The request .</param>
/// <returns>System.String.</returns>
/// <value>The fast seek command line parameter.</value>
protected string GetFastSeekCommandLineParameter ( EncodingJobOptions options )
protected string GetFastSeekCommandLineParameter ( EncodingJobOptions request )
{
var time = options. StartTimeTicks ;
var time = request. StartTimeTicks ? ? 0 ;
if ( time . HasValue & & time . Value > 0 )
if ( time > 0 )
{
return string . Format ( "-ss {0}" , MediaEncoder . GetTimeParameter ( time .Value )) ;
return string . Format ( "-ss {0}" , MediaEncoder . GetTimeParameter ( time )) ;
}
return string . Empty ;
@ -442,34 +450,35 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name=" job">The job .</param>
/// <param name=" state">The state .</param>
/// <returns>System.String.</returns>
protected string GetInputArgument ( EncodingJob job )
protected string GetInputArgument ( EncodingJob state )
{
var arg = "-i " + GetInputPathArgument ( job ) ;
var arg = string . Format ( "-i {0}" , GetInputPathArgument ( state ) ) ;
if ( job . SubtitleStream ! = null )
if ( state . SubtitleStream ! = null )
{
if ( job . SubtitleStream . IsExternal & & ! job . SubtitleStream . IsTextSubtitleStream )
if ( state . SubtitleStream . IsExternal & & ! state . SubtitleStream . IsTextSubtitleStream )
{
arg + = " -i \"" + job . SubtitleStream . Path + "\"" ;
arg + = " -i \"" + state . SubtitleStream . Path + "\"" ;
}
}
return arg ;
return arg .Trim ( ) ;
}
private string GetInputPathArgument ( EncodingJob job )
private string GetInputPathArgument ( EncodingJob state )
{
var protocol = job . InputProtocol ;
var protocol = state . InputProtocol ;
var mediaPath = state . MediaPath ? ? string . Empty ;
var inputPath = new [ ] { job. M ediaPath } ;
var inputPath = new [ ] { m ediaPath } ;
if ( job . IsInputVideo )
if ( state . IsInputVideo )
{
if ( ! ( job . VideoType = = VideoType . Iso & & job . IsoMount = = null ) )
if ( ! ( state . VideoType = = VideoType . Iso & & state . IsoMount = = null ) )
{
inputPath = MediaEncoderHelpers . GetInputArgument ( FileSystem , job. MediaPath , job . InputProtocol , job . IsoMount , job . PlayableStreamFileNames ) ;
inputPath = MediaEncoderHelpers . GetInputArgument ( FileSystem , mediaPath, state . InputProtocol , state . IsoMount , state . PlayableStreamFileNames ) ;
}
}
@ -491,7 +500,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} , false , cancellationToken ) . ConfigureAwait ( false ) ;
AttachMediaS tream Info( state , liveStreamResponse . MediaSource , state . Options ) ;
AttachMediaS ource Info( state , liveStreamResponse . MediaSource , state . Options ) ;
if ( state . IsVideoRequest )
{
@ -505,11 +514,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
private void AttachMediaS tream Info( EncodingJob state ,
private void AttachMediaS ource Info( EncodingJob state ,
MediaSourceInfo mediaSource ,
EncodingJobOptions videoRequest )
{
EncodingJobFactory . AttachMediaS tream Info( state , mediaSource , videoRequest ) ;
EncodingJobFactory . AttachMediaS ource Info( state , mediaSource , videoRequest ) ;
}
/// <summary>
@ -572,7 +581,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
param = "-preset superfast" ;
param + = " -crf 2 8 ";
param + = " -crf 2 3 ";
}
else if ( string . Equals ( videoCodec , "libx265" , StringComparison . OrdinalIgnoreCase ) )
@ -582,6 +591,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
param + = " -crf 28" ;
}
// h264 (h264_qsv)
else if ( string . Equals ( videoCodec , "h264_qsv" , StringComparison . OrdinalIgnoreCase ) )
{
param = "-preset 7 -look_ahead 0" ;
}
// h264 (libnvenc)
else if ( string . Equals ( videoCodec , "libnvenc" , StringComparison . OrdinalIgnoreCase ) )
{
param = "-preset high-performance" ;
}
// webm
else if ( string . Equals ( videoCodec , "libvpx" , StringComparison . OrdinalIgnoreCase ) )
{
@ -644,9 +666,53 @@ namespace MediaBrowser.MediaEncoding.Encoder
param + = " -profile:v " + state . Options . Profile ;
}
if ( state . Options . Level . HasValue )
var levelString = state . Options . Level . HasValue ? state . Options . Level . Value . ToString ( CultureInfo . InvariantCulture ) : null ;
if ( ! string . IsNullOrEmpty ( levelString ) )
{
param + = " -level " + state . Options . Level . Value . ToString ( UsCulture ) ;
var h264Encoder = EncodingJobFactory . GetH264Encoder ( state , GetEncodingOptions ( ) ) ;
// h264_qsv and libnvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
if ( String . Equals ( h264Encoder , "h264_qsv" , StringComparison . OrdinalIgnoreCase ) | | String . Equals ( h264Encoder , "libnvenc" , StringComparison . OrdinalIgnoreCase ) )
{
switch ( levelString )
{
case "30" :
param + = " -level 3" ;
break ;
case "31" :
param + = " -level 3.1" ;
break ;
case "32" :
param + = " -level 3.2" ;
break ;
case "40" :
param + = " -level 4" ;
break ;
case "41" :
param + = " -level 4.1" ;
break ;
case "42" :
param + = " -level 4.2" ;
break ;
case "50" :
param + = " -level 5" ;
break ;
case "51" :
param + = " -level 5.1" ;
break ;
case "52" :
param + = " -level 5.2" ;
break ;
default :
param + = " -level " + levelString ;
break ;
}
}
else
{
param + = " -level " + levelString ;
}
}
return "-pix_fmt yuv420p " + param ;
@ -658,15 +724,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
if ( bitrate . HasValue )
{
var hasFixedResolution = state . Options . HasFixedResolution ;
if ( string . Equals ( videoCodec , "libvpx" , StringComparison . OrdinalIgnoreCase ) )
{
if ( hasFixedResolution )
{
return string . Format ( " -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
}
// With vpx when crf is used, b:v becomes a max rate
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
return string . Format ( " -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
@ -677,20 +736,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
return string . Format ( " -b:v {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
}
// H 264
if ( hasFixedResolution )
// h 264
if ( isHls )
{
if ( isHls )
{
return string . Format ( " -b:v {0} -maxrate ({0}*.80) -bufsize {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
}
return string . Format ( " -b:v {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
return string . Format ( " -b:v {0} -maxrate {0} -bufsize {1}" ,
bitrate . Value . ToString ( UsCulture ) ,
( bitrate . Value * 2 ) . ToString ( UsCulture ) ) ;
}
return string . Format ( " -maxrate {0} -bufsize {1}" ,
bitrate . Value . ToString ( UsCulture ) ,
( bitrate . Value * 2 ) . ToString ( UsCulture ) ) ;
return string . Format ( " -b:v {0}" , bitrate . Value . ToString ( UsCulture ) ) ;
}
return string . Empty ;
@ -698,20 +752,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected double? GetFramerateParam ( EncodingJob state )
{
if ( state . Options . Framerate . HasValue )
if ( state . Options ! = null )
{
return state . Options . Framerate . Value ;
}
var maxrate = state . Options . MaxFramerate ;
if ( state . Options . Framerate . HasValue )
{
return state . Options . Framerate . Value ;
}
if ( maxrate . HasValue & & state . VideoStream ! = null )
{
var contentRate = state . VideoStream . AverageFrameRate ? ? state . VideoStream . RealFrameRate ;
var maxrate = state . Options . MaxFramerate ;
if ( contentRate. HasValue & & contentRate . Value > maxrate . Value )
if ( maxrate . HasValue & & state . VideoStream ! = null )
{
return maxrate ;
var contentRate = state . VideoStream . AverageFrameRate ? ? state . VideoStream . RealFrameRate ;
if ( contentRate . HasValue & & contentRate . Value > maxrate . Value )
{
return maxrate ;
}
}
}
@ -852,7 +909,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var maxWidthParam = request . MaxWidth . Value . ToString ( UsCulture ) ;
filters . Add ( string . Format ( "scale= min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam ) ) ;
filters . Add ( string . Format ( "scale= trunc( min(max( iw\\,ih*dar)\\, {0})/2)*2 :trunc(ow/dar/2)*2", maxWidthParam ) ) ;
}
// If a max height was requested
@ -863,6 +920,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
filters . Add ( string . Format ( "scale=trunc(oh*a/2)*2:min(ih\\,{0})" , maxHeightParam ) ) ;
}
if ( string . Equals ( outputVideoCodec , "h264_qsv" , StringComparison . OrdinalIgnoreCase ) )
{
if ( filters . Count > 1 )
{
//filters[filters.Count - 1] += ":flags=fast_bilinear";
}
}
var output = string . Empty ;
if ( state . SubtitleStream ! = null & & state . SubtitleStream . IsTextSubtitleStream )
@ -917,8 +982,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
seconds . ToString ( UsCulture ) ) ;
}
var mediaPath = state . MediaPath ? ? string . Empty ;
return string . Format ( "subtitles='{0}:si={1}',setpts=PTS -{2}/TB" ,
MediaEncoder . EscapeSubtitleFilterPath ( state . MediaPath ) ,
MediaEncoder . EscapeSubtitleFilterPath ( m ediaPath) ,
state . InternalSubtitleStreamOffset . ToString ( UsCulture ) ,
seconds . ToString ( UsCulture ) ) ;
}