@ -289,8 +289,10 @@ namespace MediaBrowser.Api.Playback
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
//process.BeginOutputReadLine();
state . TranscodingJob = transcodingJob ;
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
var task = Task . Run ( ( ) = > StartStreamingLog ( transcodingJob , state , process . StandardError . BaseStream , state . LogFileStream ) ) ;
new JobLogger ( Logger ) . StartStreamingLog ( state , process . StandardError . BaseStream , state . LogFileStream ) ;
// Wait for the file to exist before proceeeding
while ( ! FileSystem . FileExists ( state . WaitForPath ? ? outputPath ) & & ! transcodingJob . HasExited )
@ -340,134 +342,6 @@ namespace MediaBrowser.Api.Playback
// string.Equals(GetVideoEncoder(state), "libx264", StringComparison.OrdinalIgnoreCase);
}
private async Task StartStreamingLog ( TranscodingJob transcodingJob , StreamState state , Stream source , Stream target )
{
try
{
using ( var reader = new StreamReader ( source ) )
{
while ( ! reader . EndOfStream )
{
var line = await reader . ReadLineAsync ( ) . ConfigureAwait ( false ) ;
ParseLogLine ( line , transcodingJob , state ) ;
var bytes = Encoding . UTF8 . GetBytes ( Environment . NewLine + line ) ;
await target . WriteAsync ( bytes , 0 , bytes . Length ) . ConfigureAwait ( false ) ;
await target . FlushAsync ( ) . ConfigureAwait ( false ) ;
}
}
}
catch ( ObjectDisposedException )
{
// Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
}
catch ( Exception ex )
{
Logger . ErrorException ( "Error reading ffmpeg log" , ex ) ;
}
}
private void ParseLogLine ( string line , TranscodingJob transcodingJob , StreamState state )
{
float? framerate = null ;
double? percent = null ;
TimeSpan ? transcodingPosition = null ;
long? bytesTranscoded = null ;
int? bitRate = null ;
var parts = line . Split ( ' ' ) ;
var totalMs = state . RunTimeTicks . HasValue
? TimeSpan . FromTicks ( state . RunTimeTicks . Value ) . TotalMilliseconds
: 0 ;
var startMs = state . Request . StartTimeTicks . HasValue
? TimeSpan . FromTicks ( state . Request . StartTimeTicks . Value ) . TotalMilliseconds
: 0 ;
for ( var i = 0 ; i < parts . Length ; i + + )
{
var part = parts [ i ] ;
if ( string . Equals ( part , "fps=" , StringComparison . OrdinalIgnoreCase ) & &
( i + 1 < parts . Length ) )
{
var rate = parts [ i + 1 ] ;
float val ;
if ( float . TryParse ( rate , NumberStyles . Any , UsCulture , out val ) )
{
framerate = val ;
}
}
else if ( state . RunTimeTicks . HasValue & &
part . StartsWith ( "time=" , StringComparison . OrdinalIgnoreCase ) )
{
var time = part . Split ( new [ ] { '=' } , 2 ) . Last ( ) ;
TimeSpan val ;
if ( TimeSpan . TryParse ( time , UsCulture , out val ) )
{
var currentMs = startMs + val . TotalMilliseconds ;
var percentVal = currentMs / totalMs ;
percent = 100 * percentVal ;
transcodingPosition = val ;
}
}
else if ( part . StartsWith ( "size=" , StringComparison . OrdinalIgnoreCase ) )
{
var size = part . Split ( new [ ] { '=' } , 2 ) . Last ( ) ;
int? scale = null ;
if ( size . IndexOf ( "kb" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
{
scale = 1024 ;
size = size . Replace ( "kb" , string . Empty , StringComparison . OrdinalIgnoreCase ) ;
}
if ( scale . HasValue )
{
long val ;
if ( long . TryParse ( size , NumberStyles . Any , UsCulture , out val ) )
{
bytesTranscoded = val * scale . Value ;
}
}
}
else if ( part . StartsWith ( "bitrate=" , StringComparison . OrdinalIgnoreCase ) )
{
var rate = part . Split ( new [ ] { '=' } , 2 ) . Last ( ) ;
int? scale = null ;
if ( rate . IndexOf ( "kbits/s" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
{
scale = 1024 ;
rate = rate . Replace ( "kbits/s" , string . Empty , StringComparison . OrdinalIgnoreCase ) ;
}
if ( scale . HasValue )
{
float val ;
if ( float . TryParse ( rate , NumberStyles . Any , UsCulture , out val ) )
{
bitRate = ( int ) Math . Ceiling ( val * scale . Value ) ;
}
}
}
}
if ( framerate . HasValue | | percent . HasValue )
{
ApiEntryPoint . Instance . ReportTranscodingProgress ( transcodingJob , state , transcodingPosition , framerate , percent , bytesTranscoded , bitRate ) ;
}
}
/// <summary>
/// Processes the exited.
/// </summary>