namespace FileFlows.VideoNodes; /// /// Flow element to test if a video is interlaced /// public class VideoIsInterlaced : 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 icon /// public override string Icon => "fas fa-question"; /// /// Gets the help URL /// public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-is-interlaced"; /// /// Gets or sets the threshold /// [Range(1, 100)] [Slider(1)] [DefaultValue(10)] public int Threshold { get; set; } /// /// Executes the flow element /// /// the arguments /// the output to call next public override int Execute(NodeParameters args) { string ffmpeg = args.GetToolPath("FFmpeg"); if (string.IsNullOrEmpty(ffmpeg)) { args.Logger?.ELog("FFmpeg tool not found."); return 2; } var localFile = args.FileService.GetLocalPath(args.WorkingFile); if (localFile.Failed(out string error)) { args.FailureReason = "Failed to get local file: " + error; args.Logger?.ELog(args.FailureReason); return -1; } var ffOutput = args.Execute(new() { Command = ffmpeg, Silent = true, ArgumentList = new[] { "-hide_banner", "-i", localFile.Value, "-vf", "idet", "-f", "null", "-" } }); if (string.IsNullOrEmpty(ffOutput.StandardOutput)) { args.Logger?.ELog("Failed to get standard output from FFmpeg"); return 2; } bool isInterlaced = IsVideoInterlaced(args.Logger, ffOutput.StandardOutput, Math.Max(Threshold, 1)); args.Logger?.ILog("Is interlaced: " + isInterlaced); return isInterlaced ? 1 : 2; } /// /// Determines if a video is interlaced based on FFmpeg output. /// /// The FFmpeg output as a string. /// An optional threshold for the number of interlaced frames to consider. /// True if the video is interlaced, false if it's not. public static bool IsVideoInterlaced(ILogger logger, string ffmpegOutput, int interlaceThreshold = 10) { // Define regular expressions to match interlaced and progressive frame counts Regex tffRegex = new Regex(@"TFF:[\s]*(\d+)"); Regex bffRegex = new Regex(@"BFF:[\s]*(\d+)"); Regex progressiveRegex = new Regex(@"Progressive:[\s]*(\d+)"); int tffCount = 0; int bffCount = 0; int progressiveCount = 0; // Use regular expressions to extract counts of interlaced and progressive frames MatchCollection tffMatches = tffRegex.Matches(ffmpegOutput); MatchCollection bffMatches = bffRegex.Matches(ffmpegOutput); MatchCollection progressiveMatches = progressiveRegex.Matches(ffmpegOutput); foreach (Match match in tffMatches) { tffCount += int.Parse(match.Groups[1].Value); } foreach (Match match in bffMatches) { bffCount += int.Parse(match.Groups[1].Value); } foreach (Match match in progressiveMatches) { progressiveCount += int.Parse(match.Groups[1].Value); } // Calculate the total count of interlaced frames int totalInterlacedCount = tffCount + bffCount; int totalFrames = totalInterlacedCount + progressiveCount; float percent = ((float)totalInterlacedCount) / totalFrames * 100f; logger?.ILog("Interlaced threshold percentage: " + interlaceThreshold); logger?.ILog("Total Interlaced Frames: " + totalInterlacedCount); logger?.ILog("Total Progressive Frames: " + progressiveCount); logger?.ILog("Total Frames: " + totalFrames); logger?.ILog("Interlaced Percent: " + percent); // Check if the total count of interlaced frames exceeds the threshold return percent >= interlaceThreshold; } }