Files
FileFlowsPlugins/VideoNodes/LogicalNodes/VideoIsInterlaced.cs
2024-02-22 15:45:26 +13:00

136 lines
4.6 KiB
C#

namespace FileFlows.VideoNodes;
/// <summary>
/// Flow element to test if a video is interlaced
/// </summary>
public class VideoIsInterlaced : VideoNode
{
/// <summary>
/// Gets the number of inputs
/// </summary>
public override int Inputs => 1;
/// <summary>
/// Gets the number of outputs
/// </summary>
public override int Outputs => 2;
/// <summary>
/// Gets the type of flow element
/// </summary>
public override FlowElementType Type => FlowElementType.Logic;
/// <summary>
/// Gets the icon
/// </summary>
public override string Icon => "fas fa-question";
/// <summary>
/// Gets the help URL
/// </summary>
public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-is-interlaced";
/// <summary>
/// Gets or sets the threshold
/// </summary>
[Range(1, 100)]
[Slider(1)]
[DefaultValue(10)]
public int Threshold { get; set; }
/// <summary>
/// Executes the flow element
/// </summary>
/// <param name="args">the arguments</param>
/// <returns>the output to call next</returns>
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;
}
/// <summary>
/// Determines if a video is interlaced based on FFmpeg output.
/// </summary>
/// <param name="ffmpegOutput">The FFmpeg output as a string.</param>
/// <param name="interlaceThreshold">An optional threshold for the number of interlaced frames to consider.</param>
/// <returns>True if the video is interlaced, false if it's not.</returns>
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;
}
}