mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 00:24:14 +01:00
Merge pull request #321 from rosenbjerg/feature/throw-operationcancelledexception-on-cancellation
Throw OperationCanceledException when processing is cancelled
Former-commit-id: d64c9f4ced
This commit is contained in:
commit
e3af05f150
4 changed files with 96 additions and 40 deletions
|
@ -113,13 +113,11 @@ public void Video_ToMP4_Args_Pipe_DifferentImageSizes()
|
|||
};
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(frames);
|
||||
var ex = Assert.ThrowsException<FFMpegException>(() => FFMpegArguments
|
||||
var ex = Assert.ThrowsException<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously());
|
||||
|
||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
||||
}
|
||||
|
||||
|
||||
|
@ -135,13 +133,11 @@ public async Task Video_ToMP4_Args_Pipe_DifferentImageSizes_Async()
|
|||
};
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(frames);
|
||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegException>(() => FFMpegArguments
|
||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessAsynchronously());
|
||||
|
||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
@ -156,13 +152,11 @@ public void Video_ToMP4_Args_Pipe_DifferentPixelFormats()
|
|||
};
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(frames);
|
||||
var ex = Assert.ThrowsException<FFMpegException>(() => FFMpegArguments
|
||||
var ex = Assert.ThrowsException<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessSynchronously());
|
||||
|
||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
||||
}
|
||||
|
||||
|
||||
|
@ -178,13 +172,11 @@ public async Task Video_ToMP4_Args_Pipe_DifferentPixelFormats_Async()
|
|||
};
|
||||
|
||||
var videoFramesSource = new RawVideoPipeSource(frames);
|
||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegException>(() => FFMpegArguments
|
||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||
.FromPipeInput(videoFramesSource)
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithVideoCodec(VideoCodec.LibX264))
|
||||
.ProcessAsynchronously());
|
||||
|
||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
|
@ -596,6 +588,27 @@ public async Task Video_Cancel_Async()
|
|||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_Cancel()
|
||||
{
|
||||
var outputFile = new TemporaryFile("out.mp4");
|
||||
var task = FFMpegArguments
|
||||
.FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args
|
||||
.WithCustomArgument("-re")
|
||||
.ForceFormat("lavfi"))
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithAudioCodec(AudioCodec.Aac)
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.WithSpeedPreset(Speed.VeryFast))
|
||||
.CancellableThrough(out var cancel);
|
||||
|
||||
Task.Delay(300).ContinueWith((_) => cancel());
|
||||
|
||||
var result = task.ProcessSynchronously(false);
|
||||
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public async Task Video_Cancel_Async_With_Timeout()
|
||||
{
|
||||
|
@ -615,11 +628,10 @@ public async Task Video_Cancel_Async_With_Timeout()
|
|||
await Task.Delay(300);
|
||||
cancel();
|
||||
|
||||
var result = await task;
|
||||
await task;
|
||||
|
||||
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
||||
|
||||
Assert.IsTrue(result);
|
||||
Assert.IsNotNull(outputInfo);
|
||||
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
||||
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
|
||||
|
@ -645,14 +657,58 @@ public async Task Video_Cancel_CancellationToken_Async()
|
|||
.CancellableThrough(cts.Token)
|
||||
.ProcessAsynchronously(false);
|
||||
|
||||
await Task.Delay(300);
|
||||
cts.Cancel();
|
||||
cts.CancelAfter(300);
|
||||
|
||||
var result = await task;
|
||||
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public async Task Video_Cancel_CancellationToken_Async_Throws()
|
||||
{
|
||||
var outputFile = new TemporaryFile("out.mp4");
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var task = FFMpegArguments
|
||||
.FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args
|
||||
.WithCustomArgument("-re")
|
||||
.ForceFormat("lavfi"))
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithAudioCodec(AudioCodec.Aac)
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.WithSpeedPreset(Speed.VeryFast))
|
||||
.CancellableThrough(cts.Token)
|
||||
.ProcessAsynchronously();
|
||||
|
||||
cts.CancelAfter(300);
|
||||
|
||||
await Assert.ThrowsExceptionAsync<OperationCanceledException>(() => task);
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public void Video_Cancel_CancellationToken_Throws()
|
||||
{
|
||||
var outputFile = new TemporaryFile("out.mp4");
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var task = FFMpegArguments
|
||||
.FromFileInput("testsrc2=size=320x240[out0]; sine[out1]", false, args => args
|
||||
.WithCustomArgument("-re")
|
||||
.ForceFormat("lavfi"))
|
||||
.OutputToFile(outputFile, false, opt => opt
|
||||
.WithAudioCodec(AudioCodec.Aac)
|
||||
.WithVideoCodec(VideoCodec.LibX264)
|
||||
.WithSpeedPreset(Speed.VeryFast))
|
||||
.CancellableThrough(cts.Token);
|
||||
|
||||
cts.CancelAfter(300);
|
||||
|
||||
Assert.ThrowsException<OperationCanceledException>(() => task.ProcessSynchronously());
|
||||
}
|
||||
|
||||
[TestMethod, Timeout(10000)]
|
||||
public async Task Video_Cancel_CancellationToken_Async_With_Timeout()
|
||||
{
|
||||
|
@ -671,14 +727,12 @@ public async Task Video_Cancel_CancellationToken_Async_With_Timeout()
|
|||
.CancellableThrough(cts.Token, 8000)
|
||||
.ProcessAsynchronously(false);
|
||||
|
||||
await Task.Delay(300);
|
||||
cts.Cancel();
|
||||
cts.CancelAfter(300);
|
||||
|
||||
var result = await task;
|
||||
await task;
|
||||
|
||||
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
||||
|
||||
Assert.IsTrue(result);
|
||||
Assert.IsNotNull(outputInfo);
|
||||
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
||||
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO.Pipes;
|
||||
using System;
|
||||
using System.IO.Pipes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FFMpegCore.Pipes;
|
||||
|
@ -23,7 +24,7 @@ protected override async Task ProcessDataAsync(CancellationToken token)
|
|||
{
|
||||
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
||||
if (!Pipe.IsConnected)
|
||||
throw new TaskCanceledException();
|
||||
throw new OperationCanceledException();
|
||||
await Writer.WriteAsync(Pipe, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,14 +42,15 @@ public async Task During(CancellationToken cancellationToken = default)
|
|||
{
|
||||
await ProcessDataAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Debug.WriteLine($"ProcessDataAsync on {GetType().Name} cancelled");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Debug.WriteLine($"Disconnecting NamedPipeServerStream on {GetType().Name}");
|
||||
Pipe?.Disconnect();
|
||||
if (Pipe is { IsConnected: true })
|
||||
Pipe.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,16 +87,17 @@ public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOpti
|
|||
{
|
||||
var options = GetConfiguredOptions(ffMpegOptions);
|
||||
var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource);
|
||||
processArguments.Exited += delegate { cancellationTokenSource.Cancel(); };
|
||||
|
||||
|
||||
IProcessResult? processResult = null;
|
||||
try
|
||||
{
|
||||
processResult = Process(processArguments, cancellationTokenSource).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (!HandleException(throwOnError, e, processResult?.ErrorData ?? Array.Empty<string>())) return false;
|
||||
if (throwOnError)
|
||||
throw;
|
||||
}
|
||||
|
||||
return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty<string>());
|
||||
|
@ -106,17 +107,18 @@ public async Task<bool> ProcessAsynchronously(bool throwOnError = true, FFOption
|
|||
{
|
||||
var options = GetConfiguredOptions(ffMpegOptions);
|
||||
var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource);
|
||||
|
||||
|
||||
IProcessResult? processResult = null;
|
||||
try
|
||||
{
|
||||
processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (!HandleException(throwOnError, e, processResult?.ErrorData ?? Array.Empty<string>())) return false;
|
||||
if (throwOnError)
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty<string>());
|
||||
}
|
||||
|
||||
|
@ -127,8 +129,10 @@ private async Task<IProcessResult> Process(ProcessArguments processArguments, Ca
|
|||
_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))
|
||||
|
@ -148,6 +152,11 @@ await Task.WhenAll(instance.WaitForExitAsync().ContinueWith(t =>
|
|||
_ffMpegArguments.Post();
|
||||
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
throw new OperationCanceledException("ffmpeg processing was cancelled");
|
||||
}
|
||||
|
||||
return processResult;
|
||||
}
|
||||
finally
|
||||
|
@ -209,15 +218,6 @@ private void ErrorData(object sender, string msg)
|
|||
_onError?.Invoke(msg);
|
||||
}
|
||||
|
||||
|
||||
private static bool HandleException(bool throwOnError, Exception e, IEnumerable<string> errorData)
|
||||
{
|
||||
if (!throwOnError)
|
||||
return false;
|
||||
|
||||
throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData));
|
||||
}
|
||||
|
||||
private void OutputData(object sender, string msg)
|
||||
{
|
||||
Debug.WriteLine(msg);
|
||||
|
|
Loading…
Reference in a new issue