diff --git a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs
index 712199cb..3e672592 100644
--- a/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs
+++ b/VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderVideoEncode.cs
@@ -98,10 +98,14 @@ public class FfmpegBuilderVideoEncode:FfmpegBuilderNode
{
if (HardwareEncoding == false)
H26x_CPU(stream);
- else if (SupportsHardwareNvidia264())
+ else if (CanUseHardwareEncoding.CanProcess_Nvidia_H264(Args))
H26x_Nvidia(stream, false);
- else if (SupportsHardwareQsv264())
+ else if (CanUseHardwareEncoding.CanProcess_Qsv_H264(Args))
H26x_Qsv(stream, false);
+ else if (CanUseHardwareEncoding.CanProcess_Amd_H264(Args))
+ H26x_Amd(stream, false);
+ else if (CanUseHardwareEncoding.CanProcess_Vaapi_H264(Args))
+ H26x_Vaapi(stream, false);
else
H26x_CPU(stream);
@@ -114,10 +118,14 @@ public class FfmpegBuilderVideoEncode:FfmpegBuilderNode
// hevc_qsv -load_plugin hevc_hw -pix_fmt p010le -profile:v main10 -global_quality 21 -g 24 -look_ahead 1 -look_ahead_depth 60
if (HardwareEncoding == false)
H26x_CPU(stream);
- else if (SupportsHardwareNvidia265())
+ else if (CanUseHardwareEncoding.CanProcess_Nvidia_Hevc(Args))
H26x_Nvidia(stream, true);
- else if (SupportsHardwareQsv265())
+ else if (CanUseHardwareEncoding.CanProcess_Qsv_Hevc(Args))
H26x_Qsv(stream, true);
+ else if (CanUseHardwareEncoding.CanProcess_Amd_Hevc(Args))
+ H26x_Amd(stream, true);
+ else if (CanUseHardwareEncoding.CanProcess_Vaapi_Hevc(Args))
+ H26x_Vaapi(stream, true);
else
H26x_CPU(stream);
@@ -181,4 +189,39 @@ public class FfmpegBuilderVideoEncode:FfmpegBuilderNode
"-preset", "slower",
});
}
+
+ private void H26x_Amd(FfmpegVideoStream stream, bool h265)
+ {
+ stream.EncodingParameters.Clear();
+ stream.EncodingParameters.AddRange(new[]
+ {
+ h265 ? "amf_nvenc" : "amf_nvenc",
+ "-rc", "constqp",
+ "-qp", Quality.ToString(),
+ //"-b:v", "0K", // this would do a two-pass... slower
+ "-preset", "slower",
+ // https://www.reddit.com/r/ffmpeg/comments/gg5szi/what_is_spatial_aq_and_temporal_aq_with_nvenc/
+ "-spatial-aq", "1"
+ });
+
+ if (Codec == CODEC_H264_10BIT)
+ bit10Filter = "yuv420p";
+ }
+ private void H26x_Vaapi(FfmpegVideoStream stream, bool h265)
+ {
+ stream.EncodingParameters.Clear();
+ stream.EncodingParameters.AddRange(new[]
+ {
+ h265 ? "amf_nvenc" : "amf_nvenc",
+ "-rc", "constqp",
+ "-qp", Quality.ToString(),
+ //"-b:v", "0K", // this would do a two-pass... slower
+ "-preset", "slower",
+ // https://www.reddit.com/r/ffmpeg/comments/gg5szi/what_is_spatial_aq_and_temporal_aq_with_nvenc/
+ "-spatial-aq", "1"
+ });
+
+ if (Codec == CODEC_H264_10BIT)
+ bit10Filter = "yuv420p";
+ }
}
diff --git a/VideoNodes/LogicalNodes/CanUseHardwareEncoding.cs b/VideoNodes/LogicalNodes/CanUseHardwareEncoding.cs
new file mode 100644
index 00000000..f799ff1a
--- /dev/null
+++ b/VideoNodes/LogicalNodes/CanUseHardwareEncoding.cs
@@ -0,0 +1,167 @@
+namespace FileFlows.VideoNodes;
+
+///
+/// Node for checking if Flow Runner has access to hardware
+///
+public class CanUseHardwareEncoding:Node
+{
+ public override int Inputs => 1;
+ public override int Outputs => 2;
+
+ public override string Icon => "fas fa-eye";
+
+ public override FlowElementType Type => FlowElementType.Logic;
+
+ [Select(nameof(EncoderOptions), 1)]
+ public HardwareEncoder Encoder { get; set; }
+
+ private static List _EncoderOptions;
+ public static List EncoderOptions
+ {
+ get
+ {
+ if (_EncoderOptions == null)
+ {
+ _EncoderOptions = new List
+ {
+ new ListOption { Label = "NVIDIA", Value = "###GROUP###" },
+ new ListOption { Label = "NVIDIA H.264", Value = HardwareEncoder.Nvidia_H264 },
+ new ListOption { Label = "NVIDIA H.265", Value = HardwareEncoder.Nvidia_Hevc },
+
+ new ListOption { Label = "AMD", Value = "###GROUP###" },
+ new ListOption { Label = "AMD H.264", Value = HardwareEncoder.Amd_H264 },
+ new ListOption { Label = "AMD H.265", Value = HardwareEncoder.Amd_Hevc },
+
+ new ListOption { Label = "Intel QSV", Value = "###GROUP###" },
+ new ListOption { Label = "Intel QSV H.264", Value = HardwareEncoder.Qsv_H264 },
+ new ListOption { Label = "Intel QSV H.265", Value = HardwareEncoder.Qsv_Hevc },
+
+ new ListOption { Label = "VAAPI", Value = "###GROUP###" },
+ new ListOption { Label = "VAAPI H.264", Value = HardwareEncoder.Vaapi_H264 },
+ new ListOption { Label = "VAAPI H.265", Value = HardwareEncoder.Vaapi_Hevc },
+ };
+ }
+ return _EncoderOptions;
+ }
+ }
+
+ public enum HardwareEncoder
+ {
+ Nvidia_H264 = 1,
+ Amd_H264 = 2,
+ Qsv_H264 = 3,
+ Vaapi_H264 = 4,
+
+ Nvidia_Hevc = 11,
+ Amd_Hevc = 12,
+ Qsv_Hevc = 13,
+ Vaapi_Hevc = 14,
+ }
+
+ public override int Execute(NodeParameters args)
+ {
+ bool canProcess = false;
+
+ switch (Encoder)
+ {
+ case HardwareEncoder.Nvidia_H264: canProcess = CanProcess_Nvidia_H264(args); break;
+ case HardwareEncoder.Nvidia_Hevc: canProcess = CanProcess_Nvidia_Hevc(args); break;
+
+ case HardwareEncoder.Amd_H264: canProcess = CanProcess_Amd_H264(args); break;
+ case HardwareEncoder.Amd_Hevc: canProcess = CanProcess_Amd_Hevc(args); break;
+
+ case HardwareEncoder.Qsv_H264: canProcess = CanProcess_Qsv_H264(args); break;
+ case HardwareEncoder.Qsv_Hevc: canProcess = CanProcess_Qsv_Hevc(args); break;
+
+ case HardwareEncoder.Vaapi_H264: canProcess = CanProcess_Vaapi_H264(args); break;
+ case HardwareEncoder.Vaapi_Hevc: canProcess = CanProcess_Vaapi_Hevc(args); break;
+ }
+
+ return canProcess ? 1 : 2;
+ }
+
+ ///
+ /// Checks if this flow runner can use NVIDIA HEVC encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Nvidia_Hevc(NodeParameters args) => CanProcess(args, "hevc_nvenc");
+
+ ///
+ /// Checks if this flow runner can use NVIDIA H.264 encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Nvidia_H264(NodeParameters args) => CanProcess(args, "h264_nvenc");
+
+ ///
+ /// Checks if this flow runner can use AND HEVC encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Amd_Hevc(NodeParameters args) => CanProcess(args, "hevc_amf");
+
+ ///
+ /// Checks if this flow runner can use AND H.264 encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Amd_H264(NodeParameters args) => CanProcess(args, "h264_amf");
+
+
+ ///
+ /// Checks if this flow runner can use Intels QSV HEVC encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Qsv_Hevc(NodeParameters args) => CanProcess(args, "hevc_qsv -global_quality 28 -load_plugin hevc_hw");
+
+ ///
+ /// Checks if this flow runner can use Intels QSV H.264 encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Qsv_H264(NodeParameters args) => CanProcess(args, "h264_qsv");
+
+ ///
+ /// Checks if this flow runner can use VAAPI HEVC encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Vaapi_Hevc(NodeParameters args) => CanProcess(args, "hevc_vaapi");
+
+ ///
+ /// Checks if this flow runner can use VAAPI H.264 encoder
+ ///
+ /// the node parameters
+ /// true if can use it, otherwise false
+ internal static bool CanProcess_Vaapi_H264(NodeParameters args) => CanProcess(args, "h264_vaapi");
+
+ private static bool CanProcess(NodeParameters args, string encodingParams)
+ {
+ string ffmpeg = args.GetToolPath("FFMpeg");
+ if (string.IsNullOrEmpty(ffmpeg))
+ {
+ args.Logger.ELog("FFMpeg tool not found.");
+ return false;
+ }
+ return CanProcess(args, ffmpeg, encodingParams);
+ }
+
+ internal static bool CanProcess(NodeParameters args, string ffmpeg, string encodingParams)
+ {
+ string cmdArgs = $"-loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v {encodingParams} -f null -\"";
+ var cmd = args.Process.ExecuteShellCommand(new ExecuteArgs
+ {
+ Command = ffmpeg,
+ Arguments = cmdArgs,
+ Silent = true
+ }).Result;
+ if (cmd.ExitCode != 0 || string.IsNullOrWhiteSpace(cmd.Output) == false)
+ {
+ args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/VideoNodes/Tests/VideoInfoHelperTests.cs b/VideoNodes/Tests/VideoInfoHelperTests.cs
index 0e5b26ba..ccb7845e 100644
--- a/VideoNodes/Tests/VideoInfoHelperTests.cs
+++ b/VideoNodes/Tests/VideoInfoHelperTests.cs
@@ -79,34 +79,34 @@ namespace VideoNodes.Tests
Assert.IsTrue(result);
}
- [TestMethod]
- public void VideoInfoTest_CanEncodeNvidia()
- {
- const string file = @"D:\videos\unprocessed\Bourne.mkv";
- const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
- var args = new FileFlows.Plugin.NodeParameters(file, new TestLogger(), false, string.Empty);
- //args.Process = new FileFlows.Plugin.ProcessHelper(args.Logger);
+ //[TestMethod]
+ //public void VideoInfoTest_CanEncodeNvidia()
+ //{
+ // const string file = @"D:\videos\unprocessed\Bourne.mkv";
+ // const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
+ // var args = new FileFlows.Plugin.NodeParameters(file, new TestLogger(), false, string.Empty);
+ // //args.Process = new FileFlows.Plugin.ProcessHelper(args.Logger);
- var node = new VideoEncode();
- node.SetArgs(args);
- bool result = node.CanProcessEncoder(ffmpeg, "hevc_nvenc -preset hq");
+ // var node = new VideoEncode();
+ // node.SetArgs(args);
+ // bool result = node.CanProcessEncoder(ffmpeg, "hevc_nvenc -preset hq");
- Assert.IsTrue(result);
- }
- [TestMethod]
- public void VideoInfoTest_CanEncodeIntel()
- {
- const string file = @"D:\videos\unprocessed\Bourne.mkv";
- const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
- var args = new FileFlows.Plugin.NodeParameters(file, new TestLogger(), false, string.Empty);
- //args.Process = new FileFlows.Plugin.ProcessHelper(args.Logger);
+ // Assert.IsTrue(result);
+ //}
+ //[TestMethod]
+ //public void VideoInfoTest_CanEncodeIntel()
+ //{
+ // const string file = @"D:\videos\unprocessed\Bourne.mkv";
+ // const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
+ // var args = new FileFlows.Plugin.NodeParameters(file, new TestLogger(), false, string.Empty);
+ // //args.Process = new FileFlows.Plugin.ProcessHelper(args.Logger);
- var node = new VideoEncode();
- node.SetArgs(args);
- bool result = node.CanProcessEncoder(ffmpeg, "h264_qsv");
+ // var node = new VideoEncode();
+ // node.SetArgs(args);
+ // bool result = node.CanProcessEncoder(ffmpeg, "h264_qsv");
- Assert.IsTrue(result);
- }
+ // Assert.IsTrue(result);
+ //}
[TestMethod]
diff --git a/VideoNodes/VideoNodes.csproj b/VideoNodes/VideoNodes.csproj
index e6872c47..e7657b20 100644
Binary files a/VideoNodes/VideoNodes.csproj and b/VideoNodes/VideoNodes.csproj differ
diff --git a/VideoNodes/VideoNodes.en.json b/VideoNodes/VideoNodes.en.json
index a7691ec8..b61e8e0d 100644
--- a/VideoNodes/VideoNodes.en.json
+++ b/VideoNodes/VideoNodes.en.json
@@ -107,6 +107,13 @@
"Percent-Help": "The threshold percentage value to use for scene detection changes. A good value is 45%"
}
},
+ "CanUseHardwareEncoding": {
+ "Description": "Checks if the specified hardware encoder is currently available to the Flow.",
+ "Fields": {
+ "Encoder": "Encoder",
+ "Encoder-Help": "The hardware decoder to check"
+ }
+ },
"ComskipChapters": {
"Description": "Uses a comskip EDL file and will create chapters given that EDL comskip file.",
"Outputs": {
diff --git a/VideoNodes/VideoNodes/EncodingNode.cs b/VideoNodes/VideoNodes/EncodingNode.cs
index 1e6701e3..9fd48e6c 100644
--- a/VideoNodes/VideoNodes/EncodingNode.cs
+++ b/VideoNodes/VideoNodes/EncodingNode.cs
@@ -22,7 +22,7 @@ namespace FileFlows.VideoNodes
return Encode(args, ffmpegExe, ffmpegParameters, out output, extension, outputFile, updateWorkingFile, dontAddInputFile: dontAddInputFile, dontAddOutputFile: dontAddOutputFile);
}
- public bool Encode(NodeParameters args, string ffmpegExe, List ffmpegParameters, out string ouput, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false, bool dontAddOutputFile = false)
+ public bool Encode(NodeParameters args, string ffmpegExe, List ffmpegParameters, out string output, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false, bool dontAddOutputFile = false)
{
if (string.IsNullOrEmpty(extension))
extension = "mkv";
@@ -55,7 +55,7 @@ namespace FileFlows.VideoNodes
}
Encoder.AtTime -= AtTimeEvent;
Encoder = null;
- ouput = success.output;
+ output = success.output;
return success.successs;
}
@@ -88,7 +88,7 @@ namespace FileFlows.VideoNodes
// try find best hevc encoder
foreach(string vidparam in new [] { "hevc_nvenc -preset hq", "hevc_qsv -global_quality 28 -load_plugin hevc_hw", "hevc_amf", "hevc_vaapi" })
{
- bool canProcess = CanProcessEncoder(ffmpeg, vidparam);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparam);
if (canProcess)
return vidparam;
}
@@ -99,7 +99,7 @@ namespace FileFlows.VideoNodes
// try find best hevc encoder
foreach (string vidparam in new[] { "h264_nvenc", "h264_qsv", "h264_amf", "h264_vaapi" })
{
- bool canProcess = CanProcessEncoder(ffmpeg, vidparam);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparam);
if (canProcess)
return vidparam;
}
@@ -109,7 +109,7 @@ namespace FileFlows.VideoNodes
if (vidparams.ToLower().Contains("hevc_nvenc"))
{
// nvidia h265 encoding, check can
- bool canProcess = CanProcessEncoder(ffmpeg, vidparams);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
if (canProcess == false)
{
// change to cpu encoding
@@ -121,7 +121,7 @@ namespace FileFlows.VideoNodes
else if (vidparams.ToLower().Contains("h264_nvenc"))
{
// nvidia h264 encoding, check can
- bool canProcess = CanProcessEncoder(ffmpeg, vidparams);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
if (canProcess == false)
{
// change to cpu encoding
@@ -133,7 +133,7 @@ namespace FileFlows.VideoNodes
else if (vidparams.ToLower().Contains("hevc_qsv"))
{
// nvidia h265 encoding, check can
- bool canProcess = CanProcessEncoder(ffmpeg, vidparams);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
if (canProcess == false)
{
// change to cpu encoding
@@ -145,7 +145,7 @@ namespace FileFlows.VideoNodes
else if (vidparams.ToLower().Contains("h264_qsv"))
{
// nvidia h264 encoding, check can
- bool canProcess = CanProcessEncoder(ffmpeg, vidparams);
+ bool canProcess = CanUseHardwareEncoding.CanProcess(Args, ffmpeg, vidparams);
if (canProcess == false)
{
// change to cpu encoding
@@ -157,25 +157,6 @@ namespace FileFlows.VideoNodes
return vidparams;
}
- public bool CanProcessEncoder(string ffmpeg, string encodingParams)
- {
- //ffmpeg -loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v hevc_nven2c -preset hq -f null -"
-
- string cmdArgs = $"-loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v {encodingParams} -f null -\"";
- var cmd = Args.Process.ExecuteShellCommand(new ExecuteArgs
- {
- Command = ffmpeg,
- Arguments = cmdArgs,
- Silent = true
- }).Result;
- if (cmd.ExitCode != 0 || string.IsNullOrWhiteSpace(cmd.Output) == false)
- {
- Args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
- return false;
- }
- return true;
- }
-
public bool HasNvidiaCard(string ffmpeg)
{
try
diff --git a/VideoNodes/VideoNodes/VideoNode.cs b/VideoNodes/VideoNodes/VideoNode.cs
index a33bfdcf..94ccb8a4 100644
--- a/VideoNodes/VideoNodes/VideoNode.cs
+++ b/VideoNodes/VideoNodes/VideoNode.cs
@@ -131,74 +131,6 @@ namespace FileFlows.VideoNodes
}
-
-
-
- private bool? HW_NVIDIA_265;
- ///
- /// Can process NVIDIA h265 hardware encoding
- ///
- /// true if can support NVIDIA h265 hardware encoding
- protected bool SupportsHardwareNvidia265()
- {
- if (HW_NVIDIA_265 == null)
- HW_NVIDIA_265 = CanProcessEncoder("hevc_nvenc");
- return HW_NVIDIA_265.Value;
- }
-
- private bool? HW_NVIDIA_264;
- ///
- /// Can process NVIDIA h264 hardware encoding
- ///
- /// true if can support NVIDIA h264 hardware encoding
- protected bool SupportsHardwareNvidia264()
- {
- if (HW_NVIDIA_264 == null)
- HW_NVIDIA_264 = CanProcessEncoder("h264_nvenc");
- return HW_NVIDIA_264.Value;
- }
-
-
- private bool? HW_QSV_265;
- ///
- /// Can process QSV h265 hardware encoding
- ///
- /// true if can support QSV h265 hardware encoding
- protected bool SupportsHardwareQsv265()
- {
- if (HW_QSV_265 == null)
- HW_QSV_265 = CanProcessEncoder("hevc_qsv");
- return HW_QSV_265.Value;
- }
- private bool? HW_QSV_264;
- ///
- /// Can process QSV h264 hardware encoding
- ///
- /// true if can support QSV h264 hardware encoding
- protected bool SupportsHardwareQsv264()
- {
- if (HW_QSV_264 == null)
- HW_QSV_264 = CanProcessEncoder("h264_qsv");
- return HW_QSV_264.Value;
- }
-
- public bool CanProcessEncoder(string encodingParams)
- {
- //ffmpeg -loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v hevc_nven2c -preset hq -f null -"
-
- string cmdArgs = $"-loglevel error -f lavfi -i color=black:s=1080x1080 -vframes 1 -an -c:v {encodingParams} -f null -\"";
- var cmd = Args.Process.ExecuteShellCommand(new ExecuteArgs
- {
- Command = FFMPEG,
- Arguments = cmdArgs,
- Silent = true
- }).Result;
- if (cmd.ExitCode != 0 || string.IsNullOrWhiteSpace(cmd.Output) == false)
- {
- Args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
- return false;
- }
- return true;
- }
+
}
}
\ No newline at end of file