From 893646a52c0824e55a8f3a9ff3d4da5e4d50cf9a Mon Sep 17 00:00:00 2001 From: Tobias Faller <fallert@tf.uni-freiburg.de> Date: Fri, 5 Jan 2024 17:38:47 +0100 Subject: [PATCH 1/3] Clean-up code handling pipes in Engine class; Fixes #69 --- src/FFmpeg.NET/Engine.cs | 70 +++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 30 deletions(-) mode change 100644 => 100755 src/FFmpeg.NET/Engine.cs diff --git a/src/FFmpeg.NET/Engine.cs b/src/FFmpeg.NET/Engine.cs old mode 100644 new mode 100755 index 6b7bffa..a3fea1a --- a/src/FFmpeg.NET/Engine.cs +++ b/src/FFmpeg.NET/Engine.cs @@ -87,28 +87,35 @@ public async Task<Stream> ConvertAsync(IInputArgument input, ConversionOptions o public async Task ConvertAsync(IInputArgument input, Stream output, ConversionOptions options, CancellationToken cancellationToken) { - var pipeName = $"{_pipePrefix}{Guid.NewGuid()}"; + var outputPipeName = $"{_pipePrefix}{Guid.NewGuid()}"; + var outputArgument = new OutputPipe(GetPipePath(outputPipeName)); + var pipe = new NamedPipeServerStream(outputPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); + var parameters = new FFmpegParameters { Task = FFmpegTask.Convert, Input = input, - Output = new OutputPipe(GetPipePath(pipeName)), + Output = outputArgument, ConversionOptions = options }; - var process = CreateProcess(parameters); - var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - - await pipe.WaitForConnectionAsync(cancellationToken); - await Task.WhenAll( - pipe.CopyToAsync(output, cancellationToken), - process.ExecuteAsync(cancellationToken).ContinueWith(x => - { - pipe.Disconnect(); - pipe.Dispose(); - }, cancellationToken) - ).ConfigureAwait(false); - Cleanup(process); + var ffmpegProcess = CreateProcess(parameters); + try + { + var executeProcess = ffmpegProcess.ExecuteAsync(cancellationToken); + var copyData = pipe.WaitForConnectionAsync(cancellationToken) + .ContinueWith(async x => + { + await pipe.CopyToAsync(output, cancellationToken); + }, cancellationToken).Unwrap(); + await Task.WhenAll(executeProcess, copyData).ConfigureAwait(false); + pipe.Disconnect(); + } + finally + { + pipe.Dispose(); + Cleanup(ffmpegProcess); + } } public async Task ConvertAsync(IArgument argument, Stream output, CancellationToken cancellationToken) @@ -119,21 +126,24 @@ public async Task ConvertAsync(IArgument argument, Stream output, CancellationTo var arguments = argument.Argument + $" {outputArgument.Argument}"; var parameters = new FFmpegParameters { CustomArguments = arguments }; - var process = CreateProcess(parameters); - - await Task.WhenAll( - pipe.WaitForConnectionAsync(cancellationToken).ContinueWith(async x => - { - await pipe.CopyToAsync(output, cancellationToken); - }), - process.ExecuteAsync(cancellationToken).ContinueWith(x => - { - pipe.Disconnect(); - pipe.Dispose(); - }, cancellationToken) - ).ConfigureAwait(false); - - Cleanup(process); + + var ffmpegProcess = CreateProcess(parameters); + try + { + var executeProcess = ffmpegProcess.ExecuteAsync(cancellationToken); + var copyData = pipe.WaitForConnectionAsync(cancellationToken) + .ContinueWith(async x => + { + await pipe.CopyToAsync(output, cancellationToken); + }, cancellationToken).Unwrap(); + await Task.WhenAll(executeProcess, copyData).ConfigureAwait(false); + pipe.Disconnect(); + } + finally + { + pipe.Dispose(); + Cleanup(ffmpegProcess); + } } private async Task ExecuteAsync(FFmpegParameters parameters, CancellationToken cancellationToken) From 0bfc55ca845038887f9a30dc1e515c4f4905a319 Mon Sep 17 00:00:00 2001 From: Tobias Faller <fallert@tf.uni-freiburg.de> Date: Fri, 5 Jan 2024 19:19:21 +0100 Subject: [PATCH 2/3] Made ExecuteAsync in Engine public and added Execute task type --- src/FFmpeg.NET/Engine.cs | 100 +++++++++++------------- src/FFmpeg.NET/FFmpegArgumentBuilder.cs | 26 +++++- src/FFmpeg.NET/FFmpegTask.cs | 3 +- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/FFmpeg.NET/Engine.cs b/src/FFmpeg.NET/Engine.cs index a3fea1a..69f75f7 100755 --- a/src/FFmpeg.NET/Engine.cs +++ b/src/FFmpeg.NET/Engine.cs @@ -31,6 +31,11 @@ public Engine(string ffmpegPath = null) public event EventHandler<ConversionCompleteEventArgs> Complete; public event EventHandler<ConversionDataEventArgs> Data; + + // --------------------------------------------------------- + // Wrapper methods for ease of use + // --------------------------------------------------------- + public async Task<MetaData> GetMetaDataAsync(IInputArgument mediaFile, CancellationToken cancellationToken) { var parameters = new FFmpegParameters @@ -87,46 +92,64 @@ public async Task<Stream> ConvertAsync(IInputArgument input, ConversionOptions o public async Task ConvertAsync(IInputArgument input, Stream output, ConversionOptions options, CancellationToken cancellationToken) { - var outputPipeName = $"{_pipePrefix}{Guid.NewGuid()}"; - var outputArgument = new OutputPipe(GetPipePath(outputPipeName)); - var pipe = new NamedPipeServerStream(outputPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - var parameters = new FFmpegParameters { Task = FFmpegTask.Convert, Input = input, - Output = outputArgument, ConversionOptions = options }; + await ExecuteAsync(parameters, output, cancellationToken).ConfigureAwait(false); + } - var ffmpegProcess = CreateProcess(parameters); - try + public async Task ConvertAsync(IArgument argument, Stream output, CancellationToken cancellationToken) + { + var parameters = new FFmpegParameters { - var executeProcess = ffmpegProcess.ExecuteAsync(cancellationToken); - var copyData = pipe.WaitForConnectionAsync(cancellationToken) - .ContinueWith(async x => - { - await pipe.CopyToAsync(output, cancellationToken); - }, cancellationToken).Unwrap(); - await Task.WhenAll(executeProcess, copyData).ConfigureAwait(false); - pipe.Disconnect(); - } - finally + Task = FFmpegTask.Execute, + CustomArguments = argument.Argument + }; + await ExecuteAsync(parameters, output, cancellationToken).ConfigureAwait(false); + } + + public async Task ExecuteAsync(string arguments, CancellationToken cancellationToken) + { + var parameters = new FFmpegParameters { - pipe.Dispose(); - Cleanup(ffmpegProcess); - } + Task = FFmpegTask.Execute, + CustomArguments = arguments, + }; + await ExecuteAsync(parameters, cancellationToken).ConfigureAwait(false); + } + + public async Task ExecuteAsync(string arguments, string workingDirectory, CancellationToken cancellationToken) + { + var parameters = new FFmpegParameters + { + Task = FFmpegTask.Execute, + CustomArguments = arguments, + WorkingDirectory = workingDirectory + }; + await ExecuteAsync(parameters, cancellationToken).ConfigureAwait(false); } - public async Task ConvertAsync(IArgument argument, Stream output, CancellationToken cancellationToken) + // --------------------------------------------------------- + // Basic API to call ffmpeg + // --------------------------------------------------------- + + public async Task ExecuteAsync(FFmpegParameters parameters, CancellationToken cancellationToken) + { + var ffmpegProcess = CreateProcess(parameters); + await ffmpegProcess.ExecuteAsync(cancellationToken).ConfigureAwait(false); + Cleanup(ffmpegProcess); + } + + public async Task ExecuteAsync(FFmpegParameters parameters, Stream output, CancellationToken cancellationToken) { var outputPipeName = $"{_pipePrefix}{Guid.NewGuid()}"; var outputArgument = new OutputPipe(GetPipePath(outputPipeName)); var pipe = new NamedPipeServerStream(outputPipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); - var arguments = argument.Argument + $" {outputArgument.Argument}"; - var parameters = new FFmpegParameters { CustomArguments = arguments }; - + parameters.Output = outputArgument; var ffmpegProcess = CreateProcess(parameters); try { @@ -146,35 +169,6 @@ public async Task ConvertAsync(IArgument argument, Stream output, CancellationTo } } - private async Task ExecuteAsync(FFmpegParameters parameters, CancellationToken cancellationToken) - { - var ffmpegProcess = CreateProcess(parameters); - await ffmpegProcess.ExecuteAsync(cancellationToken).ConfigureAwait(false); - Cleanup(ffmpegProcess); - } - - public async Task ExecuteAsync(string arguments, CancellationToken cancellationToken) - { - var parameters = new FFmpegParameters - { - CustomArguments = arguments, - }; - await ExecuteAsync(parameters, cancellationToken).ConfigureAwait(false); - } - - // if further overloads are needed - // it should be considered if ExecuteAsync(FFmpegParameters parameters, CancellationToken cancellationToken) should be made public - public async Task ExecuteAsync(string arguments, string workingDirectory, CancellationToken cancellationToken) - { - var parameters = new FFmpegParameters - { - CustomArguments = arguments, - WorkingDirectory = workingDirectory - }; - await ExecuteAsync(parameters, cancellationToken).ConfigureAwait(false); - } - - private FFmpegProcess CreateProcess(FFmpegParameters parameters) { var ffmpegProcess = new FFmpegProcess(parameters, _ffmpegPath); diff --git a/src/FFmpeg.NET/FFmpegArgumentBuilder.cs b/src/FFmpeg.NET/FFmpegArgumentBuilder.cs index baa6867..5f9e5ff 100644 --- a/src/FFmpeg.NET/FFmpegArgumentBuilder.cs +++ b/src/FFmpeg.NET/FFmpegArgumentBuilder.cs @@ -9,14 +9,12 @@ internal static class FFmpegArgumentBuilder { public static string Build(FFmpegParameters parameters) { - if (parameters.HasCustomArguments) - return parameters.CustomArguments; - return parameters.Task switch { FFmpegTask.Convert => Convert(parameters.Input, parameters.Output, parameters.ConversionOptions), FFmpegTask.GetMetaData => GetMetadata(parameters.Input), FFmpegTask.GetThumbnail => GetThumbnail(parameters.Input, parameters.Output, parameters.ConversionOptions), + FFmpegTask.Execute => Execute(parameters.Input, parameters.Output, parameters.CustomArguments), _ => throw new ArgumentOutOfRangeException(), }; } @@ -182,6 +180,28 @@ private static string Convert(IInputArgument input, IOutputArgument output, Conv return commandBuilder.AppendFormat(" {0} ", output.Argument).ToString(); } + private static string Execute(IInputArgument input, IOutputArgument output, string customArguments) + { + StringBuilder commandBuilder = new StringBuilder(); + commandBuilder.Append(customArguments); + + if (input != null) + { + if (input.UseStandardInput) + { + commandBuilder.Append(" -nostdin "); + } + commandBuilder.Append($" -i {input.Argument}"); + } + + if (output != null) + { + commandBuilder.Append($"{output.Argument}"); + } + + return commandBuilder.ToString(); + } + private static void AppendHWAccelOutputFormat(StringBuilder commandBuilder, ConversionOptions conversionOptions) { if (conversionOptions.HWAccel != HWAccel.None && conversionOptions.HWAccelOutputFormatCopy) diff --git a/src/FFmpeg.NET/FFmpegTask.cs b/src/FFmpeg.NET/FFmpegTask.cs index b30634d..7e2d8a8 100644 --- a/src/FFmpeg.NET/FFmpegTask.cs +++ b/src/FFmpeg.NET/FFmpegTask.cs @@ -4,6 +4,7 @@ public enum FFmpegTask { Convert, GetMetaData, - GetThumbnail + GetThumbnail, + Execute } } \ No newline at end of file From aed8734ad5adf6c7f5231ba109a58b33a326e274 Mon Sep 17 00:00:00 2001 From: Tobias Faller <fallert@tf.uni-freiburg.de> Date: Fri, 5 Jan 2024 19:42:12 +0100 Subject: [PATCH 3/3] Added spaces around string in argument builder --- src/FFmpeg.NET/FFmpegArgumentBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FFmpeg.NET/FFmpegArgumentBuilder.cs b/src/FFmpeg.NET/FFmpegArgumentBuilder.cs index 5f9e5ff..621b1cc 100644 --- a/src/FFmpeg.NET/FFmpegArgumentBuilder.cs +++ b/src/FFmpeg.NET/FFmpegArgumentBuilder.cs @@ -191,12 +191,12 @@ private static string Execute(IInputArgument input, IOutputArgument output, stri { commandBuilder.Append(" -nostdin "); } - commandBuilder.Append($" -i {input.Argument}"); + commandBuilder.Append($" -i {input.Argument} "); } if (output != null) { - commandBuilder.Append($"{output.Argument}"); + commandBuilder.Append($" {output.Argument} "); } return commandBuilder.ToString();