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 HelpUrl => "https://docs.fileflows.com/plugins/video-nodes/logical-nodes/can-use-hardware-encoding"; 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"); /// /// Gets if a encoder/decoder has been disabled by a variable /// /// the node parameters /// the parameters to check /// if a encoder/decoder has been disabled by a variable internal static bool DisabledByVariables(NodeParameters args, string parameters) { if (parameters.ToLower().Contains("nvenc")) { if (args.GetVariable("NoNvidia") as bool? == true) return true; if (args.GetVariable("NoNVIDIA") as bool? == true) return true; } else if (parameters.ToLower().Contains("qsv")) { if (args.GetVariable("NoQSV") as bool? == true) return true; } else if (parameters.ToLower().Contains("vaapi")) { if (args.GetVariable("NoVAAPI") as bool? == true) return true; } else if (parameters.ToLower().Contains("amf")) { if (args.GetVariable("NoAMF") as bool? == true) return true; if (args.GetVariable("NoAMD") as bool? == true) return true; } return false; } private static bool CanProcess(NodeParameters args, string encodingParams) { if (DisabledByVariables(args, encodingParams)) return false; string ffmpeg = args.GetToolPath("FFMpeg"); if (string.IsNullOrEmpty(ffmpeg)) { args.Logger.ELog("FFMpeg tool not found."); return false; } return CanProcess(args, ffmpeg, encodingParams); } /// /// Tests if the encoding parameters can be executed /// /// the node paramterse /// the location of ffmpeg /// the encoding parameter to test /// true if can be processed internal static bool CanProcess(NodeParameters args, string ffmpeg, string encodingParams) { bool can = CanExecute(); if (can == false && encodingParams?.Contains("amf") == true) { // AMD/AMF has a issue where it reports false at first but then passes // https://github.com/revenz/FileFlows/issues/106 Thread.Sleep(2000); can = CanExecute(); } return can; bool CanExecute() { 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; } } }