mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-16 16:37:48 +01:00
WIP: VFR Dedupe - Added code to create timecode file
This commit is contained in:
@@ -11,34 +11,19 @@ namespace Flowframes
|
||||
{
|
||||
static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart -vf \"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
|
||||
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool rgb8, bool hdr, bool delSrc)
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc)
|
||||
{
|
||||
if (!Directory.Exists(frameFolderPath))
|
||||
Directory.CreateDirectory(frameFolderPath);
|
||||
string hdrStr = "";
|
||||
if (hdr) hdrStr = FFmpegStrings.hdrFilter;
|
||||
string deDupeStr = "";
|
||||
if (deDupe) deDupeStr = "-vf mpdecimate";
|
||||
string fmtStr = "";
|
||||
if (rgb8) fmtStr = "-pix_fmt rgb8";
|
||||
string args = $"-i {inputFile.Wrap()} {hdrStr} -vsync 0 {fmtStr} {deDupeStr} \"{frameFolderPath}/%08d.png\"";
|
||||
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
||||
await Task.Delay(1);
|
||||
if (delSrc)
|
||||
DeleteSource(inputFile);
|
||||
await VideoToFrames(inputFile, frameFolderPath, deDupe, delSrc, new Size());
|
||||
}
|
||||
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, int w, int h, bool deDupe, bool rgb8, bool hdr, bool delSrc)
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size)
|
||||
{
|
||||
string sizeStr = "";
|
||||
if (size.Width > 1 && size.Height > 1) sizeStr = $"-s {size.Width}x{size.Height}";
|
||||
if (!Directory.Exists(frameFolderPath))
|
||||
Directory.CreateDirectory(frameFolderPath);
|
||||
string hdrStr = "";
|
||||
if (hdr) hdrStr = FFmpegStrings.hdrFilter;
|
||||
string deDupeStr = "";
|
||||
if (deDupe) deDupeStr = "-vf mpdecimate";
|
||||
string fmtStr = "";
|
||||
if (rgb8) fmtStr = "-pix_fmt rgb8";
|
||||
string args = $"-i {inputFile.Wrap()} -compression_level 3 {hdrStr} -vsync 0 {fmtStr} {deDupeStr} -s {w}x{h} \"{frameFolderPath}/%08d.png\"";
|
||||
string args = $"-i {inputFile.Wrap()} -compression_level 3 -vsync 0 -pix_fmt rgb24 {sizeStr} \"{frameFolderPath}/%08d.png\"";
|
||||
if(deDupe) args = $"-i {inputFile.Wrap()} -copyts -r 1000 -compression_level 3 -vsync 0 -frame_pts true -vf mpdecimate {sizeStr} \"{frameFolderPath}/%08d.png\"";
|
||||
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
||||
await Task.Delay(1);
|
||||
if (delSrc)
|
||||
|
||||
@@ -191,6 +191,7 @@
|
||||
<Compile Include="Main\BatchProcessing.cs" />
|
||||
<Compile Include="Main\CreateVideo.cs" />
|
||||
<Compile Include="Main\InterpolateUtils.cs" />
|
||||
<Compile Include="Main\VfrDedupe.cs" />
|
||||
<Compile Include="OS\AiProcess.cs" />
|
||||
<Compile Include="ExtensionMethods.cs" />
|
||||
<Compile Include="AudioVideo\AvProcess.cs" />
|
||||
@@ -209,8 +210,8 @@
|
||||
<Compile Include="IO\IOUtils.cs" />
|
||||
<Compile Include="IO\Paths.cs" />
|
||||
<Compile Include="Logger.cs" />
|
||||
<Compile Include="Magick\Coverter.cs" />
|
||||
<Compile Include="Magick\FrameDedup.cs" />
|
||||
<Compile Include="Magick\Converter.cs" />
|
||||
<Compile Include="Magick\MagickDedupe.cs" />
|
||||
<Compile Include="OS\NvApi.cs" />
|
||||
<Compile Include="OS\OSUtils.cs" />
|
||||
<Compile Include="OS\Python.cs" />
|
||||
|
||||
@@ -103,6 +103,7 @@ namespace Flowframes.IO
|
||||
if (key == "timingMode") return WriteDefault("timingMode", "1");
|
||||
if (key == "tempDirCustom") return WriteDefault("tempDirCustom", "C:/");
|
||||
if (key == "ffprobeCountFrames") return WriteDefault("ffprobeCountFrames", "False");
|
||||
if (key == "vfrDedupe") return WriteDefault("vfrDedupe", "True");
|
||||
return WriteDefault(key, "0");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ using System.Threading.Tasks;
|
||||
namespace Flowframes.Magick
|
||||
{
|
||||
|
||||
class Coverter
|
||||
class Converter
|
||||
{
|
||||
public static async Task Convert (string dir, MagickFormat format, int quality, string ext = "", bool print = true, bool setProgress = true)
|
||||
{
|
||||
@@ -10,7 +10,7 @@ using ImageMagick;
|
||||
|
||||
namespace Flowframes.Magick
|
||||
{
|
||||
class FrameDedup
|
||||
class MagickDedupe
|
||||
{
|
||||
public enum Mode { None, Info, Enabled, Auto }
|
||||
public static Mode currentMode;
|
||||
@@ -30,7 +30,7 @@ namespace Flowframes.Main
|
||||
}
|
||||
await Task.Delay(10);
|
||||
if(Config.GetInt("timingMode") == 1)
|
||||
await FrameDedup.Reduplicate(path);
|
||||
await MagickDedupe.Reduplicate(path);
|
||||
Program.mainForm.SetStatus("Creating output video from frames...");
|
||||
try
|
||||
{
|
||||
@@ -61,7 +61,7 @@ namespace Flowframes.Main
|
||||
if (new DirectoryInfo(framesPath).GetFiles()[0].Extension != ".png")
|
||||
{
|
||||
Logger.Log("Converting output frames to PNG to encode with Gifski...");
|
||||
await Coverter.Convert(framesPath, ImageMagick.MagickFormat.Png00, 20, "png", false);
|
||||
await Converter.Convert(framesPath, ImageMagick.MagickFormat.Png00, 20, "png", false);
|
||||
}
|
||||
await GifskiCommands.CreateGifFromFrames(i.currentOutFps.RoundToInt(), Config.GetInt("gifskiQ"), framesPath, outPath);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ namespace Flowframes
|
||||
if (cancelled) return;
|
||||
sw.Restart();
|
||||
await Task.Delay(10);
|
||||
await FrameDedup.Run(framesPath);
|
||||
if (Config.GetBool("vfrDedupe"))
|
||||
VfrDedupe.CreateTimecodeFile(framesPath, Config.GetBool("enableLoop"), interpFactor);
|
||||
await MagickDedupe.Run(framesPath);
|
||||
if (cancelled) return;
|
||||
string interpFramesDir = Path.Combine(currentTempDir, "frames-interpolated");
|
||||
string outPath = Path.Combine(outDir, Path.GetFileNameWithoutExtension(inPath) + IOUtils.GetAiSuffix(ai, interpFactor) + Utils.GetExt(outMode));
|
||||
@@ -86,7 +88,6 @@ namespace Flowframes
|
||||
{
|
||||
Logger.Log("Extracting frames using FFmpeg...");
|
||||
await Task.Delay(10);
|
||||
bool rgb8 = Formats.preprocess.Contains(Path.GetExtension(inPath).ToLower());
|
||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||
Size resolution = IOUtils.GetVideoRes(inPath);
|
||||
int maxHeight = Config.GetInt("maxVidHeight");
|
||||
@@ -95,11 +96,11 @@ namespace Flowframes
|
||||
float factor = (float)maxHeight / resolution.Height;
|
||||
int width = (resolution.Width * factor).RoundToInt();
|
||||
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, width, maxHeight, false, rgb8, false, false);
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetBool("vfrDedupe"), false, new Size(width, maxHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, false, rgb8, false, false);
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetBool("vfrDedupe"), false);
|
||||
}
|
||||
/*
|
||||
if (AvProcess.lastOutputFfmpeg.ToLower().Contains("invalid"))
|
||||
@@ -111,7 +112,7 @@ namespace Flowframes
|
||||
if (extractAudio)
|
||||
{
|
||||
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
|
||||
if (!File.Exists(audioFile))
|
||||
if (audioFile != null && !File.Exists(audioFile))
|
||||
await FFmpegCommands.ExtractAudio(inPath, audioFile);
|
||||
}
|
||||
if (!cancelled && Config.GetBool("enableLoop"))
|
||||
@@ -172,6 +173,7 @@ namespace Flowframes
|
||||
cancelled = true;
|
||||
Program.mainForm.SetStatus("Cancelled.");
|
||||
Program.mainForm.SetProgress(0);
|
||||
if(!Config.GetBool("keepTempFolder"))
|
||||
IOUtils.TryDeleteIfExists(currentTempDir);
|
||||
Program.mainForm.SetWorking(false);
|
||||
Logger.Log("Cancelled interpolation.");
|
||||
|
||||
47
Code/Main/VfrDedupe.cs
Normal file
47
Code/Main/VfrDedupe.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flowframes.Main
|
||||
{
|
||||
class VfrDedupe
|
||||
{
|
||||
public static void CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor)
|
||||
{
|
||||
FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles("*.png");
|
||||
string vfrFile = Path.Combine(framesPath.GetParentDir(), "vfr.ini");
|
||||
|
||||
int lastFrameDuration = 1;
|
||||
|
||||
// Calculate time duration between frames
|
||||
int totalFileCount = 1;
|
||||
for (int i = 0; i < (frameFiles.Length - 1); i++)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
//Logger.Log("Frame " + i);
|
||||
|
||||
// Generate frames file lines
|
||||
for (int frm = 0; frm < interpolatedFrameCount; frm++)
|
||||
{
|
||||
//Logger.Log("Writing info for interp frame " + frm);
|
||||
string durationStr = (durationPerInterpFrame / 1000f).ToString("0.0000").Replace(",", ".");
|
||||
File.AppendAllText(vfrFile, $"{totalFileCount.ToString().PadLeft(8, '0')}.png\nduration {durationStr}\n");
|
||||
totalFileCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,8 @@ namespace Flowframes.UI
|
||||
public static async Task ExtractVideo(string videoPath, bool withAudio)
|
||||
{
|
||||
string outPath = Path.ChangeExtension(videoPath, null) + "-extracted";
|
||||
bool rgb8 = Formats.preprocess.Contains(Path.GetExtension(videoPath).ToLower());
|
||||
Program.mainForm.SetWorking(true);
|
||||
await FFmpegCommands.VideoToFrames(videoPath, Path.Combine(outPath, "frames"), false, rgb8, false, false);
|
||||
await FFmpegCommands.VideoToFrames(videoPath, Path.Combine(outPath, "frames"), false, false);
|
||||
File.WriteAllText(Path.Combine(outPath, "fps.ini"), Interpolate.currentInFps.ToString());
|
||||
if (withAudio)
|
||||
await FFmpegCommands.ExtractAudio(videoPath, Path.Combine(outPath, "audio"));
|
||||
@@ -94,7 +93,8 @@ namespace Flowframes.UI
|
||||
Program.mainForm.SetWorking(true);
|
||||
await Task.Delay(10);
|
||||
framesPath = Path.ChangeExtension(inPath, null) + "-frames";
|
||||
await Interpolate.ExtractFrames(inPath, framesPath);
|
||||
Directory.CreateDirectory(framesPath);
|
||||
await Interpolate.ExtractFrames(inPath, framesPath, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -103,7 +103,7 @@ namespace Flowframes.UI
|
||||
Program.mainForm.SetWorking(true);
|
||||
Logger.Log("Running frame de-duplication", true);
|
||||
await Task.Delay(10);
|
||||
await FrameDedup.Run(framesPath, testRun);
|
||||
await MagickDedupe.Run(framesPath, testRun);
|
||||
IOUtils.TryDeleteIfExists(framesPath);
|
||||
Program.mainForm.SetProgress(0);
|
||||
Program.mainForm.SetWorking(false);
|
||||
|
||||
Reference in New Issue
Block a user