added CanUseHardwareEncoding

This commit is contained in:
John Andrews
2022-06-06 15:57:53 +12:00
parent 73d6d12958
commit ba3e115ba0
7 changed files with 254 additions and 124 deletions

View File

@@ -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";
}
}

View File

@@ -0,0 +1,167 @@
namespace FileFlows.VideoNodes;
/// <summary>
/// Node for checking if Flow Runner has access to hardware
/// </summary>
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<ListOption> _EncoderOptions;
public static List<ListOption> EncoderOptions
{
get
{
if (_EncoderOptions == null)
{
_EncoderOptions = new List<ListOption>
{
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;
}
/// <summary>
/// Checks if this flow runner can use NVIDIA HEVC encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Nvidia_Hevc(NodeParameters args) => CanProcess(args, "hevc_nvenc");
/// <summary>
/// Checks if this flow runner can use NVIDIA H.264 encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Nvidia_H264(NodeParameters args) => CanProcess(args, "h264_nvenc");
/// <summary>
/// Checks if this flow runner can use AND HEVC encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Amd_Hevc(NodeParameters args) => CanProcess(args, "hevc_amf");
/// <summary>
/// Checks if this flow runner can use AND H.264 encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Amd_H264(NodeParameters args) => CanProcess(args, "h264_amf");
/// <summary>
/// Checks if this flow runner can use Intels QSV HEVC encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Qsv_Hevc(NodeParameters args) => CanProcess(args, "hevc_qsv -global_quality 28 -load_plugin hevc_hw");
/// <summary>
/// Checks if this flow runner can use Intels QSV H.264 encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Qsv_H264(NodeParameters args) => CanProcess(args, "h264_qsv");
/// <summary>
/// Checks if this flow runner can use VAAPI HEVC encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
internal static bool CanProcess_Vaapi_Hevc(NodeParameters args) => CanProcess(args, "hevc_vaapi");
/// <summary>
/// Checks if this flow runner can use VAAPI H.264 encoder
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>true if can use it, otherwise false</returns>
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;
}
}

View File

@@ -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]

Binary file not shown.

View File

@@ -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": {

View File

@@ -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<string> 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<string> 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

View File

@@ -131,74 +131,6 @@ namespace FileFlows.VideoNodes
}
private bool? HW_NVIDIA_265;
/// <summary>
/// Can process NVIDIA h265 hardware encoding
/// </summary>
/// <returns>true if can support NVIDIA h265 hardware encoding</returns>
protected bool SupportsHardwareNvidia265()
{
if (HW_NVIDIA_265 == null)
HW_NVIDIA_265 = CanProcessEncoder("hevc_nvenc");
return HW_NVIDIA_265.Value;
}
private bool? HW_NVIDIA_264;
/// <summary>
/// Can process NVIDIA h264 hardware encoding
/// </summary>
/// <returns>true if can support NVIDIA h264 hardware encoding</returns>
protected bool SupportsHardwareNvidia264()
{
if (HW_NVIDIA_264 == null)
HW_NVIDIA_264 = CanProcessEncoder("h264_nvenc");
return HW_NVIDIA_264.Value;
}
private bool? HW_QSV_265;
/// <summary>
/// Can process QSV h265 hardware encoding
/// </summary>
/// <returns>true if can support QSV h265 hardware encoding</returns>
protected bool SupportsHardwareQsv265()
{
if (HW_QSV_265 == null)
HW_QSV_265 = CanProcessEncoder("hevc_qsv");
return HW_QSV_265.Value;
}
private bool? HW_QSV_264;
/// <summary>
/// Can process QSV h264 hardware encoding
/// </summary>
/// <returns>true if can support QSV h264 hardware encoding</returns>
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;
}
}
}