add PreExecute to Video Nodes

started adding Video Encode node for basic settings
This commit is contained in:
John Andrews
2022-05-19 01:48:29 +12:00
parent 5c5bfe842a
commit 80d4ee23d3
61 changed files with 368 additions and 307 deletions

View File

@@ -95,8 +95,6 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
var audio = new FfmpegAudioStream();
var bestAudio = GetBestAudioTrack(args, Model.AudioStreams.Select(x => x.Stream));

View File

@@ -24,12 +24,6 @@ public class FfmpegBuilderAudioAdjustVolume : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (Model.AudioStreams?.Any() != true)
{
args.Logger?.ILog("No audio streams detected");

View File

@@ -24,12 +24,6 @@ public class FfmpegBuilderAudioNormalization : FfmpegBuilderNode
[RequiresUnreferencedCode("")]
public override int Execute(NodeParameters args)
{
base.Init(args);
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (Model.AudioStreams?.Any() != true)
{
args.Logger?.ILog("No audio streams detected");
@@ -60,7 +54,7 @@ public class FfmpegBuilderAudioNormalization : FfmpegBuilderNode
item.stream.Filter.Add(normalizedTracks[audio.TypeIndex]);
else
{
string twoPass = AudioNormalization.DoTwoPass(this, args, ffmpegExe, audio.TypeIndex);
string twoPass = AudioNormalization.DoTwoPass(this, args, FFMPEG, audio.TypeIndex);
item.stream.Filter.Add(twoPass);
normalizedTracks.Add(audio.TypeIndex, twoPass);
}

View File

@@ -16,8 +16,6 @@ public class FfmpegBuilderAudioSetLanguage : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
bool changes = false;
foreach (var at in Model.AudioStreams)
{

View File

@@ -28,7 +28,6 @@ public class FfmpegBuilderAudioTrackRemover: FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
this.Init(args);
bool removing = false;
Regex? regex = null;
int index = -1;

View File

@@ -20,8 +20,6 @@ public class FfmpegBuilderAudioTrackReorder : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
OrderedTracks = OrderedTracks?.Select(x => x.ToLower())?.ToList() ?? new();
var reordered = Reorder(Model.AudioStreams);

View File

@@ -20,7 +20,6 @@ public class FfmpegBuilderAddInputFile : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
this.Init(args);
var dir = new FileInfo(UseSourceDirectory ? args.FileName : args.WorkingFile).Directory;
if (dir.Exists == false)
{

View File

@@ -18,7 +18,6 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
public override int Execute(NodeParameters args)
{
this.Init(args);
var model = this.Model;
List<string> ffArgs = new List<string>();
ffArgs.AddRange(new[] { "-strict", "-2" }); // allow experimental stuff
@@ -80,7 +79,7 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
ffArgs = startArgs.Concat(ffArgs).ToList();
if (Encode(args, ffmpegExe, ffArgs, extension, dontAddInputFile: true) == false)
if (Encode(args, FFMPEG, ffArgs, extension, dontAddInputFile: true) == false)
return -1;
return 1;
@@ -88,15 +87,15 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
internal string[] GetHardwareDecodingArgs()
{
string testFile = Path.Combine(args.TempPath, Guid.NewGuid() + ".hwtest.mkv");
string testFile = Path.Combine(Args.TempPath, Guid.NewGuid() + ".hwtest.mkv");
foreach(var hw in new [] { "cuda", "dxva2", "qsv", "d3d11va", "opencl" })
{
// ffmpeg -y -hwaccel qsvf -f lavfi -i color=color=red -frames:v 10 test.mkv
try
{
var result = args.Execute(new ExecuteArgs
var result = Args.Execute(new ExecuteArgs
{
Command = ffmpegExe,
Command = FFMPEG,
ArgumentList = new[]
{
"-y",
@@ -109,14 +108,14 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
});
if (result.ExitCode == 0)
{
args.Logger?.ILog("Supported hardware decoding detected: " + hw);
Args.Logger?.ILog("Supported hardware decoding detected: " + hw);
return new[] { "-hwaccel", hw };
}
}
catch (Exception) { }
}
args.Logger?.ILog("No hardware decoding availble");
Args.Logger?.ILog("No hardware decoding availble");
return new string[] { };
}
}

View File

@@ -6,7 +6,6 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
public abstract class FfmpegBuilderNode: EncodingNode
{
private const string MODEL_KEY = "FFMPEG_BUILDER_MODEL";
protected string ffmpegExe;
public override int Inputs => 1;
public override int Outputs => 1;
@@ -14,15 +13,15 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
public override FlowElementType Type => FlowElementType.BuildPart;
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder";
protected void Init(NodeParameters args)
{
this.args = args;
this.ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
throw new Exception("FFMPEG not found");
public override bool PreExecute(NodeParameters args)
{
if (base.PreExecute(args) == false)
return false;
if (Model == null)
throw new Exception("FFMPEG Builder Model not set, use the \"FFMPEG Builder Start\" node to first");
return true;
}
public override int Execute(NodeParameters args)
@@ -36,21 +35,21 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
{
get
{
if (args.Variables.ContainsKey(MODEL_KEY))
return args.Variables[MODEL_KEY] as FfmpegModel;
if (Args.Variables.ContainsKey(MODEL_KEY))
return Args.Variables[MODEL_KEY] as FfmpegModel;
return null;
}
set
{
if (args.Variables.ContainsKey(MODEL_KEY))
if (Args.Variables.ContainsKey(MODEL_KEY))
{
if (value == null)
args.Variables.Remove(MODEL_KEY);
Args.Variables.Remove(MODEL_KEY);
else
args.Variables[MODEL_KEY] = value;
Args.Variables[MODEL_KEY] = value;
}
else if(value != null)
args.Variables.Add(MODEL_KEY, value);
Args.Variables.Add(MODEL_KEY, value);
}
}
@@ -102,7 +101,7 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
matchString = subtitle.Stream.Title + ":" + subtitle.Stream.Language + ":" + subtitle.Stream.Codec;
else
return false;
args.Logger?.ILog($"Track [{index}] test string: {matchString}");
Args.Logger?.ILog($"Track [{index}] test string: {matchString}");
match = new Regex(pattern, RegexOptions.IgnoreCase).IsMatch(matchString);
}

View File

@@ -11,13 +11,11 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
public override int Execute(NodeParameters args)
{
this.args = args;
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
this.Model = Models.FfmpegModel.CreateModel(videoInfo);
this.Init(args);
return 1;
}
}

View File

@@ -15,8 +15,6 @@
public override int Execute(NodeParameters args)
{
base.Init(args);
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
@@ -27,7 +25,7 @@
return 2;
}
string tempMetaDataFile = AutoChapters.GenerateMetaDataFile(this, args, videoInfo, ffmpegExe, this.Percent, this.MinimumLength);
string tempMetaDataFile = AutoChapters.GenerateMetaDataFile(this, args, videoInfo, FFMPEG, this.Percent, this.MinimumLength);
if (string.IsNullOrEmpty(tempMetaDataFile))
return 2;

View File

@@ -7,8 +7,6 @@ public class FfmpegBuilderComskipChapters : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;

View File

@@ -43,8 +43,6 @@ public class FfmpegBuilderSubtitleFormatRemover : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
this.Init(args);
if (RemoveAll)
{
if (Model.SubtitleStreams.Any() == false)

View File

@@ -42,7 +42,6 @@ public class FfmpegBuilderSubtitleTrackMerge : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
this.Init(args);
var dir = new FileInfo(UseSourceDirectory ? args.FileName : args.WorkingFile).Directory;
if (dir.Exists == false)
{

View File

@@ -20,7 +20,6 @@ public class FfmpegBuilderSubtitleTrackRemover : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
this.Init(args);
bool removing = false;
var regex = new Regex(this.Pattern, RegexOptions.IgnoreCase);
foreach(var stream in Model.SubtitleStreams)

View File

@@ -1,41 +1,33 @@
namespace FileFlows.VideoNodes.FfmpegBuilderNodes
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
public class FfmpegBuilderCropBlackBars : FfmpegBuilderNode
{
public class FfmpegBuilderCropBlackBars : FfmpegBuilderNode
[NumberInt(1)]
[DefaultValue(10)]
public int CroppingThreshold { get; set; }
public override int Outputs => 2;
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Crop-Black-Bars";
public override int Execute(NodeParameters args)
{
[NumberInt(1)]
[DefaultValue(10)]
public int CroppingThreshold { get; set; }
public override int Outputs => 2;
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Crop-Black-Bars";
public override int Execute(NodeParameters args)
{
base.Init(args);
string ffmpeg = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpeg))
return -1;
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;
string crop = DetectBlackBars.Detect(ffmpeg, videoInfo, args, this.CroppingThreshold);
if (string.IsNullOrWhiteSpace(crop))
return 2;
string crop = DetectBlackBars.Detect(FFMPEG, videoInfo, args, this.CroppingThreshold);
if (string.IsNullOrWhiteSpace(crop))
return 2;
//var parts = crop.Split(':');
////parts[2] = "iw-" + parts[2];
////parts[3] = "ih-" + parts[3];
//crop = String.Join(":", parts.Take(2));
//var parts = crop.Split(':');
////parts[2] = "iw-" + parts[2];
////parts[3] = "ih-" + parts[3];
//crop = String.Join(":", parts.Take(2));
args.Logger?.ILog("Black bars detected, crop: " + crop);
args.Logger?.ILog("Black bars detected, crop: " + crop);
var video = Model.VideoStreams[0];
video.Filter.AddRange(new[] { "crop=" + crop });
return 1;
}
var video = Model.VideoStreams[0];
video.Filter.AddRange(new[] { "crop=" + crop });
return 1;
}
}
}

View File

@@ -1,29 +1,26 @@
namespace FileFlows.VideoNodes.FfmpegBuilderNodes
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
public class FfmpegBuilderHdrToSdr : FfmpegBuilderNode
{
public class FfmpegBuilderHdrToSdr : FfmpegBuilderNode
public override int Outputs => 2;
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-HDR-to-SDR";
public override int Execute(NodeParameters args)
{
public override int Outputs => 2;
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-HDR-to-SDR";
public override int Execute(NodeParameters args)
var vidStream = Model.VideoStreams?.Where(x => x.Deleted == false && x.Stream?.HDR == true).FirstOrDefault();
if (vidStream == null)
{
base.Init(args);
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;
var vidStream = Model.VideoStreams?.Where(x => x.Deleted == false && x.Stream?.HDR == true).FirstOrDefault();
if (vidStream == null)
{
args.Logger.ILog("No HDR video stream found");
return 2;
}
vidStream.Filter.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p");
return 1;
args.Logger.ILog("No HDR video stream found");
return 2;
}
vidStream.Filter.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p");
return 1;
}
}
}

View File

@@ -6,7 +6,6 @@ public class FfmpegBuilderRemuxToMP4: FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
this.Model.Extension = "mp4";
return 1;
}

View File

@@ -1,12 +1,12 @@
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
public class FfmpegBuilderRemuxToMkv: FfmpegBuilderNode
public class FfmpegBuilderRemuxToMkv : FfmpegBuilderNode
{
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Remux-to-MKV";
public override int Execute(NodeParameters args)
{
base.Init(args);
this.Model.Extension = "mkv";
return 1;
}
}
}

View File

@@ -36,12 +36,6 @@ public class FfmpegBuilderScaler : FfmpegBuilderNode
public override int Outputs => 2;
public override int Execute(NodeParameters args)
{
base.Init(args);
string ffmpeg = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpeg))
return -1;
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;

View File

@@ -8,8 +8,6 @@
public override int Execute(NodeParameters args)
{
base.Init(args);
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;

View File

@@ -17,8 +17,6 @@ public class FfmpegBuilderVideoBitrate : FfmpegBuilderNode
public override int Execute(NodeParameters args)
{
base.Init(args);
var video = Model.VideoStreams?.Where(x => x.Deleted == false)?.FirstOrDefault();
if (video?.Stream == null)
{

View File

@@ -23,15 +23,13 @@
public override int Execute(NodeParameters args)
{
base.Init(args);
string codec = args.ReplaceVariables(VideoCodec ?? string.Empty);
string parameters = args.ReplaceVariables(VideoCodecParameters ?? codec);
if (string.IsNullOrWhiteSpace(parameters))
return 1; // nothing to do
parameters = CheckVideoCodec(ffmpegExe, parameters);
parameters = CheckVideoCodec(FFMPEG, parameters);
bool encoding = false;
foreach (var item in Model.VideoStreams.Select((x, index) => (stream: x, index)))

View File

@@ -0,0 +1,118 @@
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
{
public override int Outputs => 1;
private const string CODEC_H264 = "h264";
private const string CODEC_H265 = "h265";
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Video-Encode";
[DefaultValue("h265")]
[Select(nameof(CodecOptions), 1)]
public string Codec { get; set; }
private static List<ListOption> _CodecOptions;
public static List<ListOption> CodecOptions
{
get
{
if (_CodecOptions == null)
{
_CodecOptions = new List<ListOption>
{
new () { Label = "h264", Value = CODEC_H264 },
new () { Label = "h265", Value = CODEC_H265 }
};
}
return _CodecOptions;
}
}
[DefaultValue(true)]
[Boolean(2)]
public bool HardwareEncoding { get; set; }
[Slider(3)]
[Range(0, 51)]
[DefaultValue(23)]
public int Quality { get; set; }
public override int Execute(NodeParameters args)
{
var stream = Model.VideoStreams.Where(x => x.Deleted == false).First();
if (Codec == CODEC_H264)
H264(stream);
else if (Codec == CODEC_H264)
H265(stream);
bool encoding = false;
return encoding ? 1 : 2;
}
private void H264(FfmpegVideoStream stream)
{
if (HardwareEncoding == false)
H26x_CPU(stream);
else if (SupportsHardwareNvidia264())
H264_Nvidia(stream);
else
H26x_CPU(stream);
}
private void H265(FfmpegVideoStream stream)
{
// 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())
H265_Nvidia(stream);
else
H26x_CPU(stream);
}
private void H26x_CPU(FfmpegVideoStream stream)
{
stream.EncodingParameters.Clear();
stream.EncodingParameters.AddRange(new []
{
Codec == CODEC_H265 ? "libx265" : "libx264",
"-preset", "slow",
"-crf", Quality.ToString()
});
}
private void H264_Nvidia(FfmpegVideoStream stream)
{
stream.EncodingParameters.Clear();
stream.EncodingParameters.AddRange(new []
{
"h264_nvenc",
"-rc", "vbr_hq",
// 0 == auto, so we set to 1
"-cq", Quality <= 0 ? "1" : Quality.ToString(),
});
}
private void H265_Nvidia(FfmpegVideoStream stream)
{
stream.EncodingParameters.Clear();
stream.EncodingParameters.AddRange(new []
{
"hevc_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"
});
}
}

View File

@@ -51,16 +51,12 @@ namespace FileFlows.VideoNodes
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
VideoInfoHelper.ProbeSize = this.ProbeSize;
try
{
var videoInfo = new VideoInfoHelper(ffmpegExe, args.Logger).Read(args.WorkingFile);
var videoInfo = new VideoInfoHelper(FFMPEG, args.Logger).Read(args.WorkingFile);
if (videoInfo.VideoStreams.Any() == false)
{
args.Logger.ILog("No video streams detected.");

View File

@@ -32,16 +32,12 @@ namespace FileFlows.VideoNodes
public override int Execute(NodeParameters args)
{
string ffmpeg = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpeg))
return -1;
var videoInfo = GetVideoInfo(args);
if (videoInfo == null || videoInfo.VideoStreams?.Any() != true)
return -1;
string crop = Detect(ffmpeg, videoInfo, args, this.CroppingThreshold);
string crop = Detect(FFMPEG, videoInfo, args, this.CroppingThreshold);
if (crop == string.Empty)
return 2;

Binary file not shown.

View File

@@ -438,6 +438,20 @@
"DisableOtherVideoStreams-Help": "When checked if there are multiple video streams in the file, this will remove all but the first video stream from the file once executed."
}
},
"FfmpegBuilderVideoEncode": {
"Label": "FFMPEG Builder: Video Encode",
"Description": "Sets the FFMPEG Builder to encode the video with simple to use presets",
"Outputs": {
"1": "FFMPEG Builder video streams set to encode"
},
"Fields": {
"Codec": "Codec",
"HardwareEncoding": "Hardware Encode",
"HardwareEncoding-Help": "When checked, will test to see if hardware encoders are found on the Processing Node, and if found will use hardware encoding, otherwise will fallback to CPU encoding.",
"Quality": "Quality",
"Quality-Help": "Sets the quality of the video"
}
},
"RemuxToMKV": {
"Descritption": "Remuxes a video file into a MKV container. All streams will be copied to the new container",
"Outputs": {

View File

@@ -101,10 +101,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>
{
"-c", "copy",
@@ -145,7 +141,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -26,11 +26,7 @@
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (videoInfo.AudioStreams?.Any() != true)
{
args.Logger?.ILog("No audio streams detected");
@@ -39,7 +35,7 @@
if(VolumePercent == 100)
{
args.Logger?.ILog("Volume percent set to 100, no adjustment necassary");
args.Logger?.ILog("Volume percent set to 100, no adjustment necessary");
return 2;
}
@@ -63,7 +59,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -39,10 +39,6 @@ public class AudioNormalization: EncodingNode
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (videoInfo.AudioStreams?.Any() != true)
{
args.Logger?.ILog("No audio streams detected");
@@ -82,7 +78,7 @@ public class AudioNormalization: EncodingNode
{
if (TwoPass)
{
string twoPass = DoTwoPass(this, args, ffmpegExe, j);
string twoPass = DoTwoPass(this, args, FFMPEG, j);
ffArgs.AddRange(new[] { "-map", $"0:a:{j}", "-c:a:" + j, audio.Codec, "-filter:a:" + j, twoPass });
}
else
@@ -113,7 +109,7 @@ public class AudioNormalization: EncodingNode
if (extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -30,10 +30,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>
{
"-c", "copy",
@@ -74,7 +70,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -92,10 +92,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>
{
"-c", "copy",
@@ -130,7 +126,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -24,10 +24,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>();
int index = 0;
@@ -54,7 +50,7 @@
args.Logger?.DLog("Working file: " + args.WorkingFile);
args.Logger?.DLog("Extension: " + extension);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -25,9 +25,6 @@
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
@@ -38,12 +35,12 @@
return 2;
}
string tempMetaDataFile = GenerateMetaDataFile(this, args, videoInfo, ffmpegExe, this.Percent, this.MinimumLength);
string tempMetaDataFile = GenerateMetaDataFile(this, args, videoInfo, FFMPEG, this.Percent, this.MinimumLength);
if (string.IsNullOrEmpty(tempMetaDataFile))
return 2;
string[] ffArgs = new[] { "-i", tempMetaDataFile, "-map_metadata", "1", "-codec", "copy", "-max_muxing_queue_size", "1024" };
if (Encode(args, ffmpegExe, ffArgs.ToList()))
if (Encode(args, FFMPEG, ffArgs.ToList()))
{
args.Logger?.ILog($"Adding chapters to file");
return 1;

View File

@@ -15,9 +15,6 @@
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
@@ -27,7 +24,7 @@
return 2;
string[] ffArgs = new[] { "-i", tempMetaDataFile, "-map_metadata", "1", "-codec", "copy", "-max_muxing_queue_size", "1024" };
if (Encode(args, ffmpegExe, ffArgs.ToList()))
if (Encode(args, FFMPEG, ffArgs.ToList()))
{
args.Logger?.ILog($"Added chapters to file");
return 1;

View File

@@ -15,9 +15,6 @@
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
@@ -109,7 +106,7 @@
"-c", "copy"
};
bool concatResult = Encode(args, ffmpegExe, ffArgs, dontAddInputFile: true, extension: extension);
bool concatResult = Encode(args, FFMPEG, ffArgs, dontAddInputFile: true, extension: extension);
foreach(string segment in segments.Union(new[] { concatList }))
{
@@ -140,7 +137,7 @@
"-t", duration.ToString(),
"-c", "copy"
};
if (Encode(args, ffmpegExe, ffArgs, outputFile: segment, updateWorkingFile: false))
if (Encode(args, FFMPEG, ffArgs, outputFile: segment, updateWorkingFile: false))
{
segments.Add(segment);
segmentsInfo.Add(DebugString(start, end));

View File

@@ -14,8 +14,6 @@ namespace FileFlows.VideoNodes
protected TimeSpan TotalTime;
protected NodeParameters args;
private FFMpegEncoder Encoder;
public bool Encode(NodeParameters args, string ffmpegExe, List<string> ffmpegParameters, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false, bool dontAddOutputFile = false)
@@ -29,7 +27,6 @@ namespace FileFlows.VideoNodes
if (string.IsNullOrEmpty(extension))
extension = "mkv";
this.args = args;
Encoder = new FFMpegEncoder(ffmpegExe, args.Logger);
Encoder.AtTime += AtTimeEvent;
@@ -73,26 +70,14 @@ namespace FileFlows.VideoNodes
{
if (TotalTime.TotalMilliseconds == 0)
{
args?.Logger?.DLog("Can't report time progress as total time is 0");
Args?.Logger?.DLog("Can't report time progress as total time is 0");
return;
}
float percent = (float)((time.TotalMilliseconds / TotalTime.TotalMilliseconds) * 100);
if(args?.PartPercentageUpdate != null)
args.PartPercentageUpdate(percent);
if(Args?.PartPercentageUpdate != null)
Args.PartPercentageUpdate(percent);
}
#if (DEBUG)
/// <summary>
/// Used for unit tests
/// </summary>
/// <param name="args">the args</param>
public void SetArgs(NodeParameters args)
{
this.args = args;
}
#endif
public string CheckVideoCodec(string ffmpeg, string vidparams)
{
if (string.IsNullOrEmpty(vidparams))
@@ -128,7 +113,7 @@ namespace FileFlows.VideoNodes
if (canProcess == false)
{
// change to cpu encoding
args.Logger?.ILog("Can't encode using hevc_nvenc, falling back to CPU encoding H265 (libx265)");
Args.Logger?.ILog("Can't encode using hevc_nvenc, falling back to CPU encoding H265 (libx265)");
return "libx265";
}
return vidparams;
@@ -140,7 +125,7 @@ namespace FileFlows.VideoNodes
if (canProcess == false)
{
// change to cpu encoding
args.Logger?.ILog("Can't encode using h264_nvenc, falling back to CPU encoding H264 (libx264)");
Args.Logger?.ILog("Can't encode using h264_nvenc, falling back to CPU encoding H264 (libx264)");
return "libx264";
}
return vidparams;
@@ -152,7 +137,7 @@ namespace FileFlows.VideoNodes
if (canProcess == false)
{
// change to cpu encoding
args.Logger?.ILog("Can't encode using hevc_qsv, falling back to CPU encoding H265 (libx265)");
Args.Logger?.ILog("Can't encode using hevc_qsv, falling back to CPU encoding H265 (libx265)");
return "libx265";
}
return vidparams;
@@ -164,7 +149,7 @@ namespace FileFlows.VideoNodes
if (canProcess == false)
{
// change to cpu encoding
args.Logger?.ILog("Can't encode using h264_qsv, falling back to CPU encoding H264 (libx264)");
Args.Logger?.ILog("Can't encode using h264_qsv, falling back to CPU encoding H264 (libx264)");
return "libx264";
}
return vidparams;
@@ -177,7 +162,7 @@ namespace FileFlows.VideoNodes
//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
var cmd = Args.Process.ExecuteShellCommand(new ExecuteArgs
{
Command = ffmpeg,
Arguments = cmdArgs,
@@ -185,7 +170,7 @@ namespace FileFlows.VideoNodes
}).Result;
if (cmd.ExitCode != 0 || string.IsNullOrWhiteSpace(cmd.Output) == false)
{
args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
Args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
return false;
}
return true;
@@ -197,7 +182,7 @@ namespace FileFlows.VideoNodes
{
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
var cmd = args.Process.ExecuteShellCommand(new ExecuteArgs
var cmd = Args.Process.ExecuteShellCommand(new ExecuteArgs
{
Command = "wmic",
Arguments = "path win32_VideoController get name"
@@ -222,7 +207,7 @@ namespace FileFlows.VideoNodes
}
// check cuda in ffmpeg itself
var result = args.Process.ExecuteShellCommand(new ExecuteArgs
var result = Args.Process.ExecuteShellCommand(new ExecuteArgs
{
Command = ffmpeg,
Arguments = "-hide_banner -init_hw_device list"
@@ -231,7 +216,7 @@ namespace FileFlows.VideoNodes
}
catch (Exception ex)
{
args.Logger?.ELog("Failed to detect NVIDIA card: " + ex.Message + Environment.NewLine + ex.StackTrace);
Args.Logger?.ELog("Failed to detect NVIDIA card: " + ex.Message + Environment.NewLine + ex.StackTrace);
return false;
}
}

View File

@@ -41,12 +41,8 @@ namespace FileFlows.VideoNodes
args.Logger.ELog("Command Line not set");
return -1;
}
this.args = args;
try
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (string.IsNullOrEmpty(Extension))
Extension = "mkv";
@@ -54,7 +50,7 @@ namespace FileFlows.VideoNodes
string outputFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + "." + Extension);
var ffArgs = GetFFMPEGArgs(args, outputFile);
if (Encode(args, ffmpegExe, ffArgs, updateWorkingFile: false, dontAddInputFile: true, dontAddOutputFile: true) == false)
if (Encode(args, FFMPEG, ffArgs, updateWorkingFile: false, dontAddInputFile: true, dontAddOutputFile: true) == false)
return -1;
if (File.Exists(outputFile))

View File

@@ -42,14 +42,10 @@ namespace FileFlows.VideoNodes
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
try
{
var videoInfo = new VideoInfoHelper(ffmpegExe, args.Logger).Read(args.WorkingFile);
var videoInfo = new VideoInfoHelper(FFMPEG, args.Logger).Read(args.WorkingFile);
if (videoInfo.VideoStreams.Any() == false)
{
args.Logger.ILog("No video streams detected.");

View File

@@ -13,17 +13,12 @@
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (Force == false && args.WorkingFile?.ToLower()?.EndsWith(".mkv") == true)
return 2;
try
{
if (Encode(args, ffmpegExe, new List<string> { "-c", "copy", "-map", "0" }, "mkv") == false)
if (Encode(args, FFMPEG, new List<string> { "-c", "copy", "-map", "0" }, "mkv") == false)
return -1;
return 1;
@@ -45,16 +40,12 @@
public override int Execute(NodeParameters args)
{
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
if (Force == false && args.WorkingFile?.ToLower()?.EndsWith(".mp4") == true)
return 2;
try
{
if (Encode(args, ffmpegExe, new List<string> { "-c", "copy", "-map", "0" }, "mp4") == false)
if (Encode(args, FFMPEG, new List<string> { "-c", "copy", "-map", "0" }, "mp4") == false)
return -1;
return 1;

View File

@@ -38,11 +38,7 @@
VideoInfo videoInfo = GetVideoInfo(args);
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
// ffmpeg -i input.mkv -map "0:m:language:eng" -map "-0:v" -map "-0:a" output.srt
var subTrack = videoInfo.SubtitleStreams?.Where(x => string.IsNullOrEmpty(Language) || x.Language?.ToLower() == Language.ToLower()).FirstOrDefault();
if (subTrack == null)
@@ -74,7 +70,7 @@
//bool textSubtitles = Regex.IsMatch(OutputFile, @"\.(sup)$") == false;
var extracted = ExtractSubtitle(args, ffmpegExe, "0:s:" + subTrack.TypeIndex, OutputFile);
var extracted = ExtractSubtitle(args, FFMPEG, "0:s:" + subTrack.TypeIndex, OutputFile);
if(extracted)
{
args.UpdateVariables(new Dictionary<string, object>

View File

@@ -32,10 +32,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>()
{
"-map", "0:v",
@@ -74,7 +70,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -57,10 +57,6 @@
if (videoInfo == null)
return -1;
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>()
{
"-map", "0:v",
@@ -106,7 +102,7 @@
if(extension.StartsWith("."))
extension = extension.Substring(1);
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
if (Encode(args, FFMPEG, ffArgs, extension) == false)
return -1;
return 1;

View File

@@ -56,7 +56,6 @@ namespace FileFlows.VideoNodes
VideoCodec = VideoCodec.ToLower();
AudioCodec = AudioCodec.ToLower();
this.args = args;
try
{
VideoInfo videoInfo = GetVideoInfo(args);
@@ -65,10 +64,6 @@ namespace FileFlows.VideoNodes
Language = Language?.ToLower() ?? "";
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
// ffmpeg is one based for stream index, so video should be 1, audio should be 2
string encodeVideoParameters = string.Empty, encodeAudioParameters = string.Empty;
@@ -88,7 +83,7 @@ namespace FileFlows.VideoNodes
if (videoIsRightCodec == null || crop != string.Empty)
{
string codecParameters = CheckVideoCodec(ffmpegExe, VideoCodecParameters);
string codecParameters = CheckVideoCodec(FFMPEG, VideoCodecParameters);
encodeVideoParameters = $"-map 0:v:0 -c:v {codecParameters} {crop}";
}
Extension = args.ReplaceVariables(Extension)?.EmptyAsNull() ?? "mkv";
@@ -162,7 +157,7 @@ namespace FileFlows.VideoNodes
}
if (Encode(args, ffmpegExe, ffArgs, Extension) == false)
if (Encode(args, FFMPEG, ffArgs, Extension) == false)
return -1;
return 1;

View File

@@ -4,64 +4,74 @@ namespace FileFlows.VideoNodes
public abstract class VideoNode : Node
{
/// <summary>
/// Gets the Node Parameters
/// </summary>
protected NodeParameters Args { get; private set; }
#if (DEBUG)
/// <summary>
/// Used for unit tests
/// </summary>
/// <param name="args">the args</param>
public void SetArgs(NodeParameters args)
{
this.Args = args;
}
#endif
/// <summary>
/// Gets the FFMPEG executable location
/// </summary>
protected string FFMPEG { get; private set; }
public override string Icon => "fas fa-video";
protected string GetFFMpegExe(NodeParameters args)
/// <summary>
/// Executed before execute, sets ffmpegexe etc
/// </summary>
/// <param name="args">the node parametes</param>
/// <returns>true if successfully</returns>
public override bool PreExecute(NodeParameters args)
{
string ffmpeg = args.GetToolPath("FFMpeg");
this.Args = args;
this.FFMPEG = GetFFMpegExe();
return string.IsNullOrEmpty(this.FFMPEG) == false;
}
private string GetFFMpegExe()
{
string ffmpeg = Args.GetToolPath("FFMpeg");
if (string.IsNullOrEmpty(ffmpeg))
{
args.Logger.ELog("FFMpeg tool not found.");
Args.Logger.ELog("FFMpeg tool not found.");
return "";
}
var fileInfo = new FileInfo(ffmpeg);
if (fileInfo.Exists == false)
{
args.Logger.ELog("FFMpeg tool configured by ffmpeg.exe file does not exist.");
Args.Logger.ELog("FFMpeg tool configured by ffmpeg.exe file does not exist.");
return "";
}
return fileInfo.FullName;
}
protected string GetFFMpegPath(NodeParameters args)
{
string ffmpeg = args.GetToolPath("FFMpeg");
if (string.IsNullOrEmpty(ffmpeg))
{
args.Logger.ELog("FFMpeg tool not found.");
return "";
}
var fileInfo = new FileInfo(ffmpeg);
if (fileInfo.Exists == false)
{
args.Logger.ELog("FFMpeg tool configured by ffmpeg.exe file does not exist.");
return "";
}
return fileInfo.DirectoryName;
}
protected string GetFFPlayExe(NodeParameters args)
{
string ffmpeg = args.GetToolPath("FFMpeg");
if (string.IsNullOrEmpty(ffmpeg))
{
args.Logger.ELog("FFMpeg tool not found.");
return "";
}
var fileInfo = new FileInfo(ffmpeg);
if (fileInfo.Exists == false)
{
args.Logger.ELog("FFMpeg tool configured by ffmpeg file does not exist.");
return "";
}
var ffplay = Path.Combine(fileInfo.DirectoryName, "ffplay" + fileInfo.Extension);
if (File.Exists(ffplay) == false)
{
args.Logger.ELog("FFMpeg tool configured by ffplay file does not exist.");
return "";
}
return ffplay;
}
// protected string GetFFMpegPath(NodeParameters args)
// {
// string ffmpeg = args.GetToolPath("FFMpeg");
// if (string.IsNullOrEmpty(ffmpeg))
// {
// args.Logger.ELog("FFMpeg tool not found.");
// return "";
// }
// var fileInfo = new FileInfo(ffmpeg);
// if (fileInfo.Exists == false)
// {
// args.Logger.ELog("FFMpeg tool configured by ffmpeg.exe file does not exist.");
// return "";
// }
// return fileInfo.DirectoryName;
// }
private const string VIDEO_INFO = "VideoInfo";
protected void SetVideoInfo(NodeParameters args, VideoInfo videoInfo, Dictionary<string, object> variables)
@@ -119,5 +129,76 @@ namespace FileFlows.VideoNodes
}
return result;
}
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;
}
}
}

View File

@@ -89,7 +89,6 @@ namespace FileFlows.VideoNodes
public override int Execute(NodeParameters args)
{
this.args = args;
Extension = args.ReplaceVariables(Extension)?.EmptyAsNull() ?? "mkv";
try
@@ -111,12 +110,7 @@ namespace FileFlows.VideoNodes
else if (resolution == ResolutionHelper.Resolution.r480p && Resolution.StartsWith("640"))
return 2;
}
string ffmpegExe = GetFFMpegExe(args);
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
List<string> ffArgs = new List<string>()
{
"-vf", $"scale={Resolution}:flags=lanczos",
@@ -124,7 +118,7 @@ namespace FileFlows.VideoNodes
};
string codec = VideoCodec == "Custom" && string.IsNullOrWhiteSpace(VideoCodecParameters) == false ?
VideoCodecParameters : CheckVideoCodec(ffmpegExe, VideoCodec);
VideoCodecParameters : CheckVideoCodec(FFMPEG, VideoCodec);
foreach (string c in codec.Split(" "))
{
@@ -133,7 +127,7 @@ namespace FileFlows.VideoNodes
ffArgs.Add(c.Trim());
}
if (Encode(args, ffmpegExe, ffArgs, Extension) == false)
if (Encode(args, FFMPEG, ffArgs, Extension) == false)
return -1;
return 1;