@ -135,9 +135,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var inputFiles = MediaEncoderHelpers . GetInputArgument ( FileSystem , request . InputPath , request . Protocol , request . MountedIso , request . PlayableStreamFileNames ) ;
var extractKeyFrameInterval = request . ExtractKeyFrameInterval & & request . Protocol = = MediaProtocol . File & & request . VideoType = = VideoType . VideoFile ;
return GetMediaInfoInternal ( GetInputArgument ( inputFiles , request . Protocol ) , request . InputPath , request . Protocol , extractChapters , extractKeyFrameInterval ,
return GetMediaInfoInternal ( GetInputArgument ( inputFiles , request . Protocol ) , request . InputPath , request . Protocol , extractChapters ,
GetProbeSizeArgument ( inputFiles , request . Protocol ) , request . MediaType = = DlnaProfileType . Audio , request . VideoType , cancellationToken ) ;
}
@ -171,18 +169,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="primaryPath">The primary path.</param>
/// <param name="protocol">The protocol.</param>
/// <param name="extractChapters">if set to <c>true</c> [extract chapters].</param>
/// <param name="extractKeyFrameInterval">if set to <c>true</c> [extract key frame interval].</param>
/// <param name="probeSizeArgument">The probe size argument.</param>
/// <param name="isAudio">if set to <c>true</c> [is audio].</param>
/// <param name="videoType">Type of the video.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MediaInfoResult}.</returns>
/// <exception cref="System.ApplicationException"> </exception>
/// <exception cref="System.ApplicationException"> ffprobe failed - streams and format are both null. </exception>
private async Task < Model . MediaInfo . MediaInfo > GetMediaInfoInternal ( string inputPath ,
string primaryPath ,
MediaProtocol protocol ,
bool extractChapters ,
bool extractKeyFrameInterval ,
string probeSizeArgument ,
bool isAudio ,
VideoType videoType ,
@ -262,31 +258,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mediaInfo = new ProbeResultNormalizer ( _logger , FileSystem ) . GetMediaInfo ( result , videoType , isAudio , primaryPath , protocol ) ;
if ( extractKeyFrameInterval & & mediaInfo . RunTimeTicks . HasValue )
{
if ( ConfigurationManager . Configuration . EnableVideoFrameByFrameAnalysis & & mediaInfo . Size . HasValue )
{
foreach ( var stream in mediaInfo . MediaStreams )
{
if ( EnableKeyframeExtraction ( mediaInfo , stream ) )
{
try
{
stream . KeyFrames = await GetKeyFrames ( inputPath , stream . Index , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( OperationCanceledException )
{
}
catch ( Exception ex )
{
_logger . ErrorException ( "Error getting key frame interval" , ex ) ;
}
}
}
}
}
return mediaInfo ;
}
catch
@ -300,132 +271,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
_ffProbeResourcePool . Release ( ) ;
}
}
throw new ApplicationException ( string . Format ( "FFProbe failed for {0}" , inputPath ) ) ;
}
private bool EnableKeyframeExtraction ( MediaSourceInfo mediaSource , MediaStream videoStream )
{
if ( videoStream . Type = = MediaStreamType . Video & & string . Equals ( videoStream . Codec , "h264" , StringComparison . OrdinalIgnoreCase ) & &
! videoStream . IsInterlaced & &
! ( videoStream . IsAnamorphic ? ? false ) )
{
var audioStreams = mediaSource . MediaStreams . Where ( i = > i . Type = = MediaStreamType . Audio ) . ToList ( ) ;
// If it has aac audio then it will probably direct stream anyway, so don't bother with this
if ( audioStreams . Count = = 1 & & string . Equals ( audioStreams [ 0 ] . Codec , "aac" , StringComparison . OrdinalIgnoreCase ) )
{
return false ;
}
return true ;
}
return false ;
}
private async Task < List < int > > GetKeyFrames ( string inputPath , int videoStreamIndex , CancellationToken cancellationToken )
{
inputPath = inputPath . Split ( new [ ] { ':' } , 2 ) . Last ( ) . Trim ( '"' ) ;
const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\"" ;
var process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true ,
UseShellExecute = false ,
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
RedirectStandardOutput = true ,
RedirectStandardError = true ,
FileName = FFProbePath ,
Arguments = string . Format ( args , inputPath , videoStreamIndex . ToString ( CultureInfo . InvariantCulture ) ) . Trim ( ) ,
WindowStyle = ProcessWindowStyle . Hidden ,
ErrorDialog = false
} ,
EnableRaisingEvents = true
} ;
_logger . Info ( "{0} {1}" , process . StartInfo . FileName , process . StartInfo . Arguments ) ;
using ( process )
{
var start = DateTime . UtcNow ;
process . Start ( ) ;
var lines = new List < int > ( ) ;
try
{
process . BeginErrorReadLine ( ) ;
await StartReadingOutput ( process . StandardOutput . BaseStream , lines , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( OperationCanceledException )
{
if ( cancellationToken . IsCancellationRequested )
{
throw ;
}
}
process . WaitForExit ( ) ;
_logger . Info ( "Keyframe extraction took {0} seconds" , ( DateTime . UtcNow - start ) . TotalSeconds ) ;
//_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray()));
return lines ;
}
}
private async Task StartReadingOutput ( Stream source , List < int > keyframes , CancellationToken cancellationToken )
{
try
{
using ( var reader = new StreamReader ( source ) )
{
var text = await reader . ReadToEndAsync ( ) . ConfigureAwait ( false ) ;
var lines = StringHelper . RegexSplit ( text , "[\r\n]+" ) ;
foreach ( var line in lines )
{
if ( string . IsNullOrWhiteSpace ( line ) )
{
continue ;
}
var values = line . Split ( '|' )
. Where ( i = > ! string . IsNullOrWhiteSpace ( i ) )
. Select ( i = > i . Split ( '=' ) )
. Where ( i = > i . Length = = 2 )
. ToDictionary ( i = > i [ 0 ] , i = > i [ 1 ] ) ;
string flags ;
if ( values . TryGetValue ( "flags" , out flags ) & & string . Equals ( flags , "k" , StringComparison . OrdinalIgnoreCase ) )
{
string pts_time ;
double frameSeconds ;
if ( values . TryGetValue ( "pts_time" , out pts_time ) & & double . TryParse ( pts_time , NumberStyles . Any , CultureInfo . InvariantCulture , out frameSeconds ) )
{
var ms = frameSeconds * 1000 ;
keyframes . Add ( Convert . ToInt32 ( ms ) ) ;
}
}
}
}
}
catch ( OperationCanceledException )
{
throw ;
}
catch ( Exception ex )
{
_logger . ErrorException ( "Error reading ffprobe output" , ex ) ;
}
}
/// <summary>
/// The us culture
/// </summary>