mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-01-06 09:59:53 -06:00
231 lines
7.6 KiB
C#
231 lines
7.6 KiB
C#
using FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
|
|
|
|
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
|
|
|
|
/// <summary>
|
|
/// Set a video codec encoding for a video stream based on users settings
|
|
/// </summary>
|
|
public class FfmpegBuilderVideoEncode:FfmpegBuilderNode
|
|
{
|
|
/// <summary>
|
|
/// The number of outputs for this node
|
|
/// </summary>
|
|
public override int Outputs => 1;
|
|
|
|
internal const string CODEC_H264 = "h264";
|
|
internal const string CODEC_H264_10BIT = "h264 10BIT";
|
|
internal const string CODEC_H265 = "h265";
|
|
internal const string CODEC_H265_10BIT = "h265 10BIT";
|
|
|
|
/// <summary>
|
|
/// The Help URL for this node
|
|
/// </summary>
|
|
public override string HelpUrl => "https://docs.fileflows.com/plugins/video-nodes/ffmpeg-builder/video-encode";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the codec used to encode
|
|
/// </summary>
|
|
[DefaultValue(CODEC_H264_10BIT)]
|
|
[ChangeValue(nameof(Quality), 23, CODEC_H264)]
|
|
[ChangeValue(nameof(Quality), 23, CODEC_H265_10BIT)]
|
|
[ChangeValue(nameof(Quality), 28, CODEC_H265)]
|
|
[ChangeValue(nameof(Quality), 28, CODEC_H265_10BIT)]
|
|
[Select(nameof(CodecOptions), 1)]
|
|
public string Codec { get; set; }
|
|
|
|
private static List<ListOption> _CodecOptions;
|
|
/// <summary>
|
|
/// Gets or sets the codec options
|
|
/// </summary>
|
|
public static List<ListOption> CodecOptions
|
|
{
|
|
get
|
|
{
|
|
if (_CodecOptions == null)
|
|
{
|
|
_CodecOptions = new List<ListOption>
|
|
{
|
|
new () { Label = "H.264", Value = CODEC_H264 },
|
|
new () { Label = "H.264 (10-Bit)", Value = CODEC_H264_10BIT },
|
|
new () { Label = "H.265", Value = CODEC_H265 },
|
|
new () { Label = "H.265 (10-Bit)", Value = CODEC_H265_10BIT },
|
|
};
|
|
}
|
|
return _CodecOptions;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets if hardware encoding should be used if possible
|
|
/// </summary>
|
|
[DefaultValue(true)]
|
|
[Boolean(2)]
|
|
public bool HardwareEncoding { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the quality of the video encode
|
|
/// </summary>
|
|
[Slider(3, inverse: true)]
|
|
[Range(0, 51)]
|
|
[DefaultValue(28)]
|
|
public int Quality { get; set; }
|
|
|
|
//private string bit10Filter = "yuv420p10le";
|
|
private string[] bit10Filters = new[]
|
|
{
|
|
"-pix_fmt:v:{index}", "p010le", "-profile:v:{index}", "main10"
|
|
};
|
|
private string[] non10BitFilters = new string[]{};
|
|
|
|
/// <summary>
|
|
/// Executes the node
|
|
/// </summary>
|
|
/// <param name="args">The node arguments</param>
|
|
/// <returns>the output return</returns>
|
|
public override int Execute(NodeParameters args)
|
|
{
|
|
var stream = Model.VideoStreams.Where(x => x.Deleted == false).First();
|
|
if (Codec == CODEC_H264 || Codec == CODEC_H264_10BIT)
|
|
H264(stream, Codec == CODEC_H264_10BIT);
|
|
else if (Codec == CODEC_H265 || Codec == CODEC_H265_10BIT)
|
|
H265(stream, Codec == CODEC_H265_10BIT);
|
|
else
|
|
{
|
|
args.Logger?.ILog("Unknown codec: " + Codec);
|
|
return 2;
|
|
}
|
|
|
|
stream.ForcedChange = true;
|
|
return 1;
|
|
}
|
|
|
|
private void H264(FfmpegVideoStream stream, bool tenBit)
|
|
{
|
|
if (HardwareEncoding == false)
|
|
H26x_CPU(stream, false);
|
|
else if (CanUseHardwareEncoding.CanProcess_Nvidia_H264(Args))
|
|
H26x_Nvidia(stream, false);
|
|
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, false);
|
|
|
|
if (tenBit)
|
|
stream.EncodingParameters.AddRange(bit10Filters);
|
|
}
|
|
|
|
private void H265(FfmpegVideoStream stream, bool tenBit)
|
|
{
|
|
// 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, true);
|
|
else if (CanUseHardwareEncoding.CanProcess_Nvidia_Hevc(Args))
|
|
H26x_Nvidia(stream, true);
|
|
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, true);
|
|
|
|
if (tenBit)
|
|
stream.EncodingParameters.AddRange(bit10Filters);
|
|
else if(non10BitFilters?.Any() == true)
|
|
stream.EncodingParameters.AddRange(non10BitFilters);
|
|
}
|
|
|
|
|
|
private void H26x_CPU(FfmpegVideoStream stream, bool h265)
|
|
{
|
|
stream.EncodingParameters.Clear();
|
|
stream.EncodingParameters.AddRange(new []
|
|
{
|
|
h265 ? "libx265" : "libx264",
|
|
"-preset", "slow",
|
|
"-crf", Quality.ToString()
|
|
});
|
|
bit10Filters = new[]
|
|
{
|
|
"-pix_fmt:v:{index}", "yuv420p10le", "-profile:v:{index}", "main10"
|
|
};
|
|
}
|
|
|
|
private void H26x_Nvidia(FfmpegVideoStream stream, bool h265)
|
|
{
|
|
stream.EncodingParameters.Clear();
|
|
stream.EncodingParameters.AddRange(new []
|
|
{
|
|
h265 ? "hevc_nvenc" : "h264_nvenc",
|
|
"-rc", "constqp",
|
|
"-qp", Quality.ToString(),
|
|
//"-b:v", "0K", // this would do a two-pass... slower
|
|
"-preset", "p6",
|
|
// https://www.reddit.com/r/ffmpeg/comments/gg5szi/what_is_spatial_aq_and_temporal_aq_with_nvenc/
|
|
"-spatial-aq", "1"
|
|
});
|
|
if (h265 == false) {
|
|
non10BitFilters = new[] { "-pix_fmt:v:{index}", "yuv420p" };
|
|
}
|
|
}
|
|
|
|
private void H26x_Qsv(FfmpegVideoStream stream, bool h265)
|
|
{
|
|
//hevc_qsv -load_plugin hevc_hw -pix_fmt p010le -profile:v main10 -global_quality 21 -g 24 -look_ahead 1 -look_ahead_depth 60
|
|
stream.EncodingParameters.Clear();
|
|
if (h265)
|
|
{
|
|
stream.EncodingParameters.AddRange(new[]
|
|
{
|
|
"hevc_qsv",
|
|
"-load_plugin", "hevc_hw"
|
|
});
|
|
}
|
|
else
|
|
{
|
|
stream.EncodingParameters.AddRange(new[]
|
|
{
|
|
"h264_qsv"
|
|
});
|
|
|
|
}
|
|
stream.EncodingParameters.AddRange(new[]
|
|
{
|
|
"-global_quality", Quality.ToString(),
|
|
"-preset", "slower",
|
|
});
|
|
}
|
|
|
|
private void H26x_Amd(FfmpegVideoStream stream, bool h265)
|
|
{
|
|
stream.EncodingParameters.Clear();
|
|
stream.EncodingParameters.AddRange(new[]
|
|
{
|
|
h265 ? "hevc_amf" : "h264_amf",
|
|
"-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"
|
|
});
|
|
}
|
|
private void H26x_Vaapi(FfmpegVideoStream stream, bool h265)
|
|
{
|
|
stream.EncodingParameters.Clear();
|
|
stream.EncodingParameters.AddRange(new[]
|
|
{
|
|
h265 ? "hevc_vaapi" : "h264_vaapi",
|
|
"-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"
|
|
});
|
|
}
|
|
}
|