From ba3e115ba0716ecfcdbed45356ec064584833829 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Mon, 6 Jun 2022 15:57:53 +1200 Subject: [PATCH] added CanUseHardwareEncoding --- .../Video/FfmpegBuilderVideoEncode.cs | 51 +++++- .../LogicalNodes/CanUseHardwareEncoding.cs | 167 ++++++++++++++++++ VideoNodes/Tests/VideoInfoHelperTests.cs | 48 ++--- VideoNodes/VideoNodes.csproj | Bin 4098 -> 4098 bytes VideoNodes/VideoNodes.en.json | 7 + VideoNodes/VideoNodes/EncodingNode.cs | 35 +--- VideoNodes/VideoNodes/VideoNode.cs | 70 +------- 7 files changed, 254 insertions(+), 124 deletions(-) create mode 100644 VideoNodes/LogicalNodes/CanUseHardwareEncoding.cs 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 e6872c47b26723ff9ad1c2040837ff1a4ff85633..e7657b20b8abadfcbef2173a04edc8075764540c 100644 GIT binary patch delta 36 jcmZotXj0g4i;2^aL65 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