using System.Diagnostics; namespace FileFlows.VideoNodes; /// /// Tests a video file for errors /// public class VideoHasErrors: VideoNode { /// /// Gets the number of inputs /// public override int Inputs => 1; /// /// Gets the number of outputs /// public override int Outputs => 2; /// /// Gets the type of flow element /// public override FlowElementType Type => FlowElementType.Logic; /// /// Gets the help URL /// public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-has-errors"; /// /// Gets the icon for this flow element /// public override string Icon => "fas fa-exclamation-circle"; /// /// Gets or sets if hardware decoding should be used /// [DefaultValue(true)] [Boolean(1)] public bool HardwareDecoding { get; set; } /// /// Executes the flow element /// /// the arguments /// the output to call next public override int Execute(NodeParameters args) { var file = args.FileService.GetLocalPath(args.WorkingFile); if (file.IsFailed) { args.Logger?.ILog("Failed to get file: " + file.Error); return -1; } var videoInfo = GetVideoInfo(args); var result = ValidateFile(args, FFMPEG, file, videoInfo, useHardwareDecoding: HardwareDecoding); if (result.NoErrors) return 2; return 1; } /// /// Validates a video file for errors using FFmpeg. /// /// the arguments /// The path to the FFmpeg executable. /// The path to the video file to be validated. /// The video information for the current file /// If hardware decoding should be attempted /// /// A tuple containing a boolean indicating whether there are no errors (NoErrors) /// and a log message (Log) from FFmpeg. /// public static (bool NoErrors, string Log) ValidateFile(NodeParameters args, string ffmpegPath, string filename, VideoInfo info, bool useHardwareDecoding) { if (System.IO.File.Exists(ffmpegPath) == false) return (false, "FFmpeg does not exist at the specified path."); if (System.IO.File.Exists(filename) == false) return (false, "The input file does not exist."); var video = info.VideoStreams.FirstOrDefault(x => x.IsImage == false); var processStartInfo = new ProcessStartInfo { FileName = ffmpegPath, //ArgumentList = { "-v", "error", "-i", filename, "-f", "null", "-" }, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; processStartInfo.ArgumentList.Add("-v"); processStartInfo.ArgumentList.Add("error"); if (useHardwareDecoding) { var hardwareDecodingArgs = FfmpegBuilderNodes.FfmpegBuilderExecutor.GetHardwareDecodingArgs(args, null, filename, ffmpegPath, video?.Codec, video?.PixelFormat); if (hardwareDecodingArgs?.Any() == true) { foreach (var hwArg in hardwareDecodingArgs) processStartInfo.ArgumentList.Add(hwArg); } } processStartInfo.ArgumentList.Add("-i"); processStartInfo.ArgumentList.Add(filename); processStartInfo.ArgumentList.Add("-f"); processStartInfo.ArgumentList.Add("null"); processStartInfo.ArgumentList.Add("-"); Process process = new Process { StartInfo = processStartInfo }; var line = "Executing: " + (ffmpegPath.IndexOf(" ") > 0 ? "\"" + ffmpegPath + "\"" : ffmpegPath) + " " + string.Join(" ", processStartInfo.ArgumentList.Select(x => x.IndexOf(" ") > 0 ? "\"" + x + "\"" : x) .ToArray()); args.Logger?.ILog(new string('-', line.Length)); args.Logger?.ILog(line); args.Logger?.ILog(new string('-', line.Length)); process.Start(); string output = process.StandardError.ReadToEnd(); process.WaitForExit(); if (output.ToLowerInvariant().Contains("error")) { args.Logger?.ILog("Errors detected in file"); args.Logger?.WLog(output); return (false, output); } args.Logger?.ILog("No errors detected"); if(string.IsNullOrWhiteSpace(output) == false) args.Logger?.ILog(output); return (true, string.Empty); } }