diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs index 590c099..18ee9dd 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs @@ -10,8 +10,11 @@ namespace FFMpegCore; public class FFMpegArgumentProcessor { private static readonly Regex ProgressRegex = new(@"time=(\d\d:\d\d:\d\d.\d\d?)", RegexOptions.Compiled); + private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly List> _configurations; private readonly FFMpegArguments _ffMpegArguments; + private CancellationTokenRegistration? _cancellationTokenRegistration; + private bool _cancelled; private FFMpegLogLevel? _logLevel; private Action? _onError; private Action? _onOutput; @@ -29,6 +32,12 @@ public class FFMpegArgumentProcessor private event EventHandler CancelEvent = null!; + ~FFMpegArgumentProcessor() + { + _cancellationTokenSource.Dispose(); + _cancellationTokenRegistration?.Dispose(); + } + /// /// Register action that will be invoked during the ffmpeg processing, when a progress time is output and parsed and progress percentage is /// calculated. @@ -71,13 +80,21 @@ public class FFMpegArgumentProcessor public FFMpegArgumentProcessor CancellableThrough(out Action cancel, int timeout = 0) { - cancel = () => CancelEvent?.Invoke(this, timeout); + cancel = () => + { + _cancelled = true; + CancelEvent?.Invoke(this, timeout); + }; return this; } public FFMpegArgumentProcessor CancellableThrough(CancellationToken token, int timeout = 0) { - token.Register(() => CancelEvent?.Invoke(this, timeout)); + _cancellationTokenRegistration = token.Register(() => + { + _cancelled = true; + CancelEvent?.Invoke(this, timeout); + }); return this; } @@ -101,12 +118,12 @@ public class FFMpegArgumentProcessor public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) { var options = GetConfiguredOptions(ffMpegOptions); - var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource); + var processArguments = PrepareProcessArguments(options); IProcessResult? processResult = null; try { - processResult = Process(processArguments, cancellationTokenSource).ConfigureAwait(false).GetAwaiter().GetResult(); + processResult = Process(processArguments).ConfigureAwait(false).GetAwaiter().GetResult(); } catch (OperationCanceledException) { @@ -122,12 +139,12 @@ public class FFMpegArgumentProcessor public async Task ProcessAsynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) { var options = GetConfiguredOptions(ffMpegOptions); - var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource); + var processArguments = PrepareProcessArguments(options); IProcessResult? processResult = null; try { - processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(false); + processResult = await Process(processArguments).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -140,23 +157,25 @@ public class FFMpegArgumentProcessor return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty()); } - private async Task Process(ProcessArguments processArguments, CancellationTokenSource cancellationTokenSource) + private async Task Process(ProcessArguments processArguments) { IProcessResult processResult = null!; + if (_cancelled) + { + throw new OperationCanceledException("cancelled before starting processing"); + } _ffMpegArguments.Pre(); using var instance = processArguments.Start(); - var cancelled = false; void OnCancelEvent(object sender, int timeout) { - cancelled = true; instance.SendInput("q"); - if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true)) + if (!_cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true)) { - cancellationTokenSource.Cancel(); + _cancellationTokenSource.Cancel(); instance.Kill(); } } @@ -168,11 +187,11 @@ public class FFMpegArgumentProcessor await Task.WhenAll(instance.WaitForExitAsync().ContinueWith(t => { processResult = t.Result; - cancellationTokenSource.Cancel(); + _cancellationTokenSource.Cancel(); _ffMpegArguments.Post(); - }), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false); + }), _ffMpegArguments.During(_cancellationTokenSource.Token)).ConfigureAwait(false); - if (cancelled) + if (_cancelled) { throw new OperationCanceledException("ffmpeg processing was cancelled"); } @@ -214,8 +233,7 @@ public class FFMpegArgumentProcessor return options; } - private ProcessArguments PrepareProcessArguments(FFOptions ffOptions, - out CancellationTokenSource cancellationTokenSource) + private ProcessArguments PrepareProcessArguments(FFOptions ffOptions) { FFMpegHelper.RootExceptionCheck(); FFMpegHelper.VerifyFFMpegExists(ffOptions); @@ -245,7 +263,6 @@ public class FFMpegArgumentProcessor WorkingDirectory = ffOptions.WorkingDirectory }; var processArguments = new ProcessArguments(startInfo); - cancellationTokenSource = new CancellationTokenSource(); if (_onOutput != null) {