@ -199,82 +199,83 @@ namespace MediaBrowser.MediaEncoding.Encoder
await _ffProbeResourcePool . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
var processWrapper = new ProcessWrapper ( process , this ) ;
try
{
StartProcess ( processWrapper ) ;
}
catch ( Exception ex )
using ( var processWrapper = new ProcessWrapper ( process , this ) )
{
_ffProbeResourcePool . Release ( ) ;
try
{
StartProcess ( processWrapper ) ;
}
catch ( Exception ex )
{
_ffProbeResourcePool . Release ( ) ;
_logger . ErrorException ( "Error starting ffprobe" , ex ) ;
_logger . ErrorException ( "Error starting ffprobe" , ex ) ;
throw ;
}
throw ;
}
try
{
process . BeginErrorReadLine ( ) ;
try
{
process . BeginErrorReadLine ( ) ;
var result = _jsonSerializer . DeserializeFromStream < InternalMediaInfoResult > ( process . StandardOutput . BaseStream ) ;
var result = _jsonSerializer . DeserializeFromStream < InternalMediaInfoResult > ( process . StandardOutput . BaseStream ) ;
if ( result ! = null )
{
if ( result . streams ! = null )
if ( result ! = null )
{
// Normalize aspect ratio if invalid
foreach ( var stream in result . streams )
if ( result . streams ! = null )
{
if ( string . Equals ( stream . display_aspect_ratio , "0:1" , StringComparison . OrdinalIgnoreCase ) )
{
stream . display_aspect_ratio = string . Empty ;
}
if ( string . Equals ( stream . sample_aspect_ratio , "0:1" , StringComparison . OrdinalIgnoreCase ) )
// Normalize aspect ratio if invalid
foreach ( var stream in result . streams )
{
stream . sample_aspect_ratio = string . Empty ;
if ( string . Equals ( stream . display_aspect_ratio , "0:1" , StringComparison . OrdinalIgnoreCase ) )
{
stream . display_aspect_ratio = string . Empty ;
}
if ( string . Equals ( stream . sample_aspect_ratio , "0:1" , StringComparison . OrdinalIgnoreCase ) )
{
stream . sample_aspect_ratio = string . Empty ;
}
}
}
}
var mediaInfo = new ProbeResultNormalizer ( _logger , FileSystem ) . GetMediaInfo ( result , videoType , isAudio , primaryPath , protocol ) ;
var mediaInfo = new ProbeResultNormalizer ( _logger , FileSystem ) . GetMediaInfo ( result , videoType , isAudio , primaryPath , protocol ) ;
if ( extractKeyFrameInterval & & mediaInfo . RunTimeTicks . HasValue )
{
foreach ( var stream in mediaInfo . MediaStreams )
if ( extractKeyFrameInterval & & mediaInfo . RunTimeTicks . HasValue )
{
if ( stream . Type = = MediaStreamType . Video & & string . Equals ( stream . Codec , "h264" , StringComparison . OrdinalIgnoreCase ) )
foreach ( var stream in mediaInfo . MediaStreams )
{
try
{
//stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken)
// .ConfigureAwait(false);
}
catch ( OperationCanceledException )
{
}
catch ( Exception ex )
if ( stream . Type = = MediaStreamType . Video & & string . Equals ( stream . Codec , "h264" , StringComparison . OrdinalIgnoreCase ) )
{
_logger . ErrorException ( "Error getting key frame interval" , ex ) ;
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 ;
return mediaInfo ;
}
}
}
catch
{
StopProcess ( processWrapper , 100 , true ) ;
catch
{
StopProcess ( processWrapper , 100 , true ) ;
throw ;
}
finally
{
_ffProbeResourcePool . Release ( ) ;
throw ;
}
finally
{
_ffProbeResourcePool . Release ( ) ;
}
}
throw new ApplicationException ( string . Format ( "FFProbe failed for {0}" , inputPath ) ) ;
@ -307,31 +308,32 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger . Debug ( "{0} {1}" , process . StartInfo . FileName , process . StartInfo . Arguments ) ;
var processWrapper = new ProcessWrapper ( process , this ) ;
StartProcess ( processWrapper ) ;
using ( var processWrapper = new ProcessWrapper ( process , this ) )
{
StartProcess ( processWrapper ) ;
var lines = new List < int > ( ) ;
var lines = new List < int > ( ) ;
try
{
process . BeginErrorReadLine ( ) ;
try
{
process . BeginErrorReadLine ( ) ;
await StartReadingOutput ( process . StandardOutput . BaseStream , lines , 120000 , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( OperationCanceledException )
{
if ( cancellationToken . IsCancellationRequested )
await StartReadingOutput ( process . StandardOutput . BaseStream , lines , 120000 , cancellationToken ) . ConfigureAwait ( false ) ;
}
catch ( OperationCanceledException )
{
throw ;
if ( cancellationToken . IsCancellationRequested )
{
throw ;
}
}
finally
{
StopProcess ( processWrapper , 100 , true ) ;
}
}
finally
{
StopProcess ( processWrapper , 100 , true ) ;
}
return lines ;
return lines ;
}
}
private async Task StartReadingOutput ( Stream source , List < int > lines , int timeoutMs , CancellationToken cancellationToken )
@ -490,51 +492,53 @@ namespace MediaBrowser.MediaEncoding.Encoder
await resourcePool . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
var processWrapper = new ProcessWrapper ( process , this ) ;
bool ranToCompletion ;
using ( var processWrapper = new ProcessWrapper ( process , this ) )
{
bool ranToCompletion ;
var memoryStream = new MemoryStream ( ) ;
var memoryStream = new MemoryStream ( ) ;
try
{
StartProcess ( processWrapper ) ;
try
{
StartProcess ( processWrapper ) ;
#pragma warning disable 4014
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process . StandardOutput . BaseStream . CopyToAsync ( memoryStream ) ;
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process . StandardOutput . BaseStream . CopyToAsync ( memoryStream ) ;
#pragma warning restore 4014
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process . BeginErrorReadLine ( ) ;
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process . BeginErrorReadLine ( ) ;
ranToCompletion = process . WaitForExit ( 10000 ) ;
ranToCompletion = process . WaitForExit ( 10000 ) ;
if ( ! ranToCompletion )
if ( ! ranToCompletion )
{
StopProcess ( processWrapper , 1000 , false ) ;
}
}
finally
{
StopProcess ( processWrapper , 1000 , false ) ;
resourcePool. Release ( ) ;
}
}
finally
{
resourcePool . Release ( ) ;
}
var exitCode = ranToCompletion ? processWrapper . ExitCode ? ? 0 : - 1 ;
var exitCode = ranToCompletion ? processWrapper . ExitCode ? ? 0 : - 1 ;
if ( exitCode = = - 1 | | memoryStream . Length = = 0 )
{
memoryStream . Dispose ( ) ;
if ( exitCode = = - 1 | | memoryStream . Length = = 0 )
{
memoryStream . Dispose ( ) ;
var msg = string . Format ( "ffmpeg image extraction failed for {0}" , inputPath ) ;
var msg = string . Format ( "ffmpeg image extraction failed for {0}" , inputPath ) ;
_logger . Error ( msg ) ;
_logger . Error ( msg ) ;
throw new ApplicationException ( msg ) ;
}
throw new ApplicationException ( msg ) ;
memoryStream . Position = 0 ;
return memoryStream ;
}
memoryStream . Position = 0 ;
return memoryStream ;
}
public string GetTimeParameter ( long ticks )
@ -603,55 +607,56 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool ranToCompletion = false ;
var processWrapper = new ProcessWrapper ( process , this ) ;
try
using ( var processWrapper = new ProcessWrapper ( process , this ) )
{
StartProcess ( processWrapper ) ;
try
{
StartProcess ( processWrapper ) ;
// Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
// but we still need to detect if the process hangs.
// Making the assumption that as long as new jpegs are showing up, everything is good.
// Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
// but we still need to detect if the process hangs.
// Making the assumption that as long as new jpegs are showing up, everything is good.
bool isResponsive = true ;
int lastCount = 0 ;
bool isResponsive = true ;
int lastCount = 0 ;
while ( isResponsive )
{
if ( process . WaitForExit ( 30000 ) )
while ( isResponsive )
{
ranToCompletion = true ;
break ;
}
if ( process . WaitForExit ( 30000 ) )
{
ranToCompletion = true ;
break ;
}
cancellationToken . ThrowIfCancellationRequested ( ) ;
cancellationToken . ThrowIfCancellationRequested ( ) ;
var jpegCount = Directory . GetFiles ( targetDirectory )
. Count ( i = > string . Equals ( Path . GetExtension ( i ) , ".jpg" , StringComparison . OrdinalIgnoreCase ) ) ;
var jpegCount = Directory . GetFiles ( targetDirectory )
. Count ( i = > string . Equals ( Path . GetExtension ( i ) , ".jpg" , StringComparison . OrdinalIgnoreCase ) ) ;
isResponsive = ( jpegCount > lastCount ) ;
lastCount = jpegCount ;
}
isResponsive = ( jpegCount > lastCount ) ;
lastCount = jpegCount ;
if ( ! ranToCompletion )
{
StopProcess ( processWrapper , 1000 , false ) ;
}
}
if ( ! ranToCompletion )
finally
{
StopProcess( processWrapper , 1000 , false ) ;
resourcePool. Release ( ) ;
}
}
finally
{
resourcePool . Release ( ) ;
}
var exitCode = ranToCompletion ? processWrapper . ExitCode ? ? 0 : - 1 ;
var exitCode = ranToCompletion ? processWrapper . ExitCode ? ? 0 : - 1 ;
if ( exitCode = = - 1 )
{
var msg = string . Format ( "ffmpeg image extraction failed for {0}" , inputArgument ) ;
if ( exitCode = = - 1 )
{
var msg = string . Format ( "ffmpeg image extraction failed for {0}" , inputArgument ) ;
_logger . Error ( msg ) ;
_logger . Error ( msg ) ;
throw new ApplicationException ( msg ) ;
throw new ApplicationException ( msg ) ;
}
}
}
@ -781,7 +786,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
private class ProcessWrapper
private class ProcessWrapper : IDisposable
{
public readonly Process Process ;
public bool HasExited ;
@ -810,6 +815,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
process . Dispose ( ) ;
}
private bool _disposed ;
private readonly object _syncLock = new object ( ) ;
public void Dispose ( )
{
lock ( _syncLock )
{
if ( ! _disposed )
{
if ( Process ! = null )
{
Process . Exited - = Process_Exited ;
Process . Dispose ( ) ;
}
}
_disposed = true ;
}
}
}
}
}