From d550e83ab4b5bfdf56929692e210366136698db2 Mon Sep 17 00:00:00 2001 From: n00mkrad <61149547+n00mkrad@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:01:03 +0100 Subject: [PATCH] VideoExtraData improvs, print warning for HDR content with suboptimal encsettings --- CodeLegacy/Data/ExportSettings.cs | 52 +++++++++++++++++++++-- CodeLegacy/Data/MediaFile.cs | 1 + CodeLegacy/Data/VidExtraData.cs | 2 + CodeLegacy/Extensions/ExtensionMethods.cs | 14 ++++++ CodeLegacy/Main/Export.cs | 7 --- CodeLegacy/Main/InterpolateUtils.cs | 10 +++++ CodeLegacy/Media/FfmpegCommands.cs | 15 +++++-- 7 files changed, 86 insertions(+), 15 deletions(-) diff --git a/CodeLegacy/Data/ExportSettings.cs b/CodeLegacy/Data/ExportSettings.cs index df39e16..bed48f7 100644 --- a/CodeLegacy/Data/ExportSettings.cs +++ b/CodeLegacy/Data/ExportSettings.cs @@ -1,11 +1,55 @@ -namespace Flowframes.Data +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using static Flowframes.Data.Enums.Encoding; +using static Flowframes.Data.Enums.Output; + +namespace Flowframes.Data { public class OutputSettings { - public Enums.Output.Format Format { get; set; } - public Enums.Encoding.Encoder Encoder { get; set; } - public Enums.Encoding.PixelFormat PixelFormat { get; set; } + public Format Format { get; set; } + public Encoder Encoder { get; set; } + public PixelFormat PixelFormat { get; set; } public string Quality { get; set; } = ""; public string CustomQuality { get; set; } = ""; + + public static List HdrRecommendedFormats { get; set; } = new List() { Format.Mkv, Format.Mp4, Format.Mov, Format.Images }; + public static List HdrRecommendedEncs { get; set; } = new List(); + + static OutputSettings () + { + HdrRecommendedEncs = GetHdrRecommEncs(); + } + + public static List GetHdrRecommEncs () + { + var l = new List(); + var encs = Enum.GetValues(typeof(Encoder)).Cast(); + l.Add(encs.FirstOrDefault(e => $"{e}".EndsWith("265"))); + l.Add(encs.FirstOrDefault(e => $"{e}".EndsWith("Av1"))); + l.Add(encs.FirstOrDefault(e => $"{e}".EndsWith("Vp9"))); + l.AddRange(new[] { Encoder.ProResKs, Encoder.Exr }); + return l; + } + + public string GetHdrNotSuitableReason () + { + if(!Format.IsOneOf(true, HdrRecommendedFormats)) + return $"Output format may not work for HDR. Recommended: {string.Join(" - ", HdrRecommendedFormats.Select(x => Strings.OutputFormat[x.ToString()]))}"; + + if(Encoder.IsOneOf(true, HdrRecommendedEncs)) + return $"Output encoder may not work for HDR. Recommended: {string.Join(" - ", HdrRecommendedEncs.Select(x => Strings.Encoder[x.ToString()]))}"; + + string pixFmt = Strings.PixelFormat[PixelFormat.ToString()]; + var m = Regex.Match(pixFmt, @"(\d+)(?=-bit\b)"); + int bitDepth = m.Success ? m.Groups[1].Value.GetInt() : 0; + + if(bitDepth < 10) + return "Output Pixel Format may not work for HDR. Recommended: 10-bit or higher."; + + return ""; + } } } diff --git a/CodeLegacy/Data/MediaFile.cs b/CodeLegacy/Data/MediaFile.cs index 6ebcd07..b9bb64a 100644 --- a/CodeLegacy/Data/MediaFile.cs +++ b/CodeLegacy/Data/MediaFile.cs @@ -122,6 +122,7 @@ namespace Flowframes.Data Logger.Log($"Failed to initialize MediaFile: {e.Message}", true); } + VideoExtraData = await FfmpegCommands.GetVidExtraInfo(ImportPath); Initialized = true; } diff --git a/CodeLegacy/Data/VidExtraData.cs b/CodeLegacy/Data/VidExtraData.cs index 19398a9..6299d30 100644 --- a/CodeLegacy/Data/VidExtraData.cs +++ b/CodeLegacy/Data/VidExtraData.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Windows.Media; namespace Flowframes.Data { @@ -12,6 +13,7 @@ namespace Flowframes.Data public string ColRange = ""; public string ColTransfer = ""; public string ColPrimaries = ""; + public bool IsHdr => ColPrimaries == "bt2020" && (ColTransfer == "smpte2084" || ColTransfer == "arib-std-b67"); // Sample/Display Aspect Ratios public string Sar = ""; diff --git a/CodeLegacy/Extensions/ExtensionMethods.cs b/CodeLegacy/Extensions/ExtensionMethods.cs index 116cc19..c39046c 100644 --- a/CodeLegacy/Extensions/ExtensionMethods.cs +++ b/CodeLegacy/Extensions/ExtensionMethods.cs @@ -480,5 +480,19 @@ namespace Flowframes list.Add(item); } + + /// Checks if a string is equal to any of the provided , optionally case-insensitive. + public static bool IsOneOf(this object s, bool caseSensitive, params object[] strings) + { + StringComparison strComp = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + + if (strings.Length == 0) + return false; + + if (strings.Length == 1 && strings[0] is IEnumerable col) + return col.Any(v => s.ToString().Equals(v, strComp)); + + return strings.Any(v => s.ToString().Equals(v.ToString(), strComp)); + } } } diff --git a/CodeLegacy/Main/Export.cs b/CodeLegacy/Main/Export.cs index 1dc3c41..1e9aa6e 100644 --- a/CodeLegacy/Main/Export.cs +++ b/CodeLegacy/Main/Export.cs @@ -83,7 +83,6 @@ namespace Flowframes.Main string encArgs = FfmpegUtils.GetEncArgs(outSettings, outRes, s.outFps.Float, true).First(); bool fpsLimit = MaxFpsFrac.Float > 0f && s.outFps.Float > MaxFpsFrac.Float; bool gifInput = I.currentMediaFile.Format.Upper() == "GIF"; // If input is GIF, we don't need to check the color space etc - I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(s.inPath, allowColorData: !gifInput); string extraArgsIn = FfmpegEncode.GetFfmpegExportArgsIn(I.currentMediaFile.IsVfr ? s.outFpsResampled : s.outFps, s.outItsScale, I.currentMediaFile.VideoExtraData.Rotation); string extraArgsOut; string alphaPassFile = Path.Combine(s.tempFolder, "alpha.mkv"); @@ -211,9 +210,6 @@ namespace Flowframes.Main return; } - if (I.currentMediaFile.VideoExtraData == null) - I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath); - if (settings.Format == Enums.Output.Format.Gif) { int paletteColors = OutputUtils.GetGifColors(ParseUtils.GetEnum(settings.Quality, true, Strings.VideoQuality)); @@ -308,9 +304,6 @@ namespace Flowframes.Main await Blend.BlendSceneChanges(concatFile, false); bool fpsLimit = MaxFpsFrac.Float != 0 && I.currentSettings.outFps.Float > MaxFpsFrac.Float; - if (I.currentMediaFile.VideoExtraData == null) - I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath); - bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0; if (settings.Encoder.GetInfo().IsImageSequence) // Image Sequence output mode, not video diff --git a/CodeLegacy/Main/InterpolateUtils.cs b/CodeLegacy/Main/InterpolateUtils.cs index 13b34b6..3cbead2 100644 --- a/CodeLegacy/Main/InterpolateUtils.cs +++ b/CodeLegacy/Main/InterpolateUtils.cs @@ -147,6 +147,16 @@ namespace Flowframes.Main passes = false; } + if(passes && I.currentMediaFile.VideoExtraData.IsHdr) + { + string badHdrSettingsStr = I.currentSettings.outSettings.GetHdrNotSuitableReason(); + + if(badHdrSettingsStr.IsNotEmpty()) + { + Logger.Log(badHdrSettingsStr); + } + } + I.InterpProgressMultiplier = s.FpsResampling ? s.outFps.Float / fpsLimit : 1f; if (!passes) diff --git a/CodeLegacy/Media/FfmpegCommands.cs b/CodeLegacy/Media/FfmpegCommands.cs index 30a3168..c733fb6 100644 --- a/CodeLegacy/Media/FfmpegCommands.cs +++ b/CodeLegacy/Media/FfmpegCommands.cs @@ -395,11 +395,18 @@ namespace Flowframes } } - public static async Task GetVidExtraInfo(string inputFile, bool allowColorData = true) + public static async Task GetVidExtraInfo(string inputFile) { - string ffprobeOutput = await GetVideoInfo.GetFfprobeInfoAsync(inputFile, GetVideoInfo.FfprobeMode.ShowBoth); - VidExtraData data = new VidExtraData(ffprobeOutput); - return data; + try + { + string ffprobeOutput = await GetVideoInfo.GetFfprobeInfoAsync(inputFile, GetVideoInfo.FfprobeMode.ShowBoth); + VidExtraData data = new VidExtraData(ffprobeOutput); + return data; + } + catch + { + return new VidExtraData(); + } } public static async Task IsEncoderCompatible(string enc)