Skip to content

Commit

Permalink
Cancel ffmpeg (#11)
Browse files Browse the repository at this point in the history
* Bug fix for issue #8: Cancel does not cancel ffmpeg.exe.
See #8.
When the calling program cancels the task by invoking Cancel() on the CancellationToken, send "q" to standard input of the ffmpeg.exe process.
This causes ffmpeg.exe to stop processing and shut down in an orderly way.
  • Loading branch information
dm413 authored and cmxl committed Apr 26, 2019
1 parent d1f510e commit e922f37
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 39 deletions.
91 changes: 58 additions & 33 deletions src/FFmpeg.NET/Extensions/ProcessExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,58 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace FFmpeg.NET.Extensions
{
public static class ProcessExtensions
{
public static Task<int> WaitForExitAsync(this Process process, Action<int> onException, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<int>();
if (cancellationToken != default)
cancellationToken.Register(tcs.SetCanceled);

process.EnableRaisingEvents = true;
process.Exited += (sender, e) =>
{
if (process.ExitCode != 0)
onException?.Invoke(process.ExitCode);
tcs.TrySetResult(process.ExitCode);
};

var started = process.Start();
if (!started)
tcs.TrySetException(new InvalidOperationException($"Could not start process {process}"));

process.BeginErrorReadLine();

return tcs.Task;
}
}
}
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace FFmpeg.NET.Extensions
{
public static class ProcessExtensions
{
public static Task<int> WaitForExitAsync(this Process process, Action<int> onException, CancellationToken cancellationToken = default)
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
if (cancellationToken != default)
{
cancellationToken.Register(() =>
{
try
{
// Send "q" to ffmpeg, which will force it to stop (closing files).
process.StandardInput.Write("q");
}
catch (InvalidOperationException)
{
// If the process doesn't exist anymore, ignore it.
}
finally
{
// Cancel the task. This will throw an exception to the calling program.
// Exc.Message will be "A task was canceled."
try
{
tcs.SetCanceled();
}
catch (Exception)
{
}
}
});
}

process.EnableRaisingEvents = true;
process.Exited += (sender, e) =>
{
if (process.ExitCode != 0)
onException?.Invoke(process.ExitCode);
tcs.TrySetResult(process.ExitCode);
};

var started = process.Start();
if (!started)
tcs.TrySetException(new InvalidOperationException($"Could not start process {process}"));

process.BeginErrorReadLine();

return tcs.Task;
}
}
}
29 changes: 23 additions & 6 deletions src/FFmpeg.NET/FFmpegProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,24 @@ private async Task ExecuteAsync(ProcessStartInfo startInfo, FFmpegParameters par
ffmpegProcess.ErrorDataReceived += (sender, e) => OnData(new ConversionDataEventArgs(e.Data, parameters.InputFile, parameters.OutputFile));
ffmpegProcess.ErrorDataReceived += (sender, e) => FFmpegProcessOnErrorDataReceived(e, parameters, ref caughtException, messages);

await ffmpegProcess.WaitForExitAsync((exitCode) => OnException(messages, parameters, exitCode, caughtException), cancellationToken);

Task<int> task = null;
try
{
task = ffmpegProcess.WaitForExitAsync((exitCode) => OnException(messages, parameters, exitCode, caughtException), cancellationToken);
await task;
}
catch (Exception)
{
// An exception occurs if the user cancels the operation by calling Cancel on the CancellationToken.
// Exc.Message will be "A task was canceled." (in English).
// task.IsCanceled will be true.
if (task.IsCanceled)
{
throw new TaskCanceledException(task);
}
// I don't think this can occur, but if some other exception, rethrow it.
throw;
}
if (caughtException != null || ffmpegProcess.ExitCode != 0)
{
OnException(messages, parameters, ffmpegProcess.ExitCode, caughtException);
Expand All @@ -49,7 +65,7 @@ private void OnException(List<string> messages, FFmpegParameters parameters, int
OnConversionError(new ConversionErrorEventArgs(exception, parameters.InputFile, parameters.OutputFile));
}

private string GetExceptionMessage(List<string> messages)
private string GetExceptionMessage(List<string> messages)
=> messages.Count > 1
? messages[1] + messages[0]
: string.Join(string.Empty, messages);
Expand Down Expand Up @@ -93,10 +109,11 @@ private void FFmpegProcessOnErrorDataReceived(DataReceivedEventArgs e, FFmpegPar

private ProcessStartInfo GenerateStartInfo(string ffmpegPath, string arguments) => new ProcessStartInfo
{
Arguments = "-nostdin -y -loglevel info " + arguments,
// -y overwrite output files
Arguments = "-y " + arguments,
FileName = ffmpegPath,
CreateNoWindow = true,
RedirectStandardInput = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
Expand All @@ -116,4 +133,4 @@ private void FFmpegProcessOnErrorDataReceived(DataReceivedEventArgs e, FFmpegPar

private void OnData(ConversionDataEventArgs eventArgs) => Data?.Invoke(eventArgs);
}
}
}

0 comments on commit e922f37

Please sign in to comment.