diff --git a/BasicNodes/Tools/Executor.cs b/BasicNodes/Tools/Executor.cs
index ffe7ea3d..92fdc937 100644
--- a/BasicNodes/Tools/Executor.cs
+++ b/BasicNodes/Tools/Executor.cs
@@ -51,12 +51,14 @@ public class Executor : Node
private NodeParameters args;
- ///
- public override Task Cancel()
- {
- args?.Process?.Cancel();
- return Task.CompletedTask;
- }
+ // ///
+ // public override Task Cancel()
+ // {
+ // if (args.Process is ProcessHelper process)
+ // process.Kill();
+ //
+ // return Task.CompletedTask;
+ // }
///
public override int Execute(NodeParameters args)
diff --git a/FileFlows.Common.dll b/FileFlows.Common.dll
index 3025f7ac..bb13948a 100644
Binary files a/FileFlows.Common.dll and b/FileFlows.Common.dll differ
diff --git a/FileFlows.Common.pdb b/FileFlows.Common.pdb
index 4b6ca13b..1989cec1 100644
Binary files a/FileFlows.Common.pdb and b/FileFlows.Common.pdb differ
diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll
index 69202592..c1c78199 100644
Binary files a/FileFlows.Plugin.dll and b/FileFlows.Plugin.dll differ
diff --git a/FileFlows.Plugin.pdb b/FileFlows.Plugin.pdb
index c26bd11e..cf6c9f24 100644
Binary files a/FileFlows.Plugin.pdb and b/FileFlows.Plugin.pdb differ
diff --git a/VideoNodes/FFMpegEncoder.cs b/VideoNodes/FFMpegEncoder.cs
index d451fe59..5f602647 100644
--- a/VideoNodes/FFMpegEncoder.cs
+++ b/VideoNodes/FFMpegEncoder.cs
@@ -23,11 +23,17 @@ public class FFMpegEncoder
private Process process;
DateTime startedAt;
private string? AbortReason;
+ private CancellationToken _cancellationToken;
+ private ProcessHelper _processHelper;
- public FFMpegEncoder(string ffMpegExe, ILogger logger)
+ public FFMpegEncoder(string ffMpegExe, ILogger logger, CancellationToken cancellationToken)
{
this.ffMpegExe = ffMpegExe;
this.Logger = logger;
+ _processHelper = new(logger, cancellationToken, false);
+ _processHelper.OnStandardOutputReceived += OnOutputDataReceived;
+ _processHelper.OnErrorOutputReceived += OnErrorDataReceived;
+ _cancellationToken = cancellationToken;
}
///
@@ -99,20 +105,13 @@ public class FFMpegEncoder
{
try
{
- if (this.process != null)
- {
- this.process.Kill();
- this.process = null;
- }
-
+ _processHelper.Kill();
}
catch (Exception) { }
}
public async Task ExecuteShellCommand(string command, List arguments, int timeout = 0)
{
- var result = new ProcessResult();
-
var hwDecoderIndex = arguments.FindIndex(x => x == "-hwaccel");
string? decoder = null;
if (hwDecoderIndex >= 0 && hwDecoderIndex < arguments.Count - 2)
@@ -151,18 +150,19 @@ public class FFMpegEncoder
string? encoder = null;
if (arguments.Any(x =>
- x.ToLowerInvariant().Contains("hevc_qsv") || x.ToLowerInvariant().Contains("h264_qsv") ||
- x.ToLowerInvariant().Contains("av1_qsv")))
+ x.Contains("hevc_qsv", StringComparison.InvariantCultureIgnoreCase) ||
+ x.Contains("h264_qsv", StringComparison.InvariantCultureIgnoreCase) ||
+ x.Contains("av1_qsv", StringComparison.InvariantCultureIgnoreCase)))
encoder = "QSV";
- else if (arguments.Any(x => x.ToLowerInvariant().Contains("_nvenc")))
+ else if (arguments.Any(x => x.Contains("_nvenc", StringComparison.InvariantCultureIgnoreCase)))
encoder = "NVIDIA";
- else if (arguments.Any(x => x.ToLowerInvariant().Contains("_amf")))
+ else if (arguments.Any(x => x.Contains("_amf", StringComparison.InvariantCultureIgnoreCase)))
encoder = "AMF";
- else if (arguments.Any(x => x.ToLowerInvariant().Contains("_vaapi")))
+ else if (arguments.Any(x => x.Contains("_vaapi", StringComparison.InvariantCultureIgnoreCase)))
encoder = "VAAPI";
- else if (arguments.Any(x => x.ToLowerInvariant().Contains("_videotoolbox")))
+ else if (arguments.Any(x => x.Contains("_videotoolbox", StringComparison.InvariantCultureIgnoreCase)))
encoder = "VideoToolbox";
- else if (arguments.Any(x => x.ToLowerInvariant().Contains("libx") || x.ToLowerInvariant().Contains("libvpx")))
+ else if (arguments.Any(x => x.Contains("libx", StringComparison.InvariantCultureIgnoreCase) || x.Contains("libvpx", StringComparison.InvariantCultureIgnoreCase)))
encoder = "CPU";
if (encoder != null)
@@ -171,123 +171,52 @@ public class FFMpegEncoder
OnStatChange?.Invoke("Encoder", encoder, recordStatistic: true);
}
- using (var process = new Process())
+ var processHelper = new ProcessHelper(Logger, _cancellationToken, false);
+ processHelper.OnStandardOutputReceived += OnOutputDataReceived;
+ processHelper.OnErrorOutputReceived += OnErrorDataReceived;
+ var result = processHelper.ExecuteShellCommand(new()
{
- this.process = process;
-
- process.StartInfo.FileName = command;
- if (arguments?.Any() == true)
- {
- foreach (string arg in arguments)
- process.StartInfo.ArgumentList.Add(arg);
- }
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardInput = true;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.RedirectStandardError = true;
- process.StartInfo.CreateNoWindow = true;
-
- outputBuilder = new StringBuilder();
- outputCloseEvent = new TaskCompletionSource();
-
- process.OutputDataReceived += OnOutputDataReceived;
-
- errorBuilder = new StringBuilder();
- errorCloseEvent = new TaskCompletionSource();
-
- process.ErrorDataReceived += OnErrorDataReceived;
-
- bool isStarted;
-
- startedAt = DateTime.Now;
- try
- {
- isStarted = process.Start();
- }
- catch (Exception error)
- {
- // Usually it occurs when an executable file is not found or is not executable
-
- result.Completed = true;
- result.ExitCode = -1;
- result.Output = error.Message;
-
- isStarted = false;
- }
-
- if (isStarted)
- {
- // Reads the output stream first and then waits because deadlocks are possible
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
-
- // Creates task to wait for process exit using timeout
- var waitForExit = WaitForExitAsync(process, timeout);
-
- // Create task to wait for process exit and closing all output streams
- var processTask = Task.WhenAll(waitForExit, outputCloseEvent.Task, errorCloseEvent.Task);
-
- // Waits process completion and then checks it was not completed by timeout
- if (
- (
- (timeout > 0 && await Task.WhenAny(Task.Delay(timeout), processTask) == processTask) ||
- (timeout == 0 && await Task.WhenAny(processTask) == processTask)
- )
- && waitForExit.Result)
- {
- result.Completed = true;
- result.ExitCode = process.ExitCode;
- result.Output = $"{outputBuilder}{errorBuilder}";
- }
- else
- {
- try
- {
- // Kill hung process
- process.Kill();
- }
- catch
- {
- }
- }
- }
- }
- process = null;
-
- if (this.AbortReason != null)
- result.AbortReason = this.AbortReason;
-
- return result;
+ Command = command,
+ ArgumentList = arguments.ToArray()
+ }).Result;
+
+ return new()
+ {
+ Completed = result.Completed,
+ ExitCode = result.ExitCode,
+ Output = result.StandardOutput,
+ AbortReason = AbortReason?.EmptyAsNull()
+ };
}
- public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
+ private void OnOutputDataReceived(string data)
{
// The output stream has been closed i.e. the process has terminated
- if (e.Data == null)
+ if (data == null)
{
outputCloseEvent.SetResult(true);
}
else
{
- CheckOutputLine(e.Data);
+ CheckOutputLine(data);
}
}
- public void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
+ private void OnErrorDataReceived(string data)
{
// The error stream has been closed i.e. the process has terminated
- if (e.Data == null)
+ if (data == null)
{
errorCloseEvent.SetResult(true);
}
- else if (e.Data.ToLower().Contains("failed") || e.Data.Contains("No capable devices found") || e.Data.ToLower().Contains("error"))
+ else if (data.ToLower().Contains("failed") || data.Contains("No capable devices found") || data.ToLower().Contains("error"))
{
- Logger.ELog(e.Data);
- errorBuilder.AppendLine(e.Data);
+ Logger.ELog(data);
+ errorBuilder.AppendLine(data);
}
else
{
- CheckOutputLine(e.Data);
+ CheckOutputLine(data);
}
}
@@ -337,17 +266,6 @@ public class FFMpegEncoder
outputBuilder.AppendLine(line);
}
-
- private static Task WaitForExitAsync(Process process, int timeout)
- {
- if (timeout > 0)
- return Task.Run(() => process.WaitForExit(timeout));
- return Task.Run(() =>
- {
- process.WaitForExit();
- return Task.FromResult(true);
- });
- }
///
/// Represents the result of a process execution.
///
diff --git a/VideoNodes/VideoNodes/EncodingNode.cs b/VideoNodes/VideoNodes/EncodingNode.cs
index 1e444bd7..83f97367 100644
--- a/VideoNodes/VideoNodes/EncodingNode.cs
+++ b/VideoNodes/VideoNodes/EncodingNode.cs
@@ -63,7 +63,7 @@ namespace FileFlows.VideoNodes
if (string.IsNullOrEmpty(extension))
extension = "mkv";
- Encoder = new FFMpegEncoder(ffmpegExe, args.Logger);
+ Encoder = new FFMpegEncoder(ffmpegExe, args.Logger, args.CancellationToken);
Encoder.AtTime += AtTimeEvent;
Encoder.OnStatChange += EncoderOnOnStatChange;
@@ -151,13 +151,6 @@ namespace FileFlows.VideoNodes
}
}
- public override Task Cancel()
- {
- if (Encoder != null)
- Encoder.Cancel();
- return base.Cancel();
- }
-
void AtTimeEvent(TimeSpan time, DateTime startedAt)
{
if (TotalTime.TotalMilliseconds == 0)
@@ -219,133 +212,6 @@ namespace FileFlows.VideoNodes
}
return vidparams;
-
- // no longer do this
- //if (vidparams.ToLower().Contains("hevc_nvenc"))
- //{
- // // nvidia h265 encoding, check can
- // bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
- // if (canProcess == false)
- // {
- // // change to cpu encoding
- // Args.Logger?.ILog("Can't encode using hevc_nvenc, falling back to CPU encoding H265 (libx265)");
- // return "libx265";
- // }
- // return vidparams;
- //}
- //else if (vidparams.ToLower().Contains("h264_nvenc"))
- //{
- // // nvidia h264 encoding, check can
- // bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
- // if (canProcess == false)
- // {
- // // change to cpu encoding
- // Args.Logger?.ILog("Can't encode using h264_nvenc, falling back to CPU encoding H264 (libx264)");
- // return "libx264";
- // }
- // return vidparams;
- //}
- //else if (vidparams.ToLower().Contains("hevc_qsv"))
- //{
- // // nvidia h265 encoding, check can
- // bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
- // if (canProcess == false)
- // {
- // // change to cpu encoding
- // Args.Logger?.ILog("Can't encode using hevc_qsv, falling back to CPU encoding H265 (libx265)");
- // return "libx265";
- // }
- // return vidparams;
- //}
- //else if (vidparams.ToLower().Contains("h264_qsv"))
- //{
- // // nvidia h264 encoding, check can
- // bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
- // if (canProcess == false)
- // {
- // // change to cpu encoding
- // Args.Logger?.ILog("Can't encode using h264_qsv, falling back to CPU encoding H264 (libx264)");
- // return "libx264";
- // }
- // return vidparams;
- //}
- //return vidparams;
- }
-
- public bool HasNvidiaCard(string ffmpeg)
- {
- try
- {
- if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
- {
- var cmd = Args.Process.ExecuteShellCommand(new ExecuteArgs
- {
- Command = "wmic",
- Arguments = "path win32_VideoController get name"
- }).Result;
- if (cmd.ExitCode == 0)
- {
- // it worked
- if (cmd.Output?.ToLower()?.Contains("nvidia") == false)
- return false;
- }
- }
- else
- {
- // linux, crude method, look for nvidia in the /dev dir
- var dir = new System.IO.DirectoryInfo("/dev");
- if (dir.Exists == false)
- return false;
-
- bool dev = dir.GetDirectories().Any(x => x.Name.ToLower().Contains("nvidia"));
- if (dev == false)
- return false;
- }
-
- // check cuda in ffmpeg itself
- var result = Args.Process.ExecuteShellCommand(new ExecuteArgs
- {
- Command = ffmpeg,
- Arguments = "-hide_banner -init_hw_device list"
- }).Result;
- return result.Output?.Contains("cuda") == true;
- }
- catch (Exception ex)
- {
- Args.Logger?.ELog("Failed to detect NVIDIA card: " + ex.Message + Environment.NewLine + ex.StackTrace);
- return false;
- }
- }
-
- protected bool IsSameVideoCodec(string current, string wanted)
- {
- wanted = ReplaceCommon(wanted);
- current = ReplaceCommon(current);
-
- return wanted == current;
-
- string ReplaceCommon(string input)
- {
- input = input.ToLower();
- input = Regex.Replace(input, "^(divx|xvid|m(-)?peg(-)4)$", "mpeg4", RegexOptions.IgnoreCase);
- input = Regex.Replace(input, "^(hevc|h[\\.x\\-]?265)$", "h265", RegexOptions.IgnoreCase);
- input = Regex.Replace(input, "^(h[\\.x\\-]?264)$", "h264", RegexOptions.IgnoreCase);
- return input;
- }
- }
-
- protected bool SupportsSubtitles(NodeParameters args, VideoInfo videoInfo, string extension)
- {
- if (videoInfo?.SubtitleStreams?.Any() != true)
- return false;
- bool mov_text = videoInfo.SubtitleStreams.Any(x => x.Codec == "mov_text");
- // if mov_text and going to mkv, we can't convert these subtitles
- if (mov_text && extension?.ToLower()?.EndsWith("mkv") == true)
- return false;
- return true;
- //if (Regex.IsMatch(container ?? string.Empty, "(mp(e)?(g)?4)|avi|divx|xvid", RegexOptions.IgnoreCase))
- // return false;
- //return true;
}
}
}
\ No newline at end of file