From 1d2c0f760b56e48b3313540254ad5e3c4a1fac95 Mon Sep 17 00:00:00 2001 From: Notheisz57 Date: Sat, 29 Jan 2022 19:29:45 -0800 Subject: [PATCH 1/3] Update FFProbe.cs Pass FFOptions argument in call to GlobalFFOptions.GetFFProbeBinaryPath --- FFMpegCore/FFProbe/FFProbe.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 36f050c..4418371 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -201,7 +201,7 @@ private static Instance PrepareInstance(string arguments, int outputCapacity, FF { FFProbeHelper.RootExceptionCheck(); FFProbeHelper.VerifyFFProbeExists(ffOptions); - var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(), arguments) + var startInfo = new ProcessStartInfo(GlobalFFOptions.GetFFProbeBinaryPath(ffOptions), arguments) { StandardOutputEncoding = ffOptions.Encoding, StandardErrorEncoding = ffOptions.Encoding, From 7f8bd2405860b4e8d53ec276fc48628dabd78649 Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Sat, 12 Mar 2022 19:06:46 +0100 Subject: [PATCH 2/3] Init --- FFMpegCore.Test/FFProbeTests.cs | 7 - FFMpegCore.Test/VideoTest.cs | 2 +- FFMpegCore/FFMpeg/FFMpeg.cs | 32 ++-- FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs | 119 +++++++-------- FFMpegCore/FFMpegCore.csproj | 4 +- FFMpegCore/FFProbe/FFProbe.cs | 139 +++++++++--------- .../FFProbe/ProcessArgumentsExtensions.cs | 20 +++ FFMpegCore/Helpers/FFMpegHelper.cs | 4 +- FFMpegCore/Helpers/FFProbeHelper.cs | 4 +- 9 files changed, 174 insertions(+), 157 deletions(-) create mode 100644 FFMpegCore/FFProbe/ProcessArgumentsExtensions.cs diff --git a/FFMpegCore.Test/FFProbeTests.cs b/FFMpegCore.Test/FFProbeTests.cs index c2e6e5a..a4d836d 100644 --- a/FFMpegCore.Test/FFProbeTests.cs +++ b/FFMpegCore.Test/FFProbeTests.cs @@ -11,13 +11,6 @@ namespace FFMpegCore.Test [TestClass] public class FFProbeTests { - [TestMethod] - public void Probe_TooLongOutput() - { - Assert.ThrowsException(() => FFProbe.Analyse(TestResources.Mp4Video, 5)); - } - - [TestMethod] public async Task Audio_FromStream_Duration() { diff --git a/FFMpegCore.Test/VideoTest.cs b/FFMpegCore.Test/VideoTest.cs index 0f806d6..56579e9 100644 --- a/FFMpegCore.Test/VideoTest.cs +++ b/FFMpegCore.Test/VideoTest.cs @@ -544,7 +544,7 @@ public void Video_OutputsData() .WithVerbosityLevel(VerbosityLevel.Info)) .OutputToFile(outputFile, false, opt => opt .WithDuration(TimeSpan.FromSeconds(2))) - .NotifyOnOutput((_, _) => dataReceived = true) + .NotifyOnError(_ => dataReceived = true) .ProcessSynchronously(); Assert.IsTrue(dataReceived); diff --git a/FFMpegCore/FFMpeg/FFMpeg.cs b/FFMpegCore/FFMpeg/FFMpeg.cs index 6f0ede3..d227a2e 100644 --- a/FFMpegCore/FFMpeg/FFMpeg.cs +++ b/FFMpegCore/FFMpeg/FFMpeg.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Instances; namespace FFMpegCore { @@ -417,15 +418,16 @@ internal static IReadOnlyList GetPixelFormatsInternal() FFMpegHelper.RootExceptionCheck(); var list = new List(); - using var instance = new Instances.Instance(GlobalFFOptions.GetFFMpegBinaryPath(), "-pix_fmts"); - instance.DataReceived += (e, args) => + var processArguments = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), "-pix_fmts"); + processArguments.OutputDataReceived += (e, data) => { - if (PixelFormat.TryParse(args.Data, out var format)) + if (PixelFormat.TryParse(data, out var format)) list.Add(format); }; - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + var result = processArguments.StartAndWaitForExit(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData)); return list.AsReadOnly(); } @@ -462,10 +464,10 @@ private static void ParsePartOfCodecs(Dictionary codecs, string a { FFMpegHelper.RootExceptionCheck(); - using var instance = new Instances.Instance(GlobalFFOptions.GetFFMpegBinaryPath(), arguments); - instance.DataReceived += (e, args) => + var processArguments = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), arguments); + processArguments.OutputDataReceived += (e, data) => { - var codec = parser(args.Data); + var codec = parser(data); if(codec != null) if (codecs.TryGetValue(codec.Name, out var parentCodec)) parentCodec.Merge(codec); @@ -473,8 +475,8 @@ private static void ParsePartOfCodecs(Dictionary codecs, string a codecs.Add(codec.Name, codec); }; - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + var result = processArguments.StartAndWaitForExit(); + if (result.ExitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData)); } internal static Dictionary GetCodecsInternal() @@ -546,15 +548,15 @@ internal static IReadOnlyList GetContainersFormatsInternal() FFMpegHelper.RootExceptionCheck(); var list = new List(); - using var instance = new Instances.Instance(GlobalFFOptions.GetFFMpegBinaryPath(), "-formats"); - instance.DataReceived += (e, args) => + var instance = new ProcessArguments(GlobalFFOptions.GetFFMpegBinaryPath(), "-formats"); + instance.OutputDataReceived += (e, data) => { - if (ContainerFormat.TryParse(args.Data, out var fmt)) + if (ContainerFormat.TryParse(data, out var fmt)) list.Add(fmt); }; - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", instance.OutputData)); + var result = instance.StartAndWaitForExit(); + if (result.ExitCode != 0) throw new FFMpegException(FFMpegExceptionType.Process, string.Join("\r\n", result.OutputData)); return list.AsReadOnly(); } diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs index fdbdcc8..34ccc59 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentProcessor.cs @@ -18,7 +18,8 @@ public class FFMpegArgumentProcessor private readonly FFMpegArguments _ffMpegArguments; private Action? _onPercentageProgress; private Action? _onTimeProgress; - private Action? _onOutput; + private Action? _onOutput; + private Action? _onError; private TimeSpan? _totalTimespan; internal FFMpegArgumentProcessor(FFMpegArguments ffMpegArguments) @@ -57,11 +58,16 @@ public FFMpegArgumentProcessor NotifyOnProgress(Action onTimeProgress) /// Register action that will be invoked during the ffmpeg processing, when a line is output /// /// - public FFMpegArgumentProcessor NotifyOnOutput(Action onOutput) + public FFMpegArgumentProcessor NotifyOnOutput(Action onOutput) { _onOutput = onOutput; return this; } + public FFMpegArgumentProcessor NotifyOnError(Action onError) + { + _onError = onError; + return this; + } public FFMpegArgumentProcessor CancellableThrough(out Action cancel, int timeout = 0) { cancel = () => CancelEvent?.Invoke(this, timeout); @@ -80,43 +86,47 @@ public FFMpegArgumentProcessor Configure(Action configureOptions) public bool ProcessSynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) { var options = GetConfiguredOptions(ffMpegOptions); - using var instance = PrepareInstance(options, out var cancellationTokenSource); + var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource); + processArguments.Exited += delegate { cancellationTokenSource.Cancel(); }; - void OnCancelEvent(object sender, int timeout) - { - instance.SendInput("q"); - - if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true)) - { - cancellationTokenSource.Cancel(); - instance.Started = false; - } - } - CancelEvent += OnCancelEvent; - instance.Exited += delegate { cancellationTokenSource.Cancel(); }; - - var errorCode = -1; + IProcessResult? processResult = null; try { - errorCode = Process(instance, cancellationTokenSource).ConfigureAwait(false).GetAwaiter().GetResult(); + processResult = Process(processArguments, cancellationTokenSource).ConfigureAwait(false).GetAwaiter().GetResult(); } catch (Exception e) { - if (!HandleException(throwOnError, e, instance.ErrorData)) return false; - } - finally - { - CancelEvent -= OnCancelEvent; + if (!HandleException(throwOnError, e, processResult?.ErrorData ?? Array.Empty())) return false; } - return HandleCompletion(throwOnError, errorCode, instance.ErrorData); + return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty()); } public async Task ProcessAsynchronously(bool throwOnError = true, FFOptions? ffMpegOptions = null) { var options = GetConfiguredOptions(ffMpegOptions); - using var instance = PrepareInstance(options, out var cancellationTokenSource); + var processArguments = PrepareProcessArguments(options, out var cancellationTokenSource); + IProcessResult? processResult = null; + try + { + processResult = await Process(processArguments, cancellationTokenSource).ConfigureAwait(false); + } + catch (Exception e) + { + if (!HandleException(throwOnError, e, processResult?.ErrorData ?? Array.Empty())) return false; + } + + return HandleCompletion(throwOnError, processResult?.ExitCode ?? -1, processResult?.ErrorData ?? Array.Empty()); + } + + private async Task Process(ProcessArguments processArguments, CancellationTokenSource cancellationTokenSource) + { + IProcessResult processResult = null!; + + _ffMpegArguments.Pre(); + + using var instance = processArguments.Start(); void OnCancelEvent(object sender, int timeout) { instance.SendInput("q"); @@ -124,41 +134,26 @@ void OnCancelEvent(object sender, int timeout) if (!cancellationTokenSource.Token.WaitHandle.WaitOne(timeout, true)) { cancellationTokenSource.Cancel(); - instance.Started = false; + instance.Kill(); } } CancelEvent += OnCancelEvent; - var errorCode = -1; try { - errorCode = await Process(instance, cancellationTokenSource).ConfigureAwait(false); - } - catch (Exception e) - { - if (!HandleException(throwOnError, e, instance.ErrorData)) return false; + await Task.WhenAll(instance.WaitForExitAsync().ContinueWith(t => + { + processResult = t.Result; + cancellationTokenSource.Cancel(); + _ffMpegArguments.Post(); + }), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false); + + return processResult; } finally { CancelEvent -= OnCancelEvent; } - - return HandleCompletion(throwOnError, errorCode, instance.ErrorData); - } - - private async Task Process(Instance instance, CancellationTokenSource cancellationTokenSource) - { - var errorCode = -1; - - _ffMpegArguments.Pre(); - await Task.WhenAll(instance.FinishedRunning().ContinueWith(t => - { - errorCode = t.Result; - cancellationTokenSource.Cancel(); - _ffMpegArguments.Post(); - }), _ffMpegArguments.During(cancellationTokenSource.Token)).ConfigureAwait(false); - - return errorCode; } private bool HandleCompletion(bool throwOnError, int exitCode, IReadOnlyList errorData) @@ -184,7 +179,7 @@ internal FFOptions GetConfiguredOptions(FFOptions? ffOptions) return options; } - private Instance PrepareInstance(FFOptions ffOptions, + private ProcessArguments PrepareProcessArguments(FFOptions ffOptions, out CancellationTokenSource cancellationTokenSource) { FFMpegHelper.RootExceptionCheck(); @@ -197,17 +192,25 @@ private Instance PrepareInstance(FFOptions ffOptions, StandardErrorEncoding = ffOptions.Encoding, WorkingDirectory = ffOptions.WorkingDirectory }; - var instance = new Instance(startInfo); + var processArguments = new ProcessArguments(startInfo); cancellationTokenSource = new CancellationTokenSource(); if (_onOutput != null || _onTimeProgress != null || (_onPercentageProgress != null && _totalTimespan != null)) - instance.DataReceived += OutputData; + processArguments.OutputDataReceived += OutputData; + + if (_onError != null) + processArguments.ErrorDataReceived += ErrorData; - return instance; + return processArguments; + } + + private void ErrorData(object sender, string msg) + { + _onError?.Invoke(msg); } - private static bool HandleException(bool throwOnError, Exception e, IReadOnlyList errorData) + private static bool HandleException(bool throwOnError, Exception e, IEnumerable errorData) { if (!throwOnError) return false; @@ -215,12 +218,12 @@ private static bool HandleException(bool throwOnError, Exception e, IReadOnlyLis throw new FFMpegException(FFMpegExceptionType.Process, "Exception thrown during processing", e, string.Join("\n", errorData)); } - private void OutputData(object sender, (DataType Type, string Data) msg) + private void OutputData(object sender, string msg) { - Debug.WriteLine(msg.Data); - _onOutput?.Invoke(msg.Data, msg.Type); + Debug.WriteLine(msg); + _onOutput?.Invoke(msg); - var match = ProgressRegex.Match(msg.Data); + var match = ProgressRegex.Match(msg); if (!match.Success) return; var processed = TimeSpan.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); diff --git a/FFMpegCore/FFMpegCore.csproj b/FFMpegCore/FFMpegCore.csproj index afabd90..a0464c7 100644 --- a/FFMpegCore/FFMpegCore.csproj +++ b/FFMpegCore/FFMpegCore.csproj @@ -32,9 +32,9 @@ - + - + diff --git a/FFMpegCore/FFProbe/FFProbe.cs b/FFMpegCore/FFProbe/FFProbe.cs index 36f050c..afa7c82 100644 --- a/FFMpegCore/FFProbe/FFProbe.cs +++ b/FFMpegCore/FFProbe/FFProbe.cs @@ -13,61 +13,61 @@ namespace FFMpegCore { public static class FFProbe { - public static IMediaAnalysis Analyse(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static IMediaAnalysis Analyse(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var processArguments = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = processArguments.StartAndWaitForExit(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseOutput(instance); + return ParseOutput(result); } - public static FFProbeFrames GetFrames(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static FFProbeFrames GetFrames(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = instance.StartAndWaitForExit(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseFramesOutput(instance); + return ParseFramesOutput(result); } - public static FFProbePackets GetPackets(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static FFProbePackets GetPackets(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PreparePacketAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = instance.StartAndWaitForExit(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParsePacketsOutput(instance); + return ParsePacketsOutput(result); } - public static IMediaAnalysis Analyse(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static IMediaAnalysis Analyse(Uri uri, FFOptions? ffOptions = null) { - using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = instance.BlockUntilFinished(); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current); + var result = instance.StartAndWaitForExit(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseOutput(instance); + return ParseOutput(result); } - public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static IMediaAnalysis Analyse(Stream stream, FFOptions? ffOptions = null) { var streamPipeSource = new StreamPipeSource(stream); var pipeArgument = new InputPipeArgument(streamPipeSource); - using var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); + var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current); pipeArgument.Pre(); - var task = instance.FinishedRunning(); + var task = instance.StartAndWaitForExitAsync(); try { pipeArgument.During().ConfigureAwait(false).GetAwaiter().GetResult(); @@ -77,62 +77,62 @@ public static IMediaAnalysis Analyse(Stream stream, int outputCapacity = int.Max { pipeArgument.Post(); } - var exitCode = task.ConfigureAwait(false).GetAwaiter().GetResult(); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var result = task.ConfigureAwait(false).GetAwaiter().GetResult(); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseOutput(instance); + return ParseOutput(result); } - public static async Task AnalyseAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static async Task AnalyseAsync(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PrepareStreamAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = await instance.FinishedRunning().ConfigureAwait(false); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var instance = PrepareStreamAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseOutput(instance); + return ParseOutput(result); } - public static async Task GetFramesAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static async Task GetFramesAsync(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PrepareFrameAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - await instance.FinishedRunning().ConfigureAwait(false); - return ParseFramesOutput(instance); + var instance = PrepareFrameAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false); + return ParseFramesOutput(result); } - public static async Task GetPacketsAsync(string filePath, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static async Task GetPacketsAsync(string filePath, FFOptions? ffOptions = null) { if (!File.Exists(filePath)) throw new FFMpegException(FFMpegExceptionType.File, $"No file found at '{filePath}'"); - using var instance = PreparePacketAnalysisInstance(filePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - await instance.FinishedRunning().ConfigureAwait(false); - return ParsePacketsOutput(instance); + var instance = PreparePacketAnalysisInstance(filePath, ffOptions ?? GlobalFFOptions.Current); + var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false); + return ParsePacketsOutput(result); } - public static async Task AnalyseAsync(Uri uri, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static async Task AnalyseAsync(Uri uri, FFOptions? ffOptions = null) { - using var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, outputCapacity, ffOptions ?? GlobalFFOptions.Current); - var exitCode = await instance.FinishedRunning().ConfigureAwait(false); - if (exitCode != 0) - throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", null, string.Join("\n", instance.ErrorData)); + var instance = PrepareStreamAnalysisInstance(uri.AbsoluteUri, ffOptions ?? GlobalFFOptions.Current); + var result = await instance.StartAndWaitForExitAsync().ConfigureAwait(false); + if (result.ExitCode != 0) + throw new FFMpegException(FFMpegExceptionType.Process, $"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", null, string.Join("\n", result.ErrorData)); - return ParseOutput(instance); + return ParseOutput(result); } - public static async Task AnalyseAsync(Stream stream, int outputCapacity = int.MaxValue, FFOptions? ffOptions = null) + public static async Task AnalyseAsync(Stream stream, FFOptions? ffOptions = null) { var streamPipeSource = new StreamPipeSource(stream); var pipeArgument = new InputPipeArgument(streamPipeSource); - using var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, outputCapacity, ffOptions ?? GlobalFFOptions.Current); + var instance = PrepareStreamAnalysisInstance(pipeArgument.PipePath, ffOptions ?? GlobalFFOptions.Current); pipeArgument.Pre(); - var task = instance.FinishedRunning(); + var task = instance.StartAndWaitForExitAsync(); try { await pipeArgument.During().ConfigureAwait(false); @@ -144,15 +144,15 @@ public static async Task AnalyseAsync(Stream stream, int outputC { pipeArgument.Post(); } - var exitCode = await task.ConfigureAwait(false); - if (exitCode != 0) - throw new FFProbeProcessException($"ffprobe exited with non-zero exit-code ({exitCode} - {string.Join("\n", instance.ErrorData)})", instance.ErrorData); + var result = await task.ConfigureAwait(false); + if (result.ExitCode != 0) + throw new FFProbeProcessException($"ffprobe exited with non-zero exit-code ({result.ExitCode} - {string.Join("\n", result.ErrorData)})", result.ErrorData); pipeArgument.Post(); - return ParseOutput(instance); + return ParseOutput(result); } - private static IMediaAnalysis ParseOutput(Instance instance) + private static IMediaAnalysis ParseOutput(IProcessResult instance) { var json = string.Join(string.Empty, instance.OutputData); var ffprobeAnalysis = JsonSerializer.Deserialize(json, new JsonSerializerOptions @@ -165,7 +165,7 @@ private static IMediaAnalysis ParseOutput(Instance instance) return new MediaAnalysis(ffprobeAnalysis); } - private static FFProbeFrames ParseFramesOutput(Instance instance) + private static FFProbeFrames ParseFramesOutput(IProcessResult instance) { var json = string.Join(string.Empty, instance.OutputData); var ffprobeAnalysis = JsonSerializer.Deserialize(json, new JsonSerializerOptions @@ -177,7 +177,7 @@ private static FFProbeFrames ParseFramesOutput(Instance instance) return ffprobeAnalysis; } - private static FFProbePackets ParsePacketsOutput(Instance instance) + private static FFProbePackets ParsePacketsOutput(IProcessResult instance) { var json = string.Join(string.Empty, instance.OutputData); var ffprobeAnalysis = JsonSerializer.Deserialize(json, new JsonSerializerOptions @@ -190,14 +190,14 @@ private static FFProbePackets ParsePacketsOutput(Instance instance) } - private static Instance PrepareStreamAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions) - => PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", outputCapacity, ffOptions); - private static Instance PrepareFrameAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions) - => PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions); - private static Instance PreparePacketAnalysisInstance(string filePath, int outputCapacity, FFOptions ffOptions) - => PrepareInstance($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{filePath}\"", outputCapacity, ffOptions); + private static ProcessArguments PrepareStreamAnalysisInstance(string filePath, FFOptions ffOptions) + => PrepareInstance($"-loglevel error -print_format json -show_format -sexagesimal -show_streams \"{filePath}\"", ffOptions); + private static ProcessArguments PrepareFrameAnalysisInstance(string filePath, FFOptions ffOptions) + => PrepareInstance($"-loglevel error -print_format json -show_frames -v quiet -sexagesimal \"{filePath}\"", ffOptions); + private static ProcessArguments PreparePacketAnalysisInstance(string filePath, FFOptions ffOptions) + => PrepareInstance($"-loglevel error -print_format json -show_packets -v quiet -sexagesimal \"{filePath}\"", ffOptions); - private static Instance PrepareInstance(string arguments, int outputCapacity, FFOptions ffOptions) + private static ProcessArguments PrepareInstance(string arguments, FFOptions ffOptions) { FFProbeHelper.RootExceptionCheck(); FFProbeHelper.VerifyFFProbeExists(ffOptions); @@ -207,8 +207,7 @@ private static Instance PrepareInstance(string arguments, int outputCapacity, FF StandardErrorEncoding = ffOptions.Encoding, WorkingDirectory = ffOptions.WorkingDirectory }; - var instance = new Instance(startInfo) { DataBufferCapacity = outputCapacity }; - return instance; + return new ProcessArguments(startInfo); } } } diff --git a/FFMpegCore/FFProbe/ProcessArgumentsExtensions.cs b/FFMpegCore/FFProbe/ProcessArgumentsExtensions.cs new file mode 100644 index 0000000..1647e9b --- /dev/null +++ b/FFMpegCore/FFProbe/ProcessArgumentsExtensions.cs @@ -0,0 +1,20 @@ +using System.Threading; +using System.Threading.Tasks; +using Instances; + +namespace FFMpegCore +{ + public static class ProcessArgumentsExtensions + { + public static IProcessResult StartAndWaitForExit(this ProcessArguments processArguments) + { + using var instance = processArguments.Start(); + return instance.WaitForExit(); + } + public static async Task StartAndWaitForExitAsync(this ProcessArguments processArguments, CancellationToken cancellationToken = default) + { + using var instance = processArguments.Start(); + return await instance.WaitForExitAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/FFMpegCore/Helpers/FFMpegHelper.cs b/FFMpegCore/Helpers/FFMpegHelper.cs index 12e52c3..cb3b4cf 100644 --- a/FFMpegCore/Helpers/FFMpegHelper.cs +++ b/FFMpegCore/Helpers/FFMpegHelper.cs @@ -38,8 +38,8 @@ public static void RootExceptionCheck() public static void VerifyFFMpegExists(FFOptions ffMpegOptions) { if (_ffmpegVerified) return; - var (exitCode, _) = Instance.Finish(GlobalFFOptions.GetFFMpegBinaryPath(ffMpegOptions), "-version"); - _ffmpegVerified = exitCode == 0; + var result = Instance.Finish(GlobalFFOptions.GetFFMpegBinaryPath(ffMpegOptions), "-version"); + _ffmpegVerified = result.ExitCode == 0; if (!_ffmpegVerified) throw new FFMpegException(FFMpegExceptionType.Operation, "ffmpeg was not found on your system"); } diff --git a/FFMpegCore/Helpers/FFProbeHelper.cs b/FFMpegCore/Helpers/FFProbeHelper.cs index 4989542..f5b3472 100644 --- a/FFMpegCore/Helpers/FFProbeHelper.cs +++ b/FFMpegCore/Helpers/FFProbeHelper.cs @@ -27,8 +27,8 @@ public static void RootExceptionCheck() public static void VerifyFFProbeExists(FFOptions ffMpegOptions) { if (_ffprobeVerified) return; - var (exitCode, _) = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version"); - _ffprobeVerified = exitCode == 0; + var result = Instance.Finish(GlobalFFOptions.GetFFProbeBinaryPath(ffMpegOptions), "-version"); + _ffprobeVerified = result.ExitCode == 0; if (!_ffprobeVerified) throw new FFProbeException("ffprobe was not found on your system"); } From 4e977c664708c1ac82105e05f61093edc066383a Mon Sep 17 00:00:00 2001 From: Malte Rosenbjerg Date: Sun, 13 Mar 2022 11:43:48 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c55520..df5679a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ [![GitHub issues](https://img.shields.io/github/issues/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/issues) [![GitHub stars](https://img.shields.io/github/stars/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/stargazers) [![GitHub](https://img.shields.io/github/license/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/blob/master/LICENSE) -[![CI](https://github.com/rosenbjerg/FFMpegCore/workflows/CI/badge.svg)](https://github.com/rosenbjerg/FFMpegCore/actions?query=workflow%3ACI) +[![CI](https://github.com/rosenbjerg/FFMpegCore/workflows/CI/badge.svg)](https://github.com/rosenbjerg/FFMpegCore/actions/workflows/ci.yml) +[![GitHub code contributors](https://img.shields.io/github/contributors/rosenbjerg/FFMpegCore)](https://github.com/rosenbjerg/FFMpegCore/graphs/contributors) A .NET Standard FFMpeg/FFProbe wrapper for easily integrating media analysis and conversion into your .NET applications. Supports both synchronous and asynchronous calls