diff --git a/Code/Data/FpsInfo.cs b/Code/Data/FpsInfo.cs index 78e295c..e49ff09 100644 --- a/Code/Data/FpsInfo.cs +++ b/Code/Data/FpsInfo.cs @@ -3,22 +3,22 @@ public class FpsInfo { public Fraction Fps { get; set; } - public Fraction AverageFps { get; set; } - public float VfrRatio { get => AverageFps.GetFloat() / Fps.GetFloat(); } - public float VfrRatioInverse { get => Fps.GetFloat() / AverageFps.GetFloat(); } + public Fraction SpecifiedFps { get; set; } + public float VfrRatio { get => Fps.GetFloat() / SpecifiedFps.GetFloat(); } + public float VfrRatioInverse { get => SpecifiedFps.GetFloat() / Fps.GetFloat(); } public FpsInfo() { } public FpsInfo(Fraction fps) { Fps = fps; - AverageFps = fps; + SpecifiedFps = fps; } - public FpsInfo(Fraction fps, Fraction avgFps) + public FpsInfo(Fraction fps, Fraction specifiedFps) { Fps = fps; - AverageFps = avgFps; + SpecifiedFps = specifiedFps; } } } diff --git a/Code/Main/Export.cs b/Code/Main/Export.cs index a1bf235..23ea144 100644 --- a/Code/Main/Export.cs +++ b/Code/Main/Export.cs @@ -82,11 +82,11 @@ namespace Flowframes.Main Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat()); bool fpsLimit = maxFps.GetFloat() > 0f && s.outFps.GetFloat() > maxFps.GetFloat(); - Logger.Log($"VFR Ratio: {I.currentMediaFile.VideoStreams.First().FpsInfo.VfrRatio}"); + // Logger.Log($"VFR Ratio: {I.currentMediaFile.VideoStreams.First().FpsInfo.VfrRatio} ({I.currentMediaFile.VideoStreams.First().FpsInfo.Fps} FPS Specified, {I.currentMediaFile.VideoStreams.First().FpsInfo.SpecifiedFps} FPS Avg)"); bool gifInput = I.currentMediaFile.Format.Upper() == "GIF"; // If input is GIF, we don't need to check the color space etc VidExtraData extraData = gifInput ? new VidExtraData() : await FfmpegCommands.GetVidExtraInfo(s.inPath); - string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale * I.currentMediaFile.VideoStreams.First().FpsInfo.VfrRatioInverse); + string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale); string extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings); if (ffplay) diff --git a/Code/Media/FfmpegExtract.cs b/Code/Media/FfmpegExtract.cs index 351fd3c..848103b 100644 --- a/Code/Media/FfmpegExtract.cs +++ b/Code/Media/FfmpegExtract.cs @@ -115,9 +115,8 @@ namespace Flowframes.Media string mpStr = deDupe ? ((Config.GetInt(Config.Key.mpdecimateMode) == 0) ? mpDecDef : mpDecAggr) : ""; string filters = FormatUtils.ConcatStrings(new[] { GetPadFilter(), mpStr }); string vf = filters.Length > 2 ? $"-vf {filters}" : ""; - string rateArg = (rate.GetFloat() > 0) ? $"-r {rate}" : ""; - string args = $"{GetTrimArg(true)} -i {inputFile.Wrap()} {GetImgArgs(format, true, alpha)} -fps_mode cfr {rateArg} -frame_pts 1 {vf} {sizeStr} {GetTrimArg(false)} \"{framesDir}/%{Padding.inputFrames}d{format}\""; - LogMode logMode = Interpolate.currentMediaFile.FrameCount > 50 ? LogMode.OnlyLastLine : LogMode.Hidden; + string rateArg = (rate.GetFloat() > 0 && !deDupe) ? $" -fps_mode cfr -r {rate}" : "-fps_mode passthrough"; + string args = $"{GetTrimArg(true)} -itsscale {Interpolate.currentMediaFile.VideoStreams.First().FpsInfo.VfrRatio} -i {inputFile.Wrap()} {GetImgArgs(format, true, alpha)} {rateArg} -frame_pts 1 {vf} {sizeStr} {GetTrimArg(false)} \"{framesDir}/%{Padding.inputFrames}d{format}\""; LogMode logMode = Interpolate.currentMediaFile.FrameCount > 50 ? LogMode.OnlyLastLine : LogMode.Hidden; await RunFfmpeg(args, logMode, true); int amount = IoUtils.GetAmountOfFiles(framesDir, false, "*" + format); Logger.Log($"Extracted {amount} {(amount == 1 ? "frame" : "frames")} from input.", false, true); diff --git a/Code/Media/FfmpegUtils.cs b/Code/Media/FfmpegUtils.cs index 1d9db68..f3e2955 100644 --- a/Code/Media/FfmpegUtils.cs +++ b/Code/Media/FfmpegUtils.cs @@ -4,6 +4,7 @@ using Flowframes.IO; using Flowframes.MiscUtils; using Flowframes.Os; using Flowframes.Properties; +using ImageMagick; using System; using System.Collections.Generic; using System.Diagnostics; @@ -69,8 +70,8 @@ namespace Flowframes.Media Size res = await GetMediaResolutionCached.GetSizeAsync(path); Size sar = SizeFromString(await GetFfprobeInfoAsync(path, showStreams, "sample_aspect_ratio", idx)); Size dar = SizeFromString(await GetFfprobeInfoAsync(path, showStreams, "display_aspect_ratio", idx)); - FpsInfo fps = await GetFps(path, streamStr, idx, (Fraction)defaultFps); int frameCount = countFrames ? await GetFrameCountCached.GetFrameCountAsync(path) : 0; + FpsInfo fps = await GetFps(path, streamStr, idx, (Fraction)defaultFps, frameCount); VideoStream vStream = new VideoStream(lang, title, codec, codecLong, pixFmt, kbits, res, sar, dar, fps, frameCount); vStream.Index = idx; vStream.IsDefault = def; @@ -165,7 +166,7 @@ namespace Flowframes.Media return streamList; } - private static async Task GetFps (string path, string streamStr, int streamIdx, Fraction defaultFps) + private static async Task GetFps(string path, string streamStr, int streamIdx, Fraction defaultFps, int frameCount) { if (path.IsConcatFile()) return new FpsInfo(defaultFps); @@ -174,13 +175,25 @@ namespace Flowframes.Media { string fps = streamStr.Split(", ").Where(s => s.Contains(" fps")).First().Trim().Split(' ')[0]; string tbr = streamStr.Split("fps, ")[1].Split(" tbr")[0].Trim(); + long durationMs = Interpolate.currentMediaFile.DurationMs; + float fpsCalc = (float)frameCount / (durationMs / 1000f); + fpsCalc = (float)Math.Round(fpsCalc, 5); - var info = new FpsInfo(new Fraction(tbr)); + var info = new FpsInfo(new Fraction(fps.GetFloat())); // Set both true FPS and average FPS to this number for now - if(tbr != fps) + Logger.Log($"FPS: {fps} - TBR: {tbr} - Est. FPS: {fpsCalc.ToString("0.#####")}", true); + + if (tbr != fps) { - string avgFpsStr = await GetFfprobeInfoAsync(path, showStreams, "avg_frame_rate", streamIdx); - info.AverageFps = new Fraction(avgFpsStr); + info.SpecifiedFps = new Fraction(tbr); // Change FPS to TBR if they mismatch + } + + float fpsEstTolerance = GetFpsEstimationTolerance(durationMs); + + if (Math.Abs(fps.GetFloat() - fpsCalc) > fpsEstTolerance) + { + Logger.Log($"Detected FPS {fps} is not within tolerance (+-{fpsEstTolerance}) of calculated FPS ({fpsCalc}), using estimated FPS.", true); + info.Fps = new Fraction(fpsCalc); // Change true FPS to the estimated FPS if the estimate does not match the specified FPS } return info; @@ -189,6 +202,18 @@ namespace Flowframes.Media return new FpsInfo(await IoUtils.GetVideoFramerate(path)); } + private static float GetFpsEstimationTolerance (long videoDurationMs) + { + if (videoDurationMs < 300) return 5.0f; + if (videoDurationMs < 1000) return 2.5f; + if (videoDurationMs < 2500) return 1.0f; + if (videoDurationMs < 5000) return 0.75f; + if (videoDurationMs < 10000) return 0.5f; + if (videoDurationMs < 20000) return 0.25f; + + return 0.1f; + } + public static async Task IsSubtitleBitmapBased(string path, int streamIndex, string codec = "") { if (codec == "ssa" || codec == "ass" || codec == "mov_text" || codec == "srt" || codec == "subrip" || codec == "text" || codec == "webvtt") @@ -540,6 +565,9 @@ namespace Flowframes.Media { try { + if (str.IsEmpty() || str.Length < 3 || !str.Contains(delimiter)) + return new Size(); + string[] nums = str.Remove(" ").Trim().Split(delimiter); return new Size(nums[0].GetInt(), nums[1].GetInt()); } diff --git a/Code/Os/VapourSynthUtils.cs b/Code/Os/VapourSynthUtils.cs index e7a6258..929d85f 100644 --- a/Code/Os/VapourSynthUtils.cs +++ b/Code/Os/VapourSynthUtils.cs @@ -93,6 +93,12 @@ namespace Flowframes.Os l.Add($"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToStringDot()})"); // Scene detection Fraction outFps = s.InterpSettings.inFps * s.Factor; + + if (!loadFrames) + { + outFps = Interpolate.currentMediaFile.VideoStreams.First().FpsInfo.SpecifiedFps * s.Factor; + } + l.Add($"clip = core.rife.RIFE(clip, fps_num={outFps.Numerator}, fps_den={outFps.Denominator}, model_path={mdlPath}, gpu_id={s.GpuId}, gpu_thread={s.GpuThreads}, tta={s.Tta}, uhd={s.Uhd}, sc={sc})"); // Interpolate if (s.Dedupe && !s.Realtime) diff --git a/Code/Ui/MainUiFunctions.cs b/Code/Ui/MainUiFunctions.cs index e5a808e..88c480c 100644 --- a/Code/Ui/MainUiFunctions.cs +++ b/Code/Ui/MainUiFunctions.cs @@ -58,7 +58,6 @@ namespace Flowframes.Ui await PrintResolution(path); await Task.Delay(10); InterpolationProgress.SetPreviewImg(await GetThumbnail(path)); - // File.WriteAllText("media.json", Interpolate.currentMediaFile.ToJson(true)); if(AutoEncodeResume.resumeNextRun) Logger.Log($"Incomplete interpolation detected. Flowframes will resume the interpolation.");