From 3efc34565a58523299a32069186467972012b491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Kr=C3=A4mer?= Date: Wed, 7 Jul 2021 20:16:14 +0200 Subject: [PATCH 1/5] Support cancellation token for cancelling FFMPEG processes --- FFMpegCore.Test/VideoTest.cs | 60 ++++++++++++++++++++ FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs | 5 ++ 2 files changed, 65 insertions(+) diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 149dabd..2831084 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -12,6 +12,7 @@ using FFMpegCore.Arguments; using FFMpegCore.Exceptions; using FFMpegCore.Pipes; +using System.Threading; namespace FFMpegCore.Test { @@ -612,5 +613,64 @@ public async Task Video_Cancel_Async_With_Timeout() Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName); Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName); } + + [TestMethod, Timeout(10000)] + public async Task Video_Cancel_CancellationToken_Async() + { + 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(false); + + await Task.Delay(300); + cts.Cancel(); + + var result = await task; + + Assert.IsFalse(result); + } + + [TestMethod, Timeout(10000)] + public async Task Video_Cancel_CancellationToken_Async_With_Timeout() + { + 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, 10000) + .ProcessAsynchronously(false); + + await Task.Delay(300); + cts.Cancel(); + + var result = 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); + Assert.AreEqual("h264", outputInfo.PrimaryVideoStream.CodecName); + Assert.AreEqual("aac", outputInfo.PrimaryAudioStream!.CodecName); + } } } diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs index 67607af..060ffc3 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs @@ -50,6 +50,11 @@ public FFMpegArgumentProcessor CancellableThrough(out Action cancel, int timeout cancel = () => CancelEvent?.Invoke(this, timeout); return this; } + public FFMpegArgumentProcessor CancellableThrough(CancellationToken token, int timeout = 0) + { + token.Register(() => CancelEvent?.Invoke(this, timeout)); + return this; + } public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) { using var instance = PrepareInstance(ffMpegOptions ?? GlobalFFOptions.Current, out var cancellationTokenSource); From ab82e3cc0fe90bc7bb439479e1e568c91b98b5a6 Mon Sep 17 00:00:00 2001 From: cephei Date: Thu, 8 Jul 2021 20:17:09 +0800 Subject: [PATCH 2/5] support specific StandardOutputEncoding & StandardErrorEncoding for ffprobe --- FFMpegCore/FFProbe/FFProbe.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index ab35457..6fa16a1 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Text.Json; using System.Threading.Tasks; @@ -117,7 +118,13 @@ private static Instance PrepareInstance(string filePath, int outputCapacity, FFO FFProbeHelper.RootExceptionCheck(); FFProbeHelper.VerifyFFProbeExists(ffOptions); var arguments = $"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\""; - var instance = new Instance(GlobalFFOptions.GetFFProbeBinaryPath(), arguments) {DataBufferCapacity = outputCapacity}; + var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments) + { + StandardOutputEncoding = ffOptions.Encoding, + StandardErrorEncoding = ffOptions.Encoding + }; + var instance = new Instance(startInfo) + { DataBufferCapacity = outputCapacity }; return instance; } } From 27fb37700c743b705ae705523318f01a0e280e46 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 16 Jul 2021 01:02:38 +0200 Subject: [PATCH 3/5] Improve ffprobe exceptions --- FFMpegCore/FFProbe/Exceptions/FFProbeException.cs | 11 +++++++++++ .../FFProbe/Exceptions/FFProbeProcessException.cs | 15 +++++++++++++++ .../FFProbe/Exceptions/FormatNullException.cs | 9 +++++++++ FFMpegCore/FFProbe/FFProbe.cs | 7 +++---- FFMpegCore/Helpers/FFProbeHelper.cs | 2 +- 5 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 FFMpegCore/FFProbe/Exceptions/FFProbeException.cs create mode 100644 FFMpegCore/FFProbe/Exceptions/FFProbeProcessException.cs create mode 100644 FFMpegCore/FFProbe/Exceptions/FormatNullException.cs diff --git a/FFMpegCore/FFProbe/Exceptions/FFProbeException.cs b/FFMpegCore/FFProbe/Exceptions/FFProbeException.cs new file mode 100644 index 0000000..3495193 --- /dev/null +++ b/FFMpegCore/FFProbe/Exceptions/FFProbeException.cs @@ -0,0 +1,11 @@ +using System; + +namespace FFMpegCore.Exceptions +{ + public class FFProbeException : Exception + { + public FFProbeException(string message, Exception? inner = null) : base(message, inner) + { + } + } +} \ No newline at end of file diff --git a/FFMpegCore/FFProbe/Exceptions/FFProbeProcessException.cs b/FFMpegCore/FFProbe/Exceptions/FFProbeProcessException.cs new file mode 100644 index 0000000..5ab6b93 --- /dev/null +++ b/FFMpegCore/FFProbe/Exceptions/FFProbeProcessException.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace FFMpegCore.Exceptions +{ + public class FFProbeProcessException : FFProbeException + { + public IReadOnlyCollection ProcessErrors { get; } + + public FFProbeProcessException(string message, IReadOnlyCollection processErrors, Exception? inner = null) : base(message, inner) + { + ProcessErrors = processErrors; + } + } +} \ No newline at end of file diff --git a/FFMpegCore/FFProbe/Exceptions/FormatNullException.cs b/FFMpegCore/FFProbe/Exceptions/FormatNullException.cs new file mode 100644 index 0000000..4141f5f --- /dev/null +++ b/FFMpegCore/FFProbe/Exceptions/FormatNullException.cs @@ -0,0 +1,9 @@ +namespace FFMpegCore.Exceptions +{ + public class FormatNullException : FFProbeException + { + public FormatNullException() : base("Format not specified") + { + } + } +} \ No newline at end of file diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 6fa16a1..7d043a6 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -93,7 +93,7 @@ public static async Task AnalyseAsync(Stream stream, int outputC } var exitCode = await task.ConfigureAwait(false); if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"FFProbe process returned exit status {exitCode}", null, string.Join("\n", instance.ErrorData)); + throw new FFProbeProcessException($"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", instance.ErrorData); pipeArgument.Post(); return ParseOutput(instance); @@ -108,7 +108,7 @@ private static IMediaAnalysis ParseOutput(Instance instance) }); if (ffprobeAnalysis?.Format == null) - throw new Exception(); + throw new FormatNullException(); return new MediaAnalysis(ffprobeAnalysis); } @@ -123,8 +123,7 @@ private static Instance PrepareInstance(string filePath, int outputCapacity, FFO StandardOutputEncoding = ffOptions.Encoding, StandardErrorEncoding = ffOptions.Encoding }; - var instance = new Instance(startInfo) - { DataBufferCapacity = outputCapacity }; + var instance = new Instance(startInfo) { DataBufferCapacity = outputCapacity }; return instance; } } diff --git a/FFMpegCore/Helpers/FFProbeHelper.cs b/FFMpegCore/Helpers/FFProbeHelper.cs index d0064e4..4989542 100644 --- a/FFMpegCore/Helpers/FFProbeHelper.cs +++ b/FFMpegCore/Helpers/FFProbeHelper.cs @@ -30,7 +30,7 @@ public static void VerifyFFProbeExists(FFOptions ffMpegOptions) var (exitCode, _) = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version"); _ffprobeVerified = exitCode == 0; if (!_ffprobeVerified) - throw new FFMpegException(FFMpegExceptionType.Operation, "ffprobe was not found on your system"); + throw new FFProbeException("ffprobe was not found on your system"); } } } From 0c9c526e2a201923140d51755dea2837074d8c7a Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 16 Jul 2021 01:10:54 +0200 Subject: [PATCH 4/5] Update nuget info --- FFMpegCore/FFMpegCore.csproj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index c8fa692..dd96a3d 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -9,10 +9,11 @@ 3.0.0.0 3.0.0.0 3.0.0.0 - - Added support for PCM audio through RawAudioPipeSource (thanks to Namaneo) -- Removed -y in InputPipeArgument due to reported problems + - Cancellation token support (thanks patagonaa) +- Support for setting stdout and stderr encoding for ffprobe (thanks CepheiSigma) +- Improved ffprobe exceptions 8 - 4.3.0 + 4.4.0 MIT Malte Rosenbjerg, Vlad Jerca, Max Bagryantsev ffmpeg ffprobe convert video audio mediafile resize analyze muxing From 587c453a7eaf06fa6c220bd2643d7333589ba489 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Fri, 16 Jul 2021 01:17:20 +0200 Subject: [PATCH 5/5] Reduce timeout in cancellation token test --- FFMpegCore.Test/VideoTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 2831084..45853ea 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -655,7 +655,7 @@ public async Task Video_Cancel_CancellationToken_Async_With_Timeout() .WithAudioCodec(AudioCodec.Aac) .WithVideoCodec(VideoCodec.LibX264) .WithSpeedPreset(Speed.VeryFast)) - .CancellableThrough(cts.Token, 10000) + .CancellableThrough(cts.Token, 5000) .ProcessAsynchronously(false); await Task.Delay(300);