diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs
index 149dabd..45853ea 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, 5000)
+ .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);
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
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 ab35457..7d043a6 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;
@@ -92,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);
@@ -107,7 +108,7 @@ private static IMediaAnalysis ParseOutput(Instance instance)
});
if (ffprobeAnalysis?.Format == null)
- throw new Exception();
+ throw new FormatNullException();
return new MediaAnalysis(ffprobeAnalysis);
}
@@ -117,7 +118,12 @@ 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;
}
}
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");
}
}
}