From 3ad94bda6c088fe67add25425bab66645674509e Mon Sep 17 00:00:00 2001 From: N00MKRAD Date: Thu, 26 Nov 2020 20:17:18 +0100 Subject: [PATCH] more error handling, scene change support, opus support, cached magick dedupe --- Code/AudioVideo/AvProcess.cs | 15 ++- Code/AudioVideo/FFmpegCommands.cs | 13 +- Code/Flowframes.csproj | 12 +- Code/Form1.Designer.cs | 4 +- Code/Form1.cs | 6 + Code/Forms/InstallerForm.cs | 4 +- Code/Forms/SettingsForm.Designer.cs | 187 ++++++++++++++++++++-------- Code/Forms/SettingsForm.cs | 4 + Code/IO/Config.cs | 5 +- Code/IO/IOUtils.cs | 20 +++ Code/IO/Setup.cs | 2 +- Code/Magick/MagickDedupe.cs | 49 ++++++-- Code/Main/CreateVideo.cs | 2 +- Code/Main/Interpolate.cs | 13 ++ Code/Main/VfrDedupe.cs | 32 ++++- Code/OS/AiProcess.cs | 11 +- Code/OS/OSUtils.cs | 6 + Code/UI/FormatUtils.cs | 2 +- Code/UI/GetWebInfo.cs | 4 +- Code/packages.config | 3 - 20 files changed, 302 insertions(+), 92 deletions(-) diff --git a/Code/AudioVideo/AvProcess.cs b/Code/AudioVideo/AvProcess.cs index 3b46e81..67e7397 100644 --- a/Code/AudioVideo/AvProcess.cs +++ b/Code/AudioVideo/AvProcess.cs @@ -16,7 +16,6 @@ namespace Flowframes public static string lastOutputFfmpeg; public static string lastOutputGifski; - static bool replaceLastLine = false; public enum LogMode { Visible, OnlyLastLine, Hidden } static LogMode currentLogMode; @@ -47,11 +46,19 @@ namespace Flowframes static void FfmpegOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { - lastOutputFfmpeg = lastOutputFfmpeg + outLine.Data + "\n"; + if (outLine == null || outLine.Data == null) + return; + string line = outLine.Data; + lastOutputFfmpeg = lastOutputFfmpeg + line + "\n"; bool hidden = currentLogMode == LogMode.Hidden; bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine; - string trimmedLine = outLine.Data.Remove("q=-0.0").Remove("size=N/A").Remove("bitrate=N/A").TrimWhitespaces(); + string trimmedLine = line.Remove("q=-0.0").Remove("size=N/A").Remove("bitrate=N/A").TrimWhitespaces(); Logger.Log(trimmedLine, hidden, replaceLastLine, "ffmpeg.txt"); + + if(line.Contains("Could not open file")) + { + Interpolate.Cancel("Failed to write frames - Make sure the input folder is not restricted!"); + } } public static string GetFfmpegOutput (string args) @@ -101,6 +108,8 @@ namespace Flowframes static void OutputHandlerGifski(object sendingProcess, DataReceivedEventArgs outLine) { + if (outLine == null || outLine.Data == null) + return; lastOutputGifski = lastOutputGifski + outLine.Data + "\n"; bool hidden = currentLogMode == LogMode.Hidden; bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine; diff --git a/Code/AudioVideo/FFmpegCommands.cs b/Code/AudioVideo/FFmpegCommands.cs index a81f66b..575f766 100644 --- a/Code/AudioVideo/FFmpegCommands.cs +++ b/Code/AudioVideo/FFmpegCommands.cs @@ -18,22 +18,28 @@ namespace Flowframes static string mpDecDef = "\"mpdecimate\""; static string mpDecAggr = "\"mpdecimate=hi=64*32:lo=64*32:frac=0.1\""; + public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath) + { + await VideoToFrames(inputFile, frameFolderPath, (Config.GetInt("dedupMode") == 2), false, new Size(320, 180), true, true); + } + public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, bool timecodes = true) { await VideoToFrames(inputFile, frameFolderPath, deDupe, delSrc, new Size(), timecodes); } - public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size, bool timecodes = true) + public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size, bool timecodes = true, bool sceneDetect = false) { string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : ""; if (!Directory.Exists(frameFolderPath)) Directory.CreateDirectory(frameFolderPath); string timecodeStr = timecodes ? "-copyts -r 1000 -frame_pts true" : ""; - string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\""; + string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.Get("scnDetectValue")})'\"," : ""; + string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\""; if (deDupe) { string mpStr = (Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr; - args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {mpStr},{divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\""; + args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{mpStr},{divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\""; } await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine); await Task.Delay(1); @@ -174,6 +180,7 @@ namespace Flowframes switch (GetAudioCodec(videoFile)) { case "vorbis": return "ogg"; + case "opus": return "ogg"; case "mp2": return "mp2"; case "aac": return "m4a"; default: return "wav"; diff --git a/Code/Flowframes.csproj b/Code/Flowframes.csproj index 890b58a..24024da 100644 --- a/Code/Flowframes.csproj +++ b/Code/Flowframes.csproj @@ -1,6 +1,5 @@  - @@ -86,6 +85,7 @@ packages\Magick.NET.Core.4.1.0\lib\net40\Magick.NET.Core.dll + packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll @@ -95,15 +95,6 @@ packages\NvAPIWrapper.Net.0.8.0.98\lib\net45\NvAPIWrapper.dll - - packages\OpenCvSharp4.4.5.0.20201013\lib\net461\OpenCvSharp.dll - - - packages\OpenCvSharp4.4.5.0.20201013\lib\net461\OpenCvSharp.Blob.dll - - - packages\OpenCvSharp4.4.5.0.20201013\lib\net461\OpenCvSharp.Extensions.dll - packages\PagedControl.2.2.0\lib\net35\PagedControl.dll @@ -333,7 +324,6 @@ - diff --git a/Code/Form1.Designer.cs b/Code/Form1.Designer.cs index 7f60d1e..ec6e83e 100644 --- a/Code/Form1.Designer.cs +++ b/Code/Form1.Designer.cs @@ -1107,10 +1107,10 @@ // tilesizeNotAvailLabel // this.tilesizeNotAvailLabel.ForeColor = System.Drawing.Color.Silver; - this.tilesizeNotAvailLabel.Location = new System.Drawing.Point(278, 127); + this.tilesizeNotAvailLabel.Location = new System.Drawing.Point(278, 124); this.tilesizeNotAvailLabel.Margin = new System.Windows.Forms.Padding(8, 0, 3, 0); this.tilesizeNotAvailLabel.Name = "tilesizeNotAvailLabel"; - this.tilesizeNotAvailLabel.Size = new System.Drawing.Size(203, 25); + this.tilesizeNotAvailLabel.Size = new System.Drawing.Size(203, 31); this.tilesizeNotAvailLabel.TabIndex = 37; this.tilesizeNotAvailLabel.Text = "Not available for the selected AI."; this.tilesizeNotAvailLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; diff --git a/Code/Form1.cs b/Code/Form1.cs index 737f0e8..6096973 100644 --- a/Code/Form1.cs +++ b/Code/Form1.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Windows.Forms; using HTAlt.WinForms; using Flowframes.Data; +using Microsoft.WindowsAPICodePack.Taskbar; namespace Flowframes { @@ -52,7 +53,11 @@ namespace Flowframes Setup.Init(); Initialized(); + Checks(); + } + void Checks () + { GetWebInfo.LoadNews(newsLabel); GetWebInfo.LoadPatronList(patronsLabel); Updater.AsyncUpdateCheck(); @@ -156,6 +161,7 @@ namespace Flowframes Logger.Log($"Video FPS (Loaded from fps.ini): {fpsStr} - Total Number Of Frames: {IOUtils.GetAmountOfFiles(path, false)}"); else Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {FFmpegCommands.GetFrameCount(path)}"); + MagickDedupe.ClearCache(); } diff --git a/Code/Forms/InstallerForm.cs b/Code/Forms/InstallerForm.cs index 3072267..320fa0b 100644 --- a/Code/Forms/InstallerForm.cs +++ b/Code/Forms/InstallerForm.cs @@ -26,7 +26,7 @@ namespace Flowframes.Forms { PkgInstaller.installerForm = this; Print($"Welcome to the package manager.{Environment.NewLine}Here you can install packages by ticking or uninstall by unticking them."); - Refresh(); + RefreshGui(); EnableRequired(); if (pkgList.SelectedIndex < 0) pkgList.SelectedIndex = 0; @@ -78,7 +78,7 @@ namespace Flowframes.Forms SetBusy(false); } - void Refresh () + void RefreshGui () { pkgList.Items.Clear(); foreach (FlowPackage pkg in PkgInstaller.packages) diff --git a/Code/Forms/SettingsForm.Designer.cs b/Code/Forms/SettingsForm.Designer.cs index eb09fd6..dd17772 100644 --- a/Code/Forms/SettingsForm.Designer.cs +++ b/Code/Forms/SettingsForm.Designer.cs @@ -112,6 +112,8 @@ this.label8 = new System.Windows.Forms.Label(); this.minOutVidLength = new System.Windows.Forms.ComboBox(); this.debugTab = new Cyotek.Windows.Forms.TabListPage(); + this.vfrMode = new System.Windows.Forms.ComboBox(); + this.label48 = new System.Windows.Forms.Label(); this.ffEncPreset = new System.Windows.Forms.ComboBox(); this.label47 = new System.Windows.Forms.Label(); this.label46 = new System.Windows.Forms.Label(); @@ -130,8 +132,12 @@ this.cmdDebugMode = new System.Windows.Forms.ComboBox(); this.titleLabel = new System.Windows.Forms.Label(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.label48 = new System.Windows.Forms.Label(); - this.vfrMode = new System.Windows.Forms.ComboBox(); + this.label49 = new System.Windows.Forms.Label(); + this.label50 = new System.Windows.Forms.Label(); + this.scnDetect = new System.Windows.Forms.CheckBox(); + this.scnDetectValue = new System.Windows.Forms.ComboBox(); + this.label51 = new System.Windows.Forms.Label(); + this.label52 = new System.Windows.Forms.Label(); this.settingsTabList.SuspendLayout(); this.generalTab.SuspendLayout(); this.tabListPage2.SuspendLayout(); @@ -346,6 +352,12 @@ // tabListPage2 // this.tabListPage2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48))))); + this.tabListPage2.Controls.Add(this.label52); + this.tabListPage2.Controls.Add(this.scnDetectValue); + this.tabListPage2.Controls.Add(this.label51); + this.tabListPage2.Controls.Add(this.scnDetect); + this.tabListPage2.Controls.Add(this.label50); + this.tabListPage2.Controls.Add(this.label49); this.tabListPage2.Controls.Add(this.mpDedupePanel); this.tabListPage2.Controls.Add(this.magickDedupePanel); this.tabListPage2.Controls.Add(this.timingMode); @@ -492,7 +504,7 @@ // this.label27.AutoSize = true; this.label27.ForeColor = System.Drawing.Color.Silver; - this.label27.Location = new System.Drawing.Point(308, 221); + this.label27.Location = new System.Drawing.Point(308, 314); this.label27.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label27.Name = "label27"; this.label27.Size = new System.Drawing.Size(449, 13); @@ -503,7 +515,7 @@ // jpegInterps // this.jpegInterps.AutoSize = true; - this.jpegInterps.Location = new System.Drawing.Point(280, 220); + this.jpegInterps.Location = new System.Drawing.Point(280, 313); this.jpegInterps.Name = "jpegInterps"; this.jpegInterps.Size = new System.Drawing.Size(15, 14); this.jpegInterps.TabIndex = 48; @@ -512,7 +524,7 @@ // label25 // this.label25.AutoSize = true; - this.label25.Location = new System.Drawing.Point(10, 220); + this.label25.Location = new System.Drawing.Point(10, 313); this.label25.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label25.Name = "label25"; this.label25.Size = new System.Drawing.Size(149, 13); @@ -523,7 +535,7 @@ // this.label26.AutoSize = true; this.label26.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold); - this.label26.Location = new System.Drawing.Point(10, 190); + this.label26.Location = new System.Drawing.Point(10, 283); this.label26.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label26.Name = "label26"; this.label26.Size = new System.Drawing.Size(76, 16); @@ -632,7 +644,7 @@ // this.panel12.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent; this.panel12.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.panel12.Location = new System.Drawing.Point(336, 187); + this.panel12.Location = new System.Drawing.Point(336, 177); this.panel12.Name = "panel12"; this.panel12.Size = new System.Drawing.Size(21, 21); this.panel12.TabIndex = 58; @@ -642,7 +654,7 @@ // this.label44.AutoSize = true; this.label44.ForeColor = System.Drawing.Color.Silver; - this.label44.Location = new System.Drawing.Point(370, 191); + this.label44.Location = new System.Drawing.Point(370, 181); this.label44.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label44.Name = "label44"; this.label44.Size = new System.Drawing.Size(358, 13); @@ -660,7 +672,7 @@ "2", "3", "4"}); - this.ncnnThreads.Location = new System.Drawing.Point(280, 187); + this.ncnnThreads.Location = new System.Drawing.Point(280, 177); this.ncnnThreads.Name = "ncnnThreads"; this.ncnnThreads.Size = new System.Drawing.Size(50, 21); this.ncnnThreads.TabIndex = 59; @@ -668,7 +680,7 @@ // label43 // this.label43.AutoSize = true; - this.label43.Location = new System.Drawing.Point(10, 190); + this.label43.Location = new System.Drawing.Point(10, 180); this.label43.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label43.Name = "label43"; this.label43.Size = new System.Drawing.Size(135, 13); @@ -679,7 +691,7 @@ // this.panel2.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent; this.panel2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.panel2.Location = new System.Drawing.Point(536, 157); + this.panel2.Location = new System.Drawing.Point(536, 147); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(21, 21); this.panel2.TabIndex = 57; @@ -689,7 +701,7 @@ // this.panel1.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent; this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.panel1.Location = new System.Drawing.Point(536, 127); + this.panel1.Location = new System.Drawing.Point(536, 117); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(21, 21); this.panel1.TabIndex = 56; @@ -705,7 +717,7 @@ "0", "1", "0,1"}); - this.torchGpus.Location = new System.Drawing.Point(280, 127); + this.torchGpus.Location = new System.Drawing.Point(280, 117); this.torchGpus.Name = "torchGpus"; this.torchGpus.Size = new System.Drawing.Size(250, 21); this.torchGpus.TabIndex = 55; @@ -713,7 +725,7 @@ // label33 // this.label33.AutoSize = true; - this.label33.Location = new System.Drawing.Point(10, 130); + this.label33.Location = new System.Drawing.Point(10, 120); this.label33.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label33.Name = "label33"; this.label33.Size = new System.Drawing.Size(205, 13); @@ -730,7 +742,7 @@ "0", "1", "0,1"}); - this.ncnnGpus.Location = new System.Drawing.Point(280, 157); + this.ncnnGpus.Location = new System.Drawing.Point(280, 147); this.ncnnGpus.Name = "ncnnGpus"; this.ncnnGpus.Size = new System.Drawing.Size(250, 21); this.ncnnGpus.TabIndex = 53; @@ -738,7 +750,7 @@ // label5 // this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(10, 160); + this.label5.Location = new System.Drawing.Point(10, 150); this.label5.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(200, 13); @@ -749,7 +761,7 @@ // this.label32.AutoSize = true; this.label32.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold); - this.label32.Location = new System.Drawing.Point(10, 100); + this.label32.Location = new System.Drawing.Point(10, 90); this.label32.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label32.Name = "label32"; this.label32.Size = new System.Drawing.Size(162, 16); @@ -840,7 +852,7 @@ // this.panel8.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent; this.panel8.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.panel8.Location = new System.Drawing.Point(386, 247); + this.panel8.Location = new System.Drawing.Point(386, 237); this.panel8.Name = "panel8"; this.panel8.Size = new System.Drawing.Size(21, 21); this.panel8.TabIndex = 60; @@ -850,7 +862,7 @@ // this.panel7.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent; this.panel7.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.panel7.Location = new System.Drawing.Point(386, 217); + this.panel7.Location = new System.Drawing.Point(386, 207); this.panel7.Name = "panel7"; this.panel7.Size = new System.Drawing.Size(21, 21); this.panel7.TabIndex = 59; @@ -889,7 +901,7 @@ // label23 // this.label23.AutoSize = true; - this.label23.Location = new System.Drawing.Point(10, 280); + this.label23.Location = new System.Drawing.Point(10, 270); this.label23.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label23.Name = "label23"; this.label23.Size = new System.Drawing.Size(187, 13); @@ -906,7 +918,7 @@ this.maxFpsMode.Items.AddRange(new object[] { "Encode With Limited FPS", "Encode With Full FPS And Create Second Video With Limited FPS"}); - this.maxFpsMode.Location = new System.Drawing.Point(280, 277); + this.maxFpsMode.Location = new System.Drawing.Point(280, 267); this.maxFpsMode.Name = "maxFpsMode"; this.maxFpsMode.Size = new System.Drawing.Size(400, 21); this.maxFpsMode.TabIndex = 49; @@ -915,7 +927,7 @@ // this.label22.AutoSize = true; this.label22.ForeColor = System.Drawing.Color.Silver; - this.label22.Location = new System.Drawing.Point(420, 251); + this.label22.Location = new System.Drawing.Point(420, 241); this.label22.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label22.Name = "label22"; this.label22.Size = new System.Drawing.Size(325, 13); @@ -933,7 +945,7 @@ "30", "60", "120"}); - this.maxFps.Location = new System.Drawing.Point(280, 247); + this.maxFps.Location = new System.Drawing.Point(280, 237); this.maxFps.Name = "maxFps"; this.maxFps.Size = new System.Drawing.Size(100, 21); this.maxFps.TabIndex = 47; @@ -941,7 +953,7 @@ // label20 // this.label20.AutoSize = true; - this.label20.Location = new System.Drawing.Point(10, 250); + this.label20.Location = new System.Drawing.Point(10, 240); this.label20.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label20.Name = "label20"; this.label20.Size = new System.Drawing.Size(144, 13); @@ -952,7 +964,7 @@ // this.label21.AutoSize = true; this.label21.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label21.Location = new System.Drawing.Point(10, 190); + this.label21.Location = new System.Drawing.Point(10, 180); this.label21.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label21.Name = "label21"; this.label21.Size = new System.Drawing.Size(227, 16); @@ -1114,7 +1126,7 @@ // this.label9.AutoSize = true; this.label9.ForeColor = System.Drawing.Color.Silver; - this.label9.Location = new System.Drawing.Point(420, 221); + this.label9.Location = new System.Drawing.Point(420, 211); this.label9.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(328, 13); @@ -1124,7 +1136,7 @@ // label8 // this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(10, 220); + this.label8.Location = new System.Drawing.Point(10, 210); this.label8.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(193, 13); @@ -1142,7 +1154,7 @@ "5", "10", "20"}); - this.minOutVidLength.Location = new System.Drawing.Point(280, 217); + this.minOutVidLength.Location = new System.Drawing.Point(280, 207); this.minOutVidLength.Name = "minOutVidLength"; this.minOutVidLength.Size = new System.Drawing.Size(100, 21); this.minOutVidLength.TabIndex = 29; @@ -1172,6 +1184,31 @@ this.debugTab.Size = new System.Drawing.Size(762, 419); this.debugTab.Text = "Debugging / Experimental"; // + // vfrMode + // + this.vfrMode.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); + this.vfrMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.vfrMode.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.vfrMode.ForeColor = System.Drawing.Color.White; + this.vfrMode.FormattingEnabled = true; + this.vfrMode.Items.AddRange(new object[] { + "CFR (Reduplicate Or Drop Frames)", + "VFR (Extend Or Drop Frames)"}); + this.vfrMode.Location = new System.Drawing.Point(280, 247); + this.vfrMode.Name = "vfrMode"; + this.vfrMode.Size = new System.Drawing.Size(250, 21); + this.vfrMode.TabIndex = 80; + // + // label48 + // + this.label48.AutoSize = true; + this.label48.Location = new System.Drawing.Point(10, 250); + this.label48.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); + this.label48.Name = "label48"; + this.label48.Size = new System.Drawing.Size(88, 13); + this.label48.TabIndex = 79; + this.label48.Text = "Timestamp Mode"; + // // ffEncPreset // this.ffEncPreset.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); @@ -1373,30 +1410,76 @@ this.titleLabel.TabIndex = 1; this.titleLabel.Text = "Settings"; // - // label48 + // label49 // - this.label48.AutoSize = true; - this.label48.Location = new System.Drawing.Point(10, 250); - this.label48.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); - this.label48.Name = "label48"; - this.label48.Size = new System.Drawing.Size(88, 13); - this.label48.TabIndex = 79; - this.label48.Text = "Timestamp Mode"; + this.label49.AutoSize = true; + this.label49.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label49.Location = new System.Drawing.Point(10, 180); + this.label49.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); + this.label49.Name = "label49"; + this.label49.Size = new System.Drawing.Size(122, 16); + this.label49.TabIndex = 62; + this.label49.Text = "Scene Detection"; // - // vfrMode + // label50 // - this.vfrMode.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); - this.vfrMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.vfrMode.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.vfrMode.ForeColor = System.Drawing.Color.White; - this.vfrMode.FormattingEnabled = true; - this.vfrMode.Items.AddRange(new object[] { - "CFR (Reduplicate Or Drop Frames)", - "VFR (Extend Or Drop Frames)"}); - this.vfrMode.Location = new System.Drawing.Point(280, 247); - this.vfrMode.Name = "vfrMode"; - this.vfrMode.Size = new System.Drawing.Size(250, 21); - this.vfrMode.TabIndex = 80; + this.label50.AutoSize = true; + this.label50.Location = new System.Drawing.Point(10, 213); + this.label50.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); + this.label50.Name = "label50"; + this.label50.Size = new System.Drawing.Size(194, 13); + this.label50.TabIndex = 63; + this.label50.Text = "Don\'t Interpolate Scene Changes (Cuts)"; + // + // scnDetect + // + this.scnDetect.AutoSize = true; + this.scnDetect.Location = new System.Drawing.Point(280, 213); + this.scnDetect.Name = "scnDetect"; + this.scnDetect.Size = new System.Drawing.Size(15, 14); + this.scnDetect.TabIndex = 64; + this.scnDetect.UseVisualStyleBackColor = true; + // + // scnDetectValue + // + this.scnDetectValue.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); + this.scnDetectValue.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.scnDetectValue.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.scnDetectValue.ForeColor = System.Drawing.Color.White; + this.scnDetectValue.FormattingEnabled = true; + this.scnDetectValue.Items.AddRange(new object[] { + "0.4", + "0.3", + "0.25", + "0.2", + "0.15", + "0.1"}); + this.scnDetectValue.Location = new System.Drawing.Point(364, 210); + this.scnDetectValue.Margin = new System.Windows.Forms.Padding(3, 3, 8, 3); + this.scnDetectValue.Name = "scnDetectValue"; + this.scnDetectValue.Size = new System.Drawing.Size(100, 21); + this.scnDetectValue.TabIndex = 65; + // + // label51 + // + this.label51.AutoSize = true; + this.label51.Location = new System.Drawing.Point(301, 214); + this.label51.Margin = new System.Windows.Forms.Padding(3); + this.label51.Name = "label51"; + this.label51.Size = new System.Drawing.Size(57, 13); + this.label51.TabIndex = 66; + this.label51.Text = "Sensitivity:"; + // + // label52 + // + this.label52.AutoSize = true; + this.label52.ForeColor = System.Drawing.Color.Silver; + this.label52.Location = new System.Drawing.Point(482, 214); + this.label52.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7); + this.label52.Name = "label52"; + this.label52.Size = new System.Drawing.Size(225, 13); + this.label52.TabIndex = 67; + this.label52.Text = "Lower values will detect more scene changes."; // // SettingsForm // @@ -1537,5 +1620,11 @@ private System.Windows.Forms.Label label45; private System.Windows.Forms.ComboBox vfrMode; private System.Windows.Forms.Label label48; + private System.Windows.Forms.ComboBox scnDetectValue; + private System.Windows.Forms.Label label51; + private System.Windows.Forms.CheckBox scnDetect; + private System.Windows.Forms.Label label50; + private System.Windows.Forms.Label label49; + private System.Windows.Forms.Label label52; } } \ No newline at end of file diff --git a/Code/Forms/SettingsForm.cs b/Code/Forms/SettingsForm.cs index 42acd42..4ef7246 100644 --- a/Code/Forms/SettingsForm.cs +++ b/Code/Forms/SettingsForm.cs @@ -58,6 +58,8 @@ namespace Flowframes.Forms ConfigParser.SaveComboxIndex(timingMode); ConfigParser.SaveGuiElement(enableLoop); ConfigParser.SaveGuiElement(jpegInterps); + ConfigParser.SaveGuiElement(scnDetect); + ConfigParser.SaveGuiElement(scnDetectValue); // AI ConfigParser.SaveComboxIndex(rifeMode); ConfigParser.SaveGuiElement(torchGpus); @@ -98,6 +100,8 @@ namespace Flowframes.Forms ConfigParser.LoadComboxIndex(timingMode); ConfigParser.LoadGuiElement(enableLoop); ConfigParser.LoadGuiElement(jpegInterps); + ConfigParser.LoadGuiElement(scnDetect); + ConfigParser.LoadGuiElement(scnDetectValue); // AI ConfigParser.LoadComboxIndex(rifeMode); ConfigParser.LoadGuiElement(torchGpus); diff --git a/Code/IO/Config.cs b/Code/IO/Config.cs index 7f7e51f..9db1921 100644 --- a/Code/IO/Config.cs +++ b/Code/IO/Config.cs @@ -1,6 +1,7 @@ using Flowframes.OS; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; @@ -75,7 +76,7 @@ namespace Flowframes.IO public static float GetFloat(string key) { - return float.Parse(Get(key)); + return float.Parse(Get(key), CultureInfo.InvariantCulture); } private static string WriteDefaultValIfExists(string key) // DEFAULTS TO 0, which means key that use 0 as default are not listed here @@ -94,6 +95,8 @@ namespace Flowframes.IO if (key == "vfrDedupe") return WriteDefault("vfrDedupe", "True"); if (key == "jpegInterps") return WriteDefault("jpegInterps", "False"); if (key == "timingMode") return WriteDefault("timingMode", "1"); + if (key == "scnDetect") return WriteDefault("scnDetect", "False"); + if (key == "scnDetectValue") return WriteDefault("scnDetectValue", "0.2"); // Video Export if (key == "h264Crf") return WriteDefault("h264Crf", "20"); if (key == "h265Crf") return WriteDefault("h265Crf", "22"); diff --git a/Code/IO/IOUtils.cs b/Code/IO/IOUtils.cs index 314a5a2..0273f0d 100644 --- a/Code/IO/IOUtils.cs +++ b/Code/IO/IOUtils.cs @@ -1,5 +1,6 @@  using Flowframes.Data; +using Flowframes.Main; using Microsoft.WindowsAPICodePack.Shell; using System; using System.Collections.Generic; @@ -398,5 +399,24 @@ namespace Flowframes.IO } return 0; } + + public enum ErrorMode { HiddenLog, VisibleLog, Messagebox } + public static bool CanWriteToDir (string dir, ErrorMode errMode) + { + string tempFile = Path.Combine(dir, "flowframes-testfile.tmp"); + try + { + File.Create(tempFile); + File.Delete(tempFile); + return true; + } + catch (Exception e) + { + Logger.Log($"Can't write to {dir}!", errMode == ErrorMode.HiddenLog); + if (errMode == ErrorMode.Messagebox && !BatchProcessing.busy) + MessageBox.Show($"Can't write to {dir}!", "Error"); + return false; + } + } } } diff --git a/Code/IO/Setup.cs b/Code/IO/Setup.cs index 4a49efe..c2ac8e2 100644 --- a/Code/IO/Setup.cs +++ b/Code/IO/Setup.cs @@ -31,7 +31,7 @@ namespace Flowframes { if (!Directory.Exists(Paths.GetPkgPath())) { - Logger.Log("Install invalid - Reason: " + Paths.GetPkgPath() + " does not exist."); + Logger.Log("Install invalid - Reason: " + Paths.GetPkgPath() + " does not exist.", true); return false; } diff --git a/Code/Magick/MagickDedupe.cs b/Code/Magick/MagickDedupe.cs index 6c9b3ba..1a253ea 100644 --- a/Code/Magick/MagickDedupe.cs +++ b/Code/Magick/MagickDedupe.cs @@ -1,12 +1,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Flowframes.IO; using Flowframes.Main; +using Flowframes.UI; +using Microsoft.VisualBasic.Devices; using ImageMagick; +using Flowframes.OS; namespace Flowframes.Magick { @@ -42,9 +46,29 @@ namespace Flowframes.Magick } } + public static Dictionary imageCache = new Dictionary(); + static MagickImage GetImage(string path) + { + bool allowCaching = true; + + if (!allowCaching) + return new MagickImage(path); + + if (!imageCache.ContainsKey(path)) + imageCache.Add(path, new MagickImage(path)); + + return imageCache[path]; + } + + public static void ClearCache () + { + imageCache.Clear(); + } public static async Task RemoveDupeFrames(string path, float threshold, string ext, bool testRun = false, bool debugLog = false, bool skipIfNoDupes = false) { + Stopwatch sw = new Stopwatch(); + sw.Restart(); Logger.Log("Removing duplicate frames - Threshold: " + threshold.ToString("0.00")); //Logger.Log("Analyzing frames..."); DirectoryInfo dirInfo = new DirectoryInfo(path); @@ -103,8 +127,13 @@ namespace Flowframes.Magick frame2 = framePaths[i].FullName; if (oldIndex >= 0) i = oldIndex; - MagickImage img1 = new MagickImage(frame1); - MagickImage img2 = new MagickImage(frame2); + + //long msBeforeLoad = sw.ElapsedMilliseconds; + MagickImage img2 = GetImage(frame2); + MagickImage img1 = GetImage(frame1); + + //MagickImage img1 = new MagickImage(frame1); + //MagickImage img2 = new MagickImage(frame2); double err = img1.Compare(img2, ErrorMetric.Fuzz); float errPercent = (float)err * 100f; @@ -136,13 +165,17 @@ namespace Flowframes.Magick currentDupeCount = 0; } - Logger.Log($"[FrameDedup] Difference from {Path.GetFileName(img1.FileName)} to {Path.GetFileName(img2.FileName)}: {errPercent.ToString("0.00")}% - {delStr}. Total: {statsFramesKept} kept / {statsFramesDeleted} deleted.", false, true); + if(i % 15 == 0) + { + Logger.Log($"[FrameDedup] Difference from {Path.GetFileName(img1.FileName)} to {Path.GetFileName(img2.FileName)}: {errPercent.ToString("0.00")}% - {delStr}. Total: {statsFramesKept} kept / {statsFramesDeleted} deleted.", false, true); + Program.mainForm.SetProgress((int)Math.Round(((float)i / framePaths.Length) * 100f)); + if (OSUtils.GetFreeRamMb() < 2500 && imageCache.Count > 25) + ClearCache(); + } - img1.Dispose(); - img2.Dispose(); + if(i % 5 == 0) + await Task.Delay(1); - Program.mainForm.SetProgress((int)Math.Round(((float)i / framePaths.Length) * 100f)); - await Task.Delay(10); if (Interpolate.canceled) return; if (!testRun && skipIfNoDupes && !hasEncounteredAnyDupes && i >= skipAfterNoDupesFrames) @@ -170,6 +203,8 @@ namespace Flowframes.Magick if (statsFramesKept <= 0) Interpolate.Cancel("No frames were left after de-duplication."); + Logger.Log($"Finished in {FormatUtils.Time(sw.Elapsed)} - {framePaths.Length / (sw.ElapsedMilliseconds / 1000f)} Imgs/Sec"); + //RenameCounterDir(path, "png"); //ZeroPadDir(path, ext, 8); } diff --git a/Code/Main/CreateVideo.cs b/Code/Main/CreateVideo.cs index e82cd23..0bdf131 100644 --- a/Code/Main/CreateVideo.cs +++ b/Code/Main/CreateVideo.cs @@ -117,7 +117,7 @@ namespace Flowframes.Main audioFileBasePath = Path.Combine(i.currentTempDir.GetParentDir(), "audio"); if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath))) await FFmpegCommands.ExtractAudio(sourceVideo, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists - if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath))) + if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)) || new FileInfo(IOUtils.GetAudioFile(audioFileBasePath)).Length < 4096) { Logger.Log("No compatible audio stream found."); return; diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index e7f6e2f..965f405 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -88,6 +88,12 @@ namespace Flowframes { Logger.Log("Extracting frames using FFmpeg..."); await Task.Delay(10); + if (Config.GetBool("scnDetect")) + { + Program.mainForm.SetStatus("Extracting scenes from video..."); + await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, "scenes")); + await Task.Delay(10); + } Program.mainForm.SetStatus("Extracting frames from video..."); Size resolution = IOUtils.GetVideoRes(inPath); int maxHeight = Config.GetInt("maxVidHeight"); @@ -130,8 +136,15 @@ namespace Flowframes public static bool firstFrameFix; static async Task PostProcessFrames () { + if(!Directory.Exists(framesPath) || IOUtils.GetAmountOfFiles(framesPath, false, "*.png") <= 0) + { + Cancel("Failed to extract frames from input video!"); + } + if (Config.GetInt("dedupMode") == 1) await MagickDedupe.Run(framesPath); + else + MagickDedupe.ClearCache(); if (canceled) return; diff --git a/Code/Main/VfrDedupe.cs b/Code/Main/VfrDedupe.cs index bd497d7..c62caf4 100644 --- a/Code/Main/VfrDedupe.cs +++ b/Code/Main/VfrDedupe.cs @@ -14,15 +14,18 @@ namespace Flowframes.Main { public static async Task CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor, bool firstFrameFix) { + bool sceneDetection = true; Logger.Log("Generating timecodes..."); FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles("*.png"); string vfrFile = Path.Combine(framesPath.GetParentDir(), "vfr.ini"); string fileContent = ""; + string scnFramesPath = Path.Combine(framesPath.GetParentDir(), "scenes"); string interpPath = framesPath.Replace(@"\", "/") + "-interpolated"; int lastFrameDuration = 1; + bool discardNext = false; // Calculate time duration between frames int totalFileCount = 1; @@ -33,25 +36,44 @@ namespace Flowframes.Main string filename1 = frameFiles[i].Name; string filename2 = frameFiles[i + 1].Name; + int durationTotal = Path.GetFileNameWithoutExtension(filename2).GetInt() - Path.GetFileNameWithoutExtension(filename1).GetInt(); lastFrameDuration = durationTotal; float durationPerInterpFrame = (float)durationTotal / interpFactor; - int interpolatedFrameCount = interpFactor; + int interpFramesAmount = interpFactor; + + + bool discardThisFrame = (sceneDetection && (i + 2) < frameFiles.Length && File.Exists(Path.Combine(scnFramesPath, frameFiles[i + 1].Name))); // i+2 is in scene detection folder, means i+1 is ugly interp frame + //if (discardThisFrame) Logger.Log("Will discard " + totalFileCount); // If loop is enabled, account for the extra frame added to the end for loop continuity if (loopEnabled && i == (frameFiles.Length - 2)) - interpolatedFrameCount = interpolatedFrameCount * 2; + interpFramesAmount = interpFramesAmount * 2; // Generate frames file lines - for (int frm = 0; frm < interpolatedFrameCount; frm++) + for (int frm = 0; frm < interpFramesAmount; frm++) { string durationStr = ((durationPerInterpFrame / 1000f) * 1).ToString("0.00000").Replace(",", "."); + + if (discardThisFrame) + { + int lastNum = totalFileCount - 1; + for(int dupeCount = 1; dupeCount < interpFramesAmount; dupeCount++) + { + //Logger.Log($"-> Writing #{frm + 1}: {lastNum}"); + fileContent += $"file '{interpPath}/{lastNum.ToString().PadLeft(8, '0')}.png'\nduration {durationStr}\n"; + totalFileCount++; + } + frm = interpFramesAmount - 1; + } + + //Logger.Log($"-> Writing #{frm+1}: {totalFileCount}"); fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(8, '0')}.png'\nduration {durationStr}\n"; - totalFileCount++; + totalFileCount++; // Don't increment, so we dupe the last frame and ignore the interp frame } - if((i+1) % 50 == 0) + if ((i+1) % 50 == 0) { Logger.Log($"Generating timecodes... {i + 1}/{frameFiles.Length}", false, true); await Task.Delay(1); diff --git a/Code/OS/AiProcess.cs b/Code/OS/AiProcess.cs index cbc7f5c..2d91349 100644 --- a/Code/OS/AiProcess.cs +++ b/Code/OS/AiProcess.cs @@ -269,7 +269,7 @@ namespace Flowframes if (!hasShownError && line.ToLower().Contains("no longer supports this gpu")) { hasShownError = true; - InterpolateUtils.ShowMessage("Your GPU seems to be outdated and is not supported!\n\n{line}", "Error"); + InterpolateUtils.ShowMessage($"Your GPU seems to be outdated and is not supported!\n\n{line}", "Error"); } if (!hasShownError && line.Contains("RuntimeError")) @@ -277,6 +277,15 @@ namespace Flowframes hasShownError = true; InterpolateUtils.ShowMessage($"An error occured during interpolation!\n\n{line}", "Error"); } + + if (!hasShownError && line.Contains("vkQueueSubmit failed")) + { + hasShownError = true; + InterpolateUtils.ShowMessage($"A Vulkan error occured during interpolation!\n\n{line}", "Error"); + } + + if (hasShownError) + Interpolate.Cancel(); } } } diff --git a/Code/OS/OSUtils.cs b/Code/OS/OSUtils.cs index 3436b64..8a15bac 100644 --- a/Code/OS/OSUtils.cs +++ b/Code/OS/OSUtils.cs @@ -7,6 +7,7 @@ using System.Management; using Flowframes.IO; using DiskDetector; using DiskDetector.Models; +using Microsoft.VisualBasic.Devices; namespace Flowframes.OS { @@ -114,5 +115,10 @@ namespace Flowframes.OS { return (Encoding.UTF8.GetByteCount(str) != str.Length); } + + public static int GetFreeRamMb () + { + return (int)(new ComputerInfo().AvailablePhysicalMemory / 1048576); + } } } \ No newline at end of file diff --git a/Code/UI/FormatUtils.cs b/Code/UI/FormatUtils.cs index 7e329f3..8f43ae4 100644 --- a/Code/UI/FormatUtils.cs +++ b/Code/UI/FormatUtils.cs @@ -42,7 +42,7 @@ namespace Flowframes.UI if (span.TotalMinutes >= 1f) return span.ToString(@"mm\:ss"); - if (span.TotalSeconds >= 1f) + if (span.TotalSeconds >= 3f) return span.ToString(@"ss") + "s"; return span.Milliseconds.ToString() + "ms"; diff --git a/Code/UI/GetWebInfo.cs b/Code/UI/GetWebInfo.cs index 5c35d4f..a68ae45 100644 --- a/Code/UI/GetWebInfo.cs +++ b/Code/UI/GetWebInfo.cs @@ -12,7 +12,7 @@ namespace Flowframes.UI { public static async Task LoadNews (Label newsLabel) { - string url = $"https://dl.nmkd.de/flowframes/changelog.txt"; + string url = $"http://dl.nmkd.de/flowframes/changelog.txt"; var client = new WebClient(); var str = await client.DownloadStringTaskAsync(new Uri(url)); newsLabel.Text = str; @@ -20,7 +20,7 @@ namespace Flowframes.UI public static async Task LoadPatronList(Label patronsLabel) { - string url = $"https://dl.nmkd.de/flowframes/patreon.txt"; + string url = $"http://dl.nmkd.de/flowframes/patreon.txt"; var client = new WebClient(); var str = await client.DownloadStringTaskAsync(new Uri(url)); patronsLabel.Text = str; diff --git a/Code/packages.config b/Code/packages.config index eddecf1..67aa1a9 100644 --- a/Code/packages.config +++ b/Code/packages.config @@ -13,9 +13,6 @@ - - -