mirror of
https://github.com/rosenbjerg/FFMpegCore.git
synced 2024-11-10 08:34:12 +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 videoFramesSource = new RawVideoPipeSource(frames);
|
||||||
var ex = Assert.ThrowsException<FFMpegException>(() => FFMpegArguments
|
var ex = Assert.ThrowsException<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||||
.FromPipeInput(videoFramesSource)
|
.FromPipeInput(videoFramesSource)
|
||||||
.OutputToFile(outputFile, false, opt => opt
|
.OutputToFile(outputFile, false, opt => opt
|
||||||
.WithVideoCodec(VideoCodec.LibX264))
|
.WithVideoCodec(VideoCodec.LibX264))
|
||||||
.ProcessSynchronously());
|
.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 videoFramesSource = new RawVideoPipeSource(frames);
|
||||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegException>(() => FFMpegArguments
|
var ex = await Assert.ThrowsExceptionAsync<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||||
.FromPipeInput(videoFramesSource)
|
.FromPipeInput(videoFramesSource)
|
||||||
.OutputToFile(outputFile, false, opt => opt
|
.OutputToFile(outputFile, false, opt => opt
|
||||||
.WithVideoCodec(VideoCodec.LibX264))
|
.WithVideoCodec(VideoCodec.LibX264))
|
||||||
.ProcessAsynchronously());
|
.ProcessAsynchronously());
|
||||||
|
|
||||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
|
@ -156,13 +152,11 @@ public void Video_ToMP4_Args_Pipe_DifferentPixelFormats()
|
||||||
};
|
};
|
||||||
|
|
||||||
var videoFramesSource = new RawVideoPipeSource(frames);
|
var videoFramesSource = new RawVideoPipeSource(frames);
|
||||||
var ex = Assert.ThrowsException<FFMpegException>(() => FFMpegArguments
|
var ex = Assert.ThrowsException<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||||
.FromPipeInput(videoFramesSource)
|
.FromPipeInput(videoFramesSource)
|
||||||
.OutputToFile(outputFile, false, opt => opt
|
.OutputToFile(outputFile, false, opt => opt
|
||||||
.WithVideoCodec(VideoCodec.LibX264))
|
.WithVideoCodec(VideoCodec.LibX264))
|
||||||
.ProcessSynchronously());
|
.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 videoFramesSource = new RawVideoPipeSource(frames);
|
||||||
var ex = await Assert.ThrowsExceptionAsync<FFMpegException>(() => FFMpegArguments
|
var ex = await Assert.ThrowsExceptionAsync<FFMpegStreamFormatException>(() => FFMpegArguments
|
||||||
.FromPipeInput(videoFramesSource)
|
.FromPipeInput(videoFramesSource)
|
||||||
.OutputToFile(outputFile, false, opt => opt
|
.OutputToFile(outputFile, false, opt => opt
|
||||||
.WithVideoCodec(VideoCodec.LibX264))
|
.WithVideoCodec(VideoCodec.LibX264))
|
||||||
.ProcessAsynchronously());
|
.ProcessAsynchronously());
|
||||||
|
|
||||||
Assert.IsInstanceOfType(ex.GetBaseException(), typeof(FFMpegStreamFormatException));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod, Timeout(10000)]
|
[TestMethod, Timeout(10000)]
|
||||||
|
@ -596,6 +588,27 @@ public async Task Video_Cancel_Async()
|
||||||
Assert.IsFalse(result);
|
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)]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Video_Cancel_Async_With_Timeout()
|
public async Task Video_Cancel_Async_With_Timeout()
|
||||||
{
|
{
|
||||||
|
@ -615,11 +628,10 @@ public async Task Video_Cancel_Async_With_Timeout()
|
||||||
await Task.Delay(300);
|
await Task.Delay(300);
|
||||||
cancel();
|
cancel();
|
||||||
|
|
||||||
var result = await task;
|
await task;
|
||||||
|
|
||||||
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
||||||
|
|
||||||
Assert.IsTrue(result);
|
|
||||||
Assert.IsNotNull(outputInfo);
|
Assert.IsNotNull(outputInfo);
|
||||||
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
||||||
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
|
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
|
||||||
|
@ -645,14 +657,58 @@ public async Task Video_Cancel_CancellationToken_Async()
|
||||||
.CancellableThrough(cts.Token)
|
.CancellableThrough(cts.Token)
|
||||||
.ProcessAsynchronously(false);
|
.ProcessAsynchronously(false);
|
||||||
|
|
||||||
await Task.Delay(300);
|
cts.CancelAfter(300);
|
||||||
cts.Cancel();
|
|
||||||
|
|
||||||
var result = await task;
|
var result = await task;
|
||||||
|
|
||||||
Assert.IsFalse(result);
|
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)]
|
[TestMethod, Timeout(10000)]
|
||||||
public async Task Video_Cancel_CancellationToken_Async_With_Timeout()
|
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)
|
.CancellableThrough(cts.Token, 8000)
|
||||||
.ProcessAsynchronously(false);
|
.ProcessAsynchronously(false);
|
||||||
|
|
||||||
await Task.Delay(300);
|
cts.CancelAfter(300);
|
||||||
cts.Cancel();
|
|
||||||
|
|
||||||
var result = await task;
|
await task;
|
||||||
|
|
||||||
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
var outputInfo = await FFProbe.AnalyseAsync(outputFile);
|
||||||
|
|
||||||
Assert.IsTrue(result);
|
|
||||||
Assert.IsNotNull(outputInfo);
|
Assert.IsNotNull(outputInfo);
|
||||||
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
Assert.AreEqual(320, outputInfo.PrimaryVideoStream!.Width);
|
||||||
Assert.AreEqual(240, outputInfo.PrimaryVideoStream.Height);
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
|
@ -23,7 +24,7 @@ protected override async Task ProcessDataAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
await Pipe.WaitForConnectionAsync(token).ConfigureAwait(false);
|
||||||
if (!Pipe.IsConnected)
|
if (!Pipe.IsConnected)
|
||||||
throw new TaskCanceledException();
|
throw new OperationCanceledException();
|
||||||
await Writer.WriteAsync(Pipe, token).ConfigureAwait(false);
|
await Writer.WriteAsync(Pipe, token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,14 +42,15 @@ public async Task During(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await ProcessDataAsync(cancellationToken).ConfigureAwait(false);
|
await ProcessDataAsync(cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"ProcessDataAsync on {GetType().Name} cancelled");
|
Debug.WriteLine($"ProcessDataAsync on {GetType().Name} cancelled");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Disconnecting NamedPipeServerStream on {GetType().Name}");
|
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 options = GetConfiguredOptions(ffMpegOptions);
|
||||||
var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource);
|
var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource);
|
||||||
processArguments.Exited += delegate { cancellationTokenSource.Cancel(); };
|
|
||||||
|
|
||||||
IProcessResult? processResult = null;
|
IProcessResult? processResult = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
processResult = Process(processArguments, cancellationTokenSource).ConfigureAwait(false).GetAwaiter().GetResult();
|
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>());
|
return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty<string>());
|
||||||
|
@ -112,9 +113,10 @@ public async Task<bool> ProcessAsynchronously(bool throwOnError = true, FFOption
|
||||||
{
|
{
|
||||||
processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(false);
|
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>());
|
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();
|
_ffMpegArguments.Pre();
|
||||||
|
|
||||||
using var instance = processArguments.Start();
|
using var instance = processArguments.Start();
|
||||||
|
var cancelled = false;
|
||||||
void OnCancelEvent(object sender, int timeout)
|
void OnCancelEvent(object sender, int timeout)
|
||||||
{
|
{
|
||||||
|
cancelled = true;
|
||||||
instance.SendInput("q");
|
instance.SendInput("q");
|
||||||
|
|
||||||
if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true))
|
if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true))
|
||||||
|
@ -148,6 +152,11 @@ await Task.WhenAll(instance.WaitForExitAsync().ContinueWith(t =>
|
||||||
_ffMpegArguments.Post();
|
_ffMpegArguments.Post();
|
||||||
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
}), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (cancelled)
|
||||||
|
{
|
||||||
|
throw new OperationCanceledException("ffmpeg processing was cancelled");
|
||||||
|
}
|
||||||
|
|
||||||
return processResult;
|
return processResult;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -209,15 +218,6 @@ private void ErrorData(object sender, string msg)
|
||||||
_onError?.Invoke(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)
|
private void OutputData(object sender, string msg)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(msg);
|
Debug.WriteLine(msg);
|
||||||
|
|
Loading…
Reference in a new issue