Backported MediaFile stuff from Nmkoder

This commit is contained in:
n00mkrad 2022-07-20 18:10:31 +02:00
parent 30b3faf16d
commit d96513ed4b
39 changed files with 1383 additions and 271 deletions

View File

@ -21,6 +21,8 @@ namespace Flowframes.Data
public int[] SupportedFactors { get; set; } = new int[0];
public bool Piped { get; set; } = false;
public string LogFilename { get { return AiName.Replace("_", "-").ToLower() + "-log"; } }
public AI(AiBackend backend, string aiName, string friendlyName, string desc, string pkgDir, InterpFactorSupport factorSupport = InterpFactorSupport.Fixed, int[] supportedFactors = null)
{
Backend = backend;

183
Code/Data/MediaFile.cs Normal file
View File

@ -0,0 +1,183 @@
using Flowframes.Data.Streams;
using Flowframes.IO;
using Flowframes.Media;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Stream = Flowframes.Data.Streams.Stream;
namespace Flowframes.Data
{
public class MediaFile
{
public bool IsDirectory;
public FileInfo FileInfo;
public DirectoryInfo DirectoryInfo;
public int FileCount;
public string Name;
public string SourcePath;
public string ImportPath;
public string Format;
public string Title;
public string Language;
public Fraction? InputRate = null;
public long DurationMs;
public int StreamCount;
public int TotalKbits;
public long Size;
public List<Stream> AllStreams = new List<Stream>();
public List<VideoStream> VideoStreams = new List<VideoStream>();
public List<AudioStream> AudioStreams = new List<AudioStream>();
public List<SubtitleStream> SubtitleStreams = new List<SubtitleStream>();
public List<DataStream> DataStreams = new List<DataStream>();
public List<AttachmentStream> AttachmentStreams = new List<AttachmentStream>();
public VideoColorData ColorData = null;
public long CreationTime;
public bool Initialized = false;
public bool SequenceInitialized = false;
public int FrameCount { get { return VideoStreams.Count > 0 ? VideoStreams[0].FrameCount : 0; } }
public MediaFile(string path /* , bool requestFpsInputIfUnset = true */)
{
CreationTime = (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds; // Unix Timestamp as UID
if (IoUtils.IsPathDirectory(path))
{
IsDirectory = true;
DirectoryInfo = new DirectoryInfo(path);
Name = DirectoryInfo.Name;
SourcePath = DirectoryInfo.FullName;
Format = "Folder";
// if (requestFpsInputIfUnset && InputRate == null)
// {
// PromptForm form = new PromptForm("Enter Frame Rate", $"Please enter a frame rate to use for the image sequence '{Name.Trunc(80)}'.", "30");
// form.ShowDialog();
// InputRate = new Fraction(form.EnteredText);
// }
}
else
{
FileInfo = new FileInfo(path);
Name = FileInfo.Name;
SourcePath = FileInfo.FullName;
ImportPath = FileInfo.FullName;
Format = FileInfo.Extension.Remove(".").ToUpper();
FileCount = 1;
InputRate = new Fraction(-1, 1);
}
Size = GetSize();
}
public async Task InitializeSequence()
{
Logger.Log($"InitializeSequence not implemented!!");
// try
// {
// if (SequenceInitialized) return;
//
// Logger.Log($"Preparing image sequence...");
// Logger.Log($"MediaFile {Name}: Preparing image sequence", true);
// string seqPath = Path.Combine(Paths.GetFrameSeqPath(), CreationTime.ToString(), "frames.concat");
// string chosenExt = IoUtils.GetUniqueExtensions(SourcePath).FirstOrDefault();
// int fileCount = FfmpegUtils.CreateConcatFile(SourcePath, seqPath, new List<string> { chosenExt });
// ImportPath = seqPath;
// FileCount = fileCount;
// Logger.Log($"Created concat file with {fileCount} files.", true);
// SequenceInitialized = true;
// }
// catch (Exception e)
// {
// Logger.Log($"Error preparing frame sequence: {e.Message}\n{e.StackTrace}");
// FileCount = 0;
// }
}
public async Task Initialize(bool progressBar = true)
{
Logger.Log($"MediaFile {Name}: Initializing", true);
try
{
if (IsDirectory && !SequenceInitialized)
await InitializeSequence();
await LoadFormatInfo(ImportPath);
AllStreams = await FfmpegUtils.GetStreams(ImportPath, progressBar, StreamCount, (Fraction)InputRate, true);
VideoStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Video).Select(x => (VideoStream)x).ToList();
AudioStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Audio).Select(x => (AudioStream)x).ToList();
SubtitleStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Subtitle).Select(x => (SubtitleStream)x).ToList();
DataStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Data).Select(x => (DataStream)x).ToList();
AttachmentStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Attachment).Select(x => (AttachmentStream)x).ToList();
Logger.Log($"Loaded and sorted streams for {Name}", true);
}
catch (Exception e)
{
Logger.Log($"Failed to initialized MediaFile: {e.Message}", true);
}
Initialized = true;
}
private async Task LoadFormatInfo(string path)
{
Title = await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "TAG:title");
Language = await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "TAG:language");
DurationMs = (await FfmpegCommands.GetDurationMs(path));
StreamCount = await FfmpegUtils.GetStreamCount(path);
TotalKbits = (await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "bit_rate")).GetInt() / 1000;
}
public string GetName()
{
if (IsDirectory)
return DirectoryInfo.Name;
else
return FileInfo.Name;
}
public string GetPath()
{
if (IsDirectory)
return DirectoryInfo.FullName;
else
return FileInfo.FullName;
}
public long GetSize()
{
try
{
if (IsDirectory)
return IoUtils.GetDirSize(GetPath(), true);
else
return FileInfo.Length;
}
catch (Exception ex)
{
Logger.Log($"Failed to get file size of {FileInfo.FullName}: {ex.Message} (Path Length: {FileInfo.FullName.Length})", true);
return 0;
}
}
public bool CheckFiles()
{
if (IsDirectory)
return Directory.Exists(DirectoryInfo.FullName);
else
return File.Exists(FileInfo.FullName);
}
public override string ToString()
{
return $"{GetName()} ({FormatUtils.Bytes(Size)})";
}
}
}

View File

@ -57,7 +57,6 @@ namespace Flowframes.IO
return path;
}
public static string GetPkgPath()
{
string path = Path.Combine(GetDataPath(), "pkgs");

View File

@ -0,0 +1,22 @@
namespace Flowframes.Data.Streams
{
public class AttachmentStream : Stream
{
public string Filename { get; } = "";
public string MimeType { get; } = "";
public AttachmentStream(string codec, string codecLong, string filename, string mimeType)
{
base.Type = StreamType.Attachment;
Codec = codec;
CodecLong = codecLong;
Filename = filename;
MimeType = mimeType;
}
public override string ToString()
{
return $"{base.ToString()} - Filename: {Filename} - MIME Type: {MimeType}";
}
}
}

View File

@ -0,0 +1,29 @@
namespace Flowframes.Data.Streams
{
public class AudioStream : Stream
{
public int Kbits { get; }
public int SampleRate { get; }
public int Channels { get; }
public string Layout { get; }
public AudioStream(string language, string title, string codec, string codecLong, int kbits, int sampleRate, int channels, string layout)
{
base.Type = StreamType.Audio;
Language = language;
Title = title;
Codec = codec;
CodecLong = codecLong;
Kbits = kbits;
SampleRate = sampleRate;
Channels = channels;
Layout = layout;
}
public override string ToString()
{
string title = string.IsNullOrWhiteSpace(Title.Trim()) ? "None" : Title;
return $"{base.ToString()} - Language: {Language} - Title: {title} - Kbps: {Kbits} - SampleRate: {SampleRate} - Channels: {Channels} - Layout: {Layout}";
}
}
}

View File

@ -0,0 +1,17 @@
namespace Flowframes.Data.Streams
{
public class DataStream : Stream
{
public DataStream(string codec, string codecLong)
{
base.Type = StreamType.Data;
Codec = codec;
CodecLong = codecLong;
}
public override string ToString()
{
return $"{base.ToString()}";
}
}
}

View File

@ -0,0 +1,19 @@
namespace Flowframes.Data.Streams
{
public class Stream
{
public enum StreamType { Video, Audio, Subtitle, Data, Attachment, Unknown }
public StreamType Type;
public int Index = -1;
public bool IsDefault = false;
public string Codec = "";
public string CodecLong = "";
public string Language = "";
public string Title = "";
public override string ToString()
{
return $"Stream #{Index.ToString().PadLeft(2, '0')}{(IsDefault ? "*" : "")} - {Codec} {Type}";
}
}
}

View File

@ -0,0 +1,26 @@
using Flowframes.Extensions;
namespace Flowframes.Data.Streams
{
public class SubtitleStream : Stream
{
public bool Bitmap { get; }
public SubtitleStream(string language, string title, string codec, string codecLong, bool bitmap)
{
base.Type = StreamType.Subtitle;
Language = language;
Title = title;
Codec = codec;
CodecLong = codecLong;
Bitmap = bitmap;
}
public override string ToString()
{
string lang = string.IsNullOrWhiteSpace(Language.Trim()) ? "?" : Language;
string ttl = string.IsNullOrWhiteSpace(Title.Trim()) ? "None" : Title;
return $"{base.ToString()} - Language: {lang} - Title: {ttl} - Bitmap-based: {Bitmap.ToString().ToTitleCase()}";
}
}
}

View File

@ -0,0 +1,36 @@
using System.Drawing;
namespace Flowframes.Data.Streams
{
public class VideoStream : Stream
{
public int FrameCount { get; } = 0;
public string PixelFormat { get; }
public int Kbits { get; }
public Size Resolution { get; }
public Size Sar { get; }
public Size Dar { get; }
public Fraction Rate { get; }
public VideoStream(string language, string title, string codec, string codecLong, string pixFmt, int kbits, Size resolution, Size sar, Size dar, Fraction rate, int frameCount)
{
base.Type = StreamType.Video;
Codec = codec;
CodecLong = codecLong;
PixelFormat = pixFmt;
Kbits = kbits;
Resolution = resolution;
Sar = sar;
Dar = dar;
Rate = rate;
FrameCount = frameCount;
Language = language;
Title = title;
}
public override string ToString()
{
return $"{base.ToString()} - Language: {Language} - Color Format: {PixelFormat} - Size: {Resolution.Width}x{Resolution.Height} - FPS: {Rate}";
}
}
}

View File

@ -0,0 +1,54 @@
using Flowframes.Utilities;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Data
{
public class VideoColorData
{
public int ColorTransfer { get; set; } = 2;
public int ColorMatrixCoeffs { get; set; } = 2;
public int ColorPrimaries { get; set; } = 2;
public int ColorRange { get; set; } = 0;
public string RedX { get; set; } = "";
public string RedY { get; set; } = "";
public string GreenX { get; set; } = "";
public string GreenY { get; set; } = "";
public string BlueX { get; set; } = "";
public string BlueY { get; set; } = "";
public string WhiteX { get; set; } = "";
public string WhiteY { get; set; } = "";
public string LumaMin { get; set; } = "";
public string LumaMax { get; set; } = "";
public string MaxCll { get; set; } = "";
public string MaxFall { get; set; } = "";
public override string ToString()
{
List<string> lines = new List<string>();
try
{
lines.Add($"Color transfer: {ColorTransfer} ({ColorDataUtils.GetColorTransferName(ColorTransfer)})");
lines.Add($"Colour matrix coefficients: {ColorMatrixCoeffs} ({ColorDataUtils.GetColorMatrixCoeffsName(ColorMatrixCoeffs)})");
lines.Add($"Colour primaries: {ColorPrimaries} ({ColorDataUtils.GetColorPrimariesName(ColorPrimaries)})");
lines.Add($"Colour range: {ColorRange} ({ColorDataUtils.GetColorRangeName(ColorRange)})");
if (!string.IsNullOrWhiteSpace(RedX) && !string.IsNullOrWhiteSpace(RedY)) lines.Add($"Red color coordinates X/Y: {RedX}/{RedY}");
if (!string.IsNullOrWhiteSpace(GreenX) && !string.IsNullOrWhiteSpace(GreenY)) lines.Add($"Green color coordinates X/Y: {GreenX}/{GreenY}");
if (!string.IsNullOrWhiteSpace(BlueX) && !string.IsNullOrWhiteSpace(BlueY)) lines.Add($"Blue color coordinates X/Y: {BlueX}/{BlueY}");
if (!string.IsNullOrWhiteSpace(WhiteX) && !string.IsNullOrWhiteSpace(WhiteY)) lines.Add($"White color coordinates X/Y: {WhiteX}/{WhiteY}");
if (!string.IsNullOrWhiteSpace(LumaMin)) lines.Add($"Minimum luminance: {LumaMin}");
if (!string.IsNullOrWhiteSpace(LumaMax)) lines.Add($"Maximum luminance: {LumaMax}");
if (!string.IsNullOrWhiteSpace(MaxCll)) lines.Add($"Maximum Content Light Level: {MaxCll}");
if (!string.IsNullOrWhiteSpace(MaxFall)) lines.Add($"Maximum Frame-Average Light Level: {MaxFall}");
}
catch { }
return string.Join("\n", lines);
}
}
}

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Flowframes.Data;
using System.Management.Automation;
using System.Drawing;
namespace Flowframes
{
@ -230,5 +231,45 @@ namespace Flowframes
WildcardPattern pattern = new WildcardPattern(wildcard);
return pattern.IsMatch(str);
}
public static int RoundMod(this int n, int mod = 2) // Round to a number that's divisible by 2 (for h264 etc)
{
int a = (n / 2) * 2; // Smaller multiple
int b = a + 2; // Larger multiple
return (n - a > b - n) ? b : a; // Return of closest of two
}
public static string ToTitleCase(this string s)
{
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(s);
}
public static string ToStringShort(this Size s, string separator = "x")
{
return $"{s.Width}{separator}{s.Height}";
}
public static bool IsConcatFile(this string filePath)
{
try
{
return Path.GetExtension(filePath)?.ToLower() == ".concat";
}
catch
{
return false;
}
}
public static string GetConcStr(this string filePath, int rate = -1)
{
string rateStr = rate >= 0 ? $"-r {rate} " : "";
return filePath.IsConcatFile() ? $"{rateStr}-safe 0 -f concat " : "";
}
public static string GetFfmpegInputArg(this string filePath)
{
return $"{(filePath.IsConcatFile() ? filePath.GetConcStr() : "")} -i {filePath.Wrap()}";
}
}
}

View File

@ -335,8 +335,16 @@
<Compile Include="Data\AI.cs" />
<Compile Include="Data\AudioTrack.cs" />
<Compile Include="Data\Filetypes.cs" />
<Compile Include="Data\MediaFile.cs" />
<Compile Include="Data\ModelCollection.cs" />
<Compile Include="Data\Servers.cs" />
<Compile Include="Data\Streams\AttachmentStream.cs" />
<Compile Include="Data\Streams\AudioStream.cs" />
<Compile Include="Data\Streams\DataStream.cs" />
<Compile Include="Data\Streams\Stream.cs" />
<Compile Include="Data\Streams\SubtitleStream.cs" />
<Compile Include="Data\Streams\VideoStream.cs" />
<Compile Include="Data\VideoColorData.cs" />
<Compile Include="Data\VidExtraData.cs" />
<Compile Include="Data\Fraction.cs" />
<Compile Include="Data\InterpSettings.cs" />
@ -452,6 +460,7 @@
<Compile Include="Ui\MainUiFunctions.cs" />
<Compile Include="Ui\UiUtils.cs" />
<Compile Include="Ui\QuickSettingsTab.cs" />
<Compile Include="Utilities\ColorDataUtils.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
<SubType>Designer</SubType>

View File

@ -323,7 +323,7 @@ namespace Flowframes
ValidateFactor();
if (!BatchProcessing.busy) // Don't load values from gui if batch processing is used
Interpolate.current = GetCurrentSettings();
Interpolate.currentSettings = GetCurrentSettings();
AiProcessSuspend.Reset();
Interpolate.Start();
@ -707,8 +707,7 @@ namespace Flowframes
{
if (BatchProcessing.busy || !File.Exists(inputTbox.Text.Trim())) return;
Interpolate.current = GetCurrentSettings();
Interpolate.currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(Interpolate.current.inPath);
Interpolate.currentSettings = GetCurrentSettings();
AiProcessSuspend.Reset();
await Interpolate.Realtime();

View File

@ -560,7 +560,7 @@ namespace Flowframes.IO
public static async Task<string> GetCurrentExportFilename(bool fpsLimit, bool withExt)
{
InterpSettings curr = Interpolate.current;
InterpSettings curr = Interpolate.currentSettings;
string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
float fps = fpsLimit ? maxFps.GetFloat() : curr.outFps.GetFloat();
@ -570,7 +570,7 @@ namespace Flowframes.IO
Size outRes = await InterpolateUtils.GetOutputResolution(curr.inPath, false, false);
string pattern = Config.Get(Config.Key.exportNamePattern);
string inName = Interpolate.current.inputIsFrames ? Path.GetFileName(curr.inPath) : Path.GetFileNameWithoutExtension(curr.inPath);
string inName = Interpolate.currentSettings.inputIsFrames ? Path.GetFileName(curr.inPath) : Path.GetFileNameWithoutExtension(curr.inPath);
bool encodeBoth = Config.GetInt(Config.Key.maxFpsMode) == 0;
bool addSuffix = fpsLimit && (!pattern.Contains("[FPS]") && !pattern.Contains("[ROUNDFPS]")) && encodeBoth;
string filename = pattern;

View File

@ -35,7 +35,7 @@ namespace Flowframes.Magick
if (setStatus)
Program.mainForm.SetStatus("Blending scene transitions...");
string[] frames = FrameRename.framesAreRenamed ? new string[0] : IoUtils.GetFilesSorted(Interpolate.current.framesFolder);
string[] frames = FrameRename.framesAreRenamed ? new string[0] : IoUtils.GetFilesSorted(Interpolate.currentSettings.framesFolder);
List<Task> runningTasks = new List<Task>();
int maxThreads = Environment.ProcessorCount * 2;
@ -52,8 +52,8 @@ namespace Flowframes.Magick
string frameTo = FrameRename.framesAreRenamed ? values[1] : frames[values[1].GetInt()];
int amountOfBlendFrames = values[2].GetInt();
string img1 = Path.Combine(Interpolate.current.framesFolder, frameFrom);
string img2 = Path.Combine(Interpolate.current.framesFolder, frameTo);
string img1 = Path.Combine(Interpolate.currentSettings.framesFolder, frameFrom);
string img2 = Path.Combine(Interpolate.currentSettings.framesFolder, frameTo);
string firstOutputFrameName = line.Split('/').Last().Remove("'").Split('#').First();
string ext = Path.GetExtension(firstOutputFrameName);
@ -63,7 +63,7 @@ namespace Flowframes.Magick
for (int blendFrameNum = 1; blendFrameNum <= amountOfBlendFrames; blendFrameNum++)
{
int outputNum = firstOutputFrameNum + blendFrameNum;
string outputPath = Path.Combine(Interpolate.current.interpFolder, outputNum.ToString().PadLeft(Padding.interpFrames, '0'));
string outputPath = Path.Combine(Interpolate.currentSettings.interpFolder, outputNum.ToString().PadLeft(Padding.interpFrames, '0'));
outputPath = Path.ChangeExtension(outputPath, ext);
outputFilenames.Add(outputPath);
}

View File

@ -157,7 +157,7 @@ namespace Flowframes.Magick
if (Interpolate.canceled) return;
int framesLeft = IoUtils.GetAmountOfFiles(path, false, "*" + Interpolate.current.framesExt);
int framesLeft = IoUtils.GetAmountOfFiles(path, false, "*" + Interpolate.currentSettings.framesExt);
int framesDeleted = framePaths.Length - framesLeft;
float percentDeleted = ((float)framesDeleted / framePaths.Length) * 100f;
string keptPercent = $"{(100f - percentDeleted).ToString("0.0")}%";
@ -180,7 +180,7 @@ namespace Flowframes.Magick
static async Task<int> GetBufferSize ()
{
Size res = Interpolate.current.ScaledResolution;
Size res = Interpolate.currentSettings.ScaledResolution;
long pixels = res.Width * res.Height; // 4K = 8294400, 1440p = 3686400, 1080p = 2073600, 720p = 921600, 540p = 518400, 360p = 230400
int bufferSize = 100;
if (pixels < 518400) bufferSize = 1800;

View File

@ -32,14 +32,14 @@ namespace Flowframes.Main
public static void UpdateChunkAndBufferSizes ()
{
chunkSize = GetChunkSize((IoUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*" + Interpolate.current.framesExt) * Interpolate.current.interpFactor).RoundToInt());
chunkSize = GetChunkSize((IoUtils.GetAmountOfFiles(Interpolate.currentSettings.framesFolder, false, "*" + Interpolate.currentSettings.framesExt) * Interpolate.currentSettings.interpFactor).RoundToInt());
safetyBufferFrames = 90;
if (Interpolate.current.ai.Backend == AI.AiBackend.Ncnn)
if (Interpolate.currentSettings.ai.Backend == AI.AiBackend.Ncnn)
safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferNcnn, 150);
if (Interpolate.current.ai.Backend == AI.AiBackend.Pytorch)
if (Interpolate.currentSettings.ai.Backend == AI.AiBackend.Pytorch)
safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferCuda, 90);
}
@ -54,7 +54,7 @@ namespace Flowframes.Main
{
UpdateChunkAndBufferSizes();
bool imgSeq = Interpolate.current.outMode.ToString().ToLower().StartsWith("img");
bool imgSeq = Interpolate.currentSettings.outMode.ToString().ToLower().StartsWith("img");
interpFramesFolder = interpFramesPath;
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
@ -66,7 +66,7 @@ namespace Flowframes.Main
Logger.Log($"[AE] Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true);
int chunkNo = AutoEncodeResume.encodedChunks + 1;
string encFile = Path.Combine(interpFramesPath.GetParentDir(), Paths.GetFrameOrderFilename(Interpolate.current.interpFactor));
string encFile = Path.Combine(interpFramesPath.GetParentDir(), Paths.GetFrameOrderFilename(Interpolate.currentSettings.interpFactor));
interpFramesLines = IoUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'").Split('#').First()).ToArray(); // Array with frame filenames
while (!Interpolate.canceled && GetInterpFramesAmount() < 2)
@ -103,7 +103,7 @@ namespace Flowframes.Main
if(overwhelmed && !AiProcessSuspend.aiProcFrozen && OsUtils.IsProcessHidden(AiProcess.lastAiProcess))
{
string dirSize = FormatUtils.Bytes(IoUtils.GetDirSize(Interpolate.current.interpFolder, true));
string dirSize = FormatUtils.Bytes(IoUtils.GetDirSize(Interpolate.currentSettings.interpFolder, true));
Logger.Log($"AutoEnc is overwhelmed! ({unencodedFrameLines.Count} unencoded frames > {maxFrames}) - Pausing.", true);
AiProcessSuspend.SuspendResumeAi(true);
}
@ -130,12 +130,12 @@ namespace Flowframes.Main
}
busy = true;
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{chunkNo.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.current.outMode)}");
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{chunkNo.ToString().PadLeft(4, '0')}{FfmpegUtils.GetExt(Interpolate.currentSettings.outMode)}");
string firstFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.First()].Trim());
string lastFile = Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()].Trim());
Logger.Log($"[AE] Encoding Chunk #{chunkNo} to using line {frameLinesToEncode.First()} ({firstFile}) through {frameLinesToEncode.Last()} ({lastFile}) - {unencodedFrameLines.Count} unencoded frames left in total", true, false, "ffmpeg");
await Export.EncodeChunk(outpath, Interpolate.current.interpFolder, chunkNo, Interpolate.current.outMode, frameLinesToEncode.First(), frameLinesToEncode.Count);
await Export.EncodeChunk(outpath, Interpolate.currentSettings.interpFolder, chunkNo, Interpolate.currentSettings.outMode, frameLinesToEncode.First(), frameLinesToEncode.Count);
if (Interpolate.canceled) return;
@ -153,7 +153,7 @@ namespace Flowframes.Main
if(!imgSeq && Config.GetInt(Config.Key.autoEncBackupMode) > 0)
{
if (aiRunning && (currentMuxTask == null || (currentMuxTask != null && currentMuxTask.IsCompleted)))
currentMuxTask = Task.Run(() => Export.ChunksToVideo(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outPath, true));
currentMuxTask = Task.Run(() => Export.ChunksToVideo(Interpolate.currentSettings.tempFolder, videoChunksFolder, Interpolate.currentSettings.outPath, true));
else
Logger.Log($"[AE] Skipping backup because {(!aiRunning ? "this is the final chunk" : "previous mux task has not finished yet")}!", true, false, "ffmpeg");
}
@ -178,7 +178,7 @@ namespace Flowframes.Main
if (imgSeq)
return;
await Export.ChunksToVideo(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outPath);
await Export.ChunksToVideo(Interpolate.currentSettings.tempFolder, videoChunksFolder, Interpolate.currentSettings.outPath);
}
catch (Exception e)
{
@ -239,7 +239,7 @@ namespace Flowframes.Main
static int GetInterpFramesAmount()
{
return IoUtils.GetAmountOfFiles(interpFramesFolder, false, "*" + Interpolate.current.interpExt);
return IoUtils.GetAmountOfFiles(interpFramesFolder, false, "*" + Interpolate.currentSettings.interpExt);
}
}
}

View File

@ -34,7 +34,7 @@ namespace Flowframes.Main
public static void Save ()
{
string saveDir = Path.Combine(I.current.tempFolder, Paths.resumeDir);
string saveDir = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir);
Directory.CreateDirectory(saveDir);
string chunksJsonPath = Path.Combine(saveDir, chunksFilename);
@ -47,7 +47,7 @@ namespace Flowframes.Main
File.WriteAllText(inputFramesJsonPath, JsonConvert.SerializeObject(processedInputFrames, Formatting.Indented));
string settingsJsonPath = Path.Combine(saveDir, interpSettingsFilename);
File.WriteAllText(settingsJsonPath, JsonConvert.SerializeObject(I.current, Formatting.Indented));
File.WriteAllText(settingsJsonPath, JsonConvert.SerializeObject(I.currentSettings, Formatting.Indented));
}
public static void LoadTempFolder(string tempFolderPath)
@ -72,8 +72,8 @@ namespace Flowframes.Main
try
{
string chunkJsonPath = Path.Combine(I.current.tempFolder, Paths.resumeDir, chunksFilename);
string inFramesJsonPath = Path.Combine(I.current.tempFolder, Paths.resumeDir, inputFramesFilename);
string chunkJsonPath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, chunksFilename);
string inFramesJsonPath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, inputFramesFilename);
dynamic chunksData = JsonConvert.DeserializeObject(File.ReadAllText(chunkJsonPath));
encodedChunks = chunksData.encodedChunks;
@ -84,18 +84,18 @@ namespace Flowframes.Main
foreach (string inputFrameName in processedInputFrames)
{
string inputFrameFullPath = Path.Combine(I.current.tempFolder, Paths.framesDir, inputFrameName);
string inputFrameFullPath = Path.Combine(I.currentSettings.tempFolder, Paths.framesDir, inputFrameName);
IoUtils.TryDeleteIfExists(inputFrameFullPath);
}
string videoChunksFolder = Path.Combine(I.current.tempFolder, Paths.chunksDir);
string videoChunksFolder = Path.Combine(I.currentSettings.tempFolder, Paths.chunksDir);
FileInfo[] invalidChunks = IoUtils.GetFileInfosSorted(videoChunksFolder, true, "????.*").Skip(encodedChunks).ToArray();
foreach (FileInfo chunk in invalidChunks)
chunk.Delete();
int inputFramesLeft = IoUtils.GetAmountOfFiles(Path.Combine(I.current.tempFolder, Paths.framesDir), false);
int inputFramesLeft = IoUtils.GetAmountOfFiles(Path.Combine(I.currentSettings.tempFolder, Paths.framesDir), false);
Logger.Log($"Resume: Already encoded {encodedFrames} frames in {encodedChunks} chunks. There are now {inputFramesLeft} input frames left to interpolate.");
@ -104,7 +104,7 @@ namespace Flowframes.Main
if(IoUtils.GetAmountOfFiles(videoChunksFolder, true, "*.*") > 0)
{
Logger.Log($"No more frames left to interpolate - Merging existing video chunks instead.");
await Export.ChunksToVideo(I.current.tempFolder, videoChunksFolder, I.current.outPath);
await Export.ChunksToVideo(I.currentSettings.tempFolder, videoChunksFolder, I.currentSettings.outPath);
await I.Done();
}
else

View File

@ -84,7 +84,7 @@ namespace Flowframes.Main
Logger.Log($"Queue: Processing {fname} ({entry.interpFactor}x {entry.ai.AiNameShort}).");
Program.mainForm.LoadBatchEntry(entry); // Load entry into GUI
Interpolate.current = entry;
Interpolate.currentSettings = entry;
Program.mainForm.runBtn_Click(null, null);
while (Program.busy)

View File

@ -25,7 +25,7 @@ namespace Flowframes.Main
{
if(Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
{
string frameFile = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilename(I.current.interpFactor));
string frameFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
await Blend.BlendSceneChanges(frameFile);
}
@ -44,7 +44,7 @@ namespace Flowframes.Main
return;
}
if (IoUtils.GetAmountOfFiles(path, false, "*" + I.current.interpExt) <= 1)
if (IoUtils.GetAmountOfFiles(path, false, "*" + I.currentSettings.interpExt) <= 1)
{
I.Cancel("Output folder does not contain frames - An error must have occured during interpolation!", AiProcess.hasShownError);
return;
@ -56,14 +56,14 @@ namespace Flowframes.Main
{
string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
bool fpsLimit = maxFps.GetFloat() > 0f && I.current.outFps.GetFloat() > maxFps.GetFloat();
bool fpsLimit = maxFps.GetFloat() > 0f && I.currentSettings.outFps.GetFloat() > maxFps.GetFloat();
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
if (!dontEncodeFullFpsVid)
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(false, true)), I.current.outFps, new Fraction());
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(false, true)), I.currentSettings.outFps, new Fraction());
if (fpsLimit)
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(true, true)), I.current.outFps, maxFps);
await Encode(mode, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(true, true)), I.currentSettings.outFps, maxFps);
}
catch (Exception e)
{
@ -79,31 +79,31 @@ namespace Flowframes.Main
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath)[0]).Remove(".").ToUpper();
string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
bool fpsLimit = maxFps.GetFloat() > 0f && I.current.outFps.GetFloat() > maxFps.GetFloat();
bool fpsLimit = maxFps.GetFloat() > 0f && I.currentSettings.outFps.GetFloat() > maxFps.GetFloat();
bool dontEncodeFullFpsSeq = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.current.interpFactor));
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
if (!dontEncodeFullFpsSeq)
{
string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(false, false));
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, false));
IoUtils.RenameExistingFolder(outputFolderPath);
Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}'...");
if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format
await CopyOutputFrames(framesPath, framesFile, outputFolderPath, 1, fpsLimit, false);
else // Encode if frames are not in desired format
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.current.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat));
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.currentSettings.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat));
}
if (fpsLimit)
{
string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false));
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(true, false));
Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}' (Resampled to {maxFps} FPS)...");
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.current.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat));
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.currentSettings.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat));
}
if (!stepByStep)
await IoUtils.DeleteContentsOfDirAsync(I.current.interpFolder);
await IoUtils.DeleteContentsOfDirAsync(I.currentSettings.interpFolder);
}
static int GetImgSeqQ (string format)
@ -167,7 +167,7 @@ namespace Flowframes.Main
static async Task Encode(I.OutMode mode, string framesPath, string outPath, Fraction fps, Fraction resampleFps)
{
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.current.interpFactor));
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
if (!File.Exists(framesFile))
{
@ -178,13 +178,13 @@ namespace Flowframes.Main
if (mode == I.OutMode.VidGif)
{
await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, Config.GetInt(Config.Key.gifColors), resampleFps, I.current.outItsScale);
await FfmpegEncode.FramesToGifConcat(framesFile, outPath, fps, true, Config.GetInt(Config.Key.gifColors), resampleFps, I.currentSettings.outItsScale);
}
else
{
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.current.inPath);
await FfmpegEncode.FramesToVideo(framesFile, outPath, mode, fps, resampleFps, I.current.outItsScale, extraData);
await MuxOutputVideo(I.current.inPath, outPath);
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
await FfmpegEncode.FramesToVideo(framesFile, outPath, mode, fps, resampleFps, I.currentSettings.outItsScale, extraData);
await MuxOutputVideo(I.currentSettings.inPath, outPath);
await Loop(outPath, await GetLoopTimes());
}
}
@ -197,7 +197,7 @@ namespace Flowframes.Main
public static async Task ChunksToVideo(string tempFolder, string chunksFolder, string baseOutPath, bool isBackup = false)
{
if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.current.outMode)) < 1)
if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.currentSettings.outMode)) < 1)
{
I.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError);
return;
@ -252,7 +252,7 @@ namespace Flowframes.Main
await FfmpegCommands.ConcatVideos(framesFile, outPath, -1, !isBackup);
if(!isBackup || (isBackup && Config.GetInt(Config.Key.autoEncBackupMode) == 2)) // Mux if no backup, or if backup AND muxing is enabled for backups
await MuxOutputVideo(I.current.inPath, outPath, isBackup, !isBackup);
await MuxOutputVideo(I.currentSettings.inPath, outPath, isBackup, !isBackup);
if(!isBackup)
await Loop(outPath, await GetLoopTimes());
@ -260,8 +260,8 @@ namespace Flowframes.Main
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, I.OutMode mode, int firstFrameNum, int framesAmount)
{
string framesFileFull = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilename(I.current.interpFactor));
string concatFile = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount));
string framesFileFull = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
string concatFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount));
File.WriteAllLines(concatFile, IoUtils.ReadLines(framesFileFull).Skip(firstFrameNum).Take(framesAmount));
List<string> inputFrames = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(framesFileFull + ".inputframes.json")).Skip(firstFrameNum).Take(framesAmount).ToList();
@ -271,8 +271,8 @@ namespace Flowframes.Main
string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
bool fpsLimit = maxFps.GetFloat() != 0 && I.current.outFps.GetFloat() > maxFps.GetFloat();
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.current.inPath);
bool fpsLimit = maxFps.GetFloat() != 0 && I.currentSettings.outFps.GetFloat() > maxFps.GetFloat();
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
@ -283,7 +283,7 @@ namespace Flowframes.Main
if (!dontEncodeFullFpsVid)
{
string outFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(false, false));
string outFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, false));
int startNo = IoUtils.GetAmountOfFiles(outFolderPath, false) + 1;
if(chunkNo == 1) // Only check for existing folder on first chunk, otherwise each chunk makes a new folder
@ -292,27 +292,27 @@ namespace Flowframes.Main
if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format
await CopyOutputFrames(interpDir, concatFile, outFolderPath, startNo, fpsLimit, true);
else // Encode if frames are not in desired format
await FfmpegEncode.FramesToFrames(concatFile, outFolderPath, startNo, I.current.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
await FfmpegEncode.FramesToFrames(concatFile, outFolderPath, startNo, I.currentSettings.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
}
if (fpsLimit)
{
string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false));
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(true, false));
int startNumber = IoUtils.GetAmountOfFiles(outputFolderPath, false) + 1;
await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.current.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.currentSettings.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
}
}
else
{
if (!dontEncodeFullFpsVid)
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.current.outFps, new Fraction(), I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.currentSettings.outFps, new Fraction(), I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode
if (fpsLimit)
{
string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix;
outPath = Path.Combine(newParentDir, filename);
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.current.outFps, maxFps, I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.currentSettings.outFps, maxFps, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
}
}
@ -332,9 +332,9 @@ namespace Flowframes.Main
{
int times = -1;
int minLength = Config.GetInt(Config.Key.minOutVidLength);
int minFrameCount = (minLength * I.current.outFps.GetFloat()).RoundToInt();
int outFrames = ((await I.GetCurrentInputFrameCount()) * I.current.interpFactor).RoundToInt();
if (outFrames / I.current.outFps.GetFloat() < minLength)
int minFrameCount = (minLength * I.currentSettings.outFps.GetFloat()).RoundToInt();
int outFrames = (I.currentMediaFile.FrameCount * I.currentSettings.interpFactor).RoundToInt();
if (outFrames / I.currentSettings.outFps.GetFloat() < minLength)
times = (int)Math.Ceiling((double)minFrameCount / (double)outFrames);
times--; // Not counting the 1st play (0 loops)
if (times <= 0) return -1; // Never try to loop 0 times, idk what would happen, probably nothing
@ -355,7 +355,7 @@ namespace Flowframes.Main
if(showLog)
Program.mainForm.SetStatus("Muxing audio/subtitles into video...");
if (I.current.inputIsFrames)
if (I.currentSettings.inputIsFrames)
{
Logger.Log("Skipping muxing from input step as there is no input video, only frames.", true);
return;
@ -363,7 +363,7 @@ namespace Flowframes.Main
try
{
await FfmpegAudioAndMetadata.MergeStreamsFromInput(inputPath, outVideo, I.current.tempFolder, shortest);
await FfmpegAudioAndMetadata.MergeStreamsFromInput(inputPath, outVideo, I.currentSettings.tempFolder, shortest);
}
catch (Exception e)
{

View File

@ -55,12 +55,12 @@ namespace Flowframes.Main
bool loop = Config.GetBool(Config.Key.enableLoop);
bool sceneDetection = true;
string ext = Interpolate.current.interpExt;
string ext = Interpolate.currentSettings.interpExt;
frameFileContents.Clear();
lastOutFileCount = 0;
frameFiles = new DirectoryInfo(framesPath).GetFiles("*" + Interpolate.current.framesExt);
frameFiles = new DirectoryInfo(framesPath).GetFiles("*" + Interpolate.currentSettings.framesExt);
frameFilesWithoutLast = frameFiles;
Array.Resize(ref frameFilesWithoutLast, frameFilesWithoutLast.Length - 1);
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(interpFactor));
@ -168,7 +168,7 @@ namespace Flowframes.Main
{
int totalFileCount = 0;
bool blendSceneChances = Config.GetInt(Config.Key.sceneChangeFillMode) > 0;
string ext = Interpolate.current.interpExt;
string ext = Interpolate.currentSettings.interpExt;
Fraction step = new Fraction(sourceFrameCount, targetFrameCount + InterpolateUtils.GetRoundedInterpFramesPerInputFrame(factor));
List<FrameFileLine> lines = new List<FrameFileLine>();
@ -235,7 +235,7 @@ namespace Flowframes.Main
{
int totalFileCount = (startIndex) * factor;
int interpFramesAmount = factor;
string ext = Interpolate.current.interpExt;
string ext = Interpolate.currentSettings.interpExt;
string fileContent = "";

View File

@ -24,9 +24,9 @@ namespace Flowframes
{
public enum OutMode { VidMp4, VidMkv, VidWebm, VidProRes, VidAvi, VidGif, ImgPng }
public static int currentInputFrameCount;
public static bool currentlyUsingAutoEnc;
public static InterpSettings current;
public static InterpSettings currentSettings;
public static MediaFile currentMediaFile;
public static bool canceled = false;
static Stopwatch sw = new Stopwatch();
@ -36,18 +36,17 @@ namespace Flowframes
canceled = false;
Program.initialRun = false;
Program.mainForm.SetWorking(true);
if (!Utils.InputIsValid(current.inPath, current.outPath, current.inFps, current.interpFactor, current.outMode, current.tempFolder)) return; // General input checks
if (!Utils.CheckAiAvailable(current.ai, current.model)) return; // Check if selected AI pkg is installed
if (!Utils.InputIsValid(currentSettings)) return; // General input checks
if (!Utils.CheckPathValid(currentSettings.inPath)) return; // Check if input path/file is valid
if (!Utils.CheckAiAvailable(currentSettings.ai, currentSettings.model)) return; // Check if selected AI pkg is installed
if (!AutoEncodeResume.resumeNextRun && !Utils.CheckDeleteOldTempFolder()) return; // Try to delete temp folder if an old one exists
if (!Utils.CheckPathValid(current.inPath)) return; // Check if input path/file is valid
if (!(await Utils.CheckEncoderValid(current.outFps.GetFloat()))) return; // Check encoder compat
Utils.ShowWarnings(current.interpFactor, current.ai);
currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(current.inPath);
current.stepByStep = false;
if (!(await Utils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return; // Check encoder compat
Utils.ShowWarnings(currentSettings.interpFactor, currentSettings.ai);
currentSettings.stepByStep = false;
Program.mainForm.SetStatus("Starting...");
sw.Restart();
if (!AutoEncodeResume.resumeNextRun && !(current.ai.Piped && !current.inputIsFrames && Config.GetInt(Config.Key.dedupMode) == 0))
if (!AutoEncodeResume.resumeNextRun && !(currentSettings.ai.Piped && !currentSettings.inputIsFrames && Config.GetInt(Config.Key.dedupMode) == 0))
{
await GetFrames();
if (canceled) return;
@ -57,19 +56,19 @@ namespace Flowframes
if (canceled) return;
bool skip = await AutoEncodeResume.PrepareResumedRun();
if (skip || canceled) return;
await RunAi(current.interpFolder, current.ai);
await RunAi(currentSettings.interpFolder, currentSettings.ai);
if (canceled) return;
Program.mainForm.SetProgress(100);
if (!currentlyUsingAutoEnc)
{
if (current.ai.Piped)
await Export.MuxPipedVideo(current.inPath, current.FullOutPath);
if (currentSettings.ai.Piped)
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath);
else
await Export.ExportFrames(current.interpFolder, current.outPath, current.outMode, false);
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outMode, false);
}
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(current.framesFolder, false) > 0)
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0)
await Task.Run(async () => { await FrameRename.Unrename(); });
await Done();
@ -90,48 +89,40 @@ namespace Flowframes
public static async Task Realtime ()
{
await AiProcess.RunRifeNcnnVs(current.framesFolder, "", current.interpFactor, current.model.dir, true);
}
public static async Task<int> GetCurrentInputFrameCount()
{
if (currentInputFrameCount < 2)
currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(current.inPath);
return currentInputFrameCount;
await AiProcess.RunRifeNcnnVs(currentSettings.framesFolder, "", currentSettings.interpFactor, currentSettings.model.dir, true);
}
public static async Task GetFrames()
{
current.RefreshAlpha();
current.RefreshExtensions(InterpSettings.FrameType.Import);
currentSettings.RefreshAlpha();
currentSettings.RefreshExtensions(InterpSettings.FrameType.Import);
if (Config.GetBool(Config.Key.scnDetect))
{
Program.mainForm.SetStatus("Extracting scenes from video...");
await FfmpegExtract.ExtractSceneChanges(current.inPath, Path.Combine(current.tempFolder, Paths.scenesDir), current.inFpsDetected, current.inputIsFrames, current.framesExt);
await FfmpegExtract.ExtractSceneChanges(currentSettings.inPath, Path.Combine(currentSettings.tempFolder, Paths.scenesDir), currentSettings.inFpsDetected, currentSettings.inputIsFrames, currentSettings.framesExt);
}
if (!current.inputIsFrames) // Extract if input is video, import if image sequence
await ExtractFrames(current.inPath, current.framesFolder, current.alpha);
if (!currentSettings.inputIsFrames) // Extract if input is video, import if image sequence
await ExtractFrames(currentSettings.inPath, currentSettings.framesFolder, currentSettings.alpha);
else
await FfmpegExtract.ImportImagesCheckCompat(current.inPath, current.framesFolder, current.alpha, current.ScaledResolution, true, current.framesExt);
await FfmpegExtract.ImportImagesCheckCompat(currentSettings.inPath, currentSettings.framesFolder, currentSettings.alpha, currentSettings.ScaledResolution, true, currentSettings.framesExt);
}
public static async Task ExtractFrames(string inPath, string outPath, bool alpha)
{
if (canceled) return;
Program.mainForm.SetStatus("Extracting frames from video...");
current.RefreshExtensions(InterpSettings.FrameType.Import);
currentSettings.RefreshExtensions(InterpSettings.FrameType.Import);
bool mpdecimate = Config.GetInt(Config.Key.dedupMode) == 2;
Size res = await Utils.GetOutputResolution(inPath, true, true);
await FfmpegExtract.VideoToFrames(inPath, outPath, alpha, current.inFpsDetected, mpdecimate, false, res, current.framesExt);
await FfmpegExtract.VideoToFrames(inPath, outPath, alpha, currentSettings.inFpsDetected, mpdecimate, false, res, currentSettings.framesExt);
if (mpdecimate)
{
int framesLeft = IoUtils.GetAmountOfFiles(outPath, false, "*" + current.framesExt);
int framesDeleted = currentInputFrameCount - framesLeft;
float percentDeleted = ((float)framesDeleted / currentInputFrameCount) * 100f;
int framesLeft = IoUtils.GetAmountOfFiles(outPath, false, "*" + currentSettings.framesExt);
int framesDeleted = currentMediaFile.FrameCount - framesLeft;
float percentDeleted = ((float)framesDeleted / currentMediaFile.FrameCount) * 100f;
string keptPercent = $"{(100f - percentDeleted).ToString("0.0")}%";
if (QuickSettingsTab.trimEnabled)
@ -141,7 +132,7 @@ namespace Flowframes
}
if (!Config.GetBool("allowConsecutiveSceneChanges", true))
Utils.FixConsecutiveSceneFrames(Path.Combine(current.tempFolder, Paths.scenesDir), current.framesFolder);
Utils.FixConsecutiveSceneFrames(Path.Combine(currentSettings.tempFolder, Paths.scenesDir), currentSettings.framesFolder);
}
public static async Task PostProcessFrames(bool stepByStep)
@ -150,31 +141,31 @@ namespace Flowframes
Program.mainForm.SetStatus("Processing frames...");
int extractedFrames = IoUtils.GetAmountOfFiles(current.framesFolder, false, "*" + current.framesExt);
int extractedFrames = IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false, "*" + currentSettings.framesExt);
if (!Directory.Exists(current.framesFolder) || currentInputFrameCount <= 0 || extractedFrames < 2)
if (!Directory.Exists(currentSettings.framesFolder) || currentMediaFile.FrameCount <= 0 || extractedFrames < 2)
{
if (extractedFrames == 1)
Cancel("Only a single frame was extracted from your input file!\n\nPossibly your input is an image, not a video?");
else
Cancel($"Frame extraction failed!\nExtracted {extractedFrames} frames - current.framesFolder exists: {Directory.Exists(current.framesFolder)} - currentInputFrameCount = {currentInputFrameCount} - extractedFrames = {extractedFrames}.\n\nYour input file might be incompatible.");
Cancel($"Frame extraction failed!\nExtracted {extractedFrames} frames - current.framesFolder exists: {Directory.Exists(currentSettings.framesFolder)} - currentInputFrameCount = {currentMediaFile.FrameCount} - extractedFrames = {extractedFrames}.\n\nYour input file might be incompatible.");
}
if (Config.GetInt(Config.Key.dedupMode) == 1)
await Dedupe.Run(current.framesFolder);
await Dedupe.Run(currentSettings.framesFolder);
else
Dedupe.ClearCache();
if (!Config.GetBool(Config.Key.enableLoop))
{
await Utils.CopyLastFrame(currentInputFrameCount);
await Utils.CopyLastFrame(currentMediaFile.FrameCount);
}
else
{
FileInfo[] frameFiles = IoUtils.GetFileInfosSorted(current.framesFolder);
FileInfo[] frameFiles = IoUtils.GetFileInfosSorted(currentSettings.framesFolder);
string ext = frameFiles.First().Extension;
int lastNum = frameFiles.Last().Name.GetInt() + 1;
string loopFrameTargetPath = Path.Combine(current.framesFolder, lastNum.ToString().PadLeft(Padding.inputFrames, '0') + ext);
string loopFrameTargetPath = Path.Combine(currentSettings.framesFolder, lastNum.ToString().PadLeft(Padding.inputFrames, '0') + ext);
File.Copy(frameFiles.First().FullName, loopFrameTargetPath, true);
Logger.Log($"Copied loop frame to {loopFrameTargetPath}.", true);
}
@ -186,42 +177,42 @@ namespace Flowframes
bool dedupe = Config.GetInt(Config.Key.dedupMode) != 0;
if (!ai.Piped || (ai.Piped && current.inputIsFrames) || (ai.Piped && dedupe))
if (!ai.Piped || (ai.Piped && currentSettings.inputIsFrames) || (ai.Piped && dedupe))
{
await Task.Run(async () => { await Dedupe.CreateDupesFile(current.framesFolder, currentInputFrameCount, current.framesExt); });
await Task.Run(async () => { await Dedupe.CreateDupesFile(currentSettings.framesFolder, currentMediaFile.FrameCount, currentSettings.framesExt); });
await Task.Run(async () => { await FrameRename.Rename(); });
}
if (!ai.Piped || (ai.Piped && dedupe))
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(current.framesFolder, Config.GetBool(Config.Key.enableLoop), current.interpFactor); });
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(currentSettings.framesFolder, Config.GetBool(Config.Key.enableLoop), currentSettings.interpFactor); });
Program.mainForm.SetStatus("Downloading models...");
await ModelDownloader.DownloadModelFiles(ai, current.model.dir);
await ModelDownloader.DownloadModelFiles(ai, currentSettings.model.dir);
if (canceled) return;
currentlyUsingAutoEnc = Utils.CanUseAutoEnc(stepByStep, current);
currentlyUsingAutoEnc = Utils.CanUseAutoEnc(stepByStep, currentSettings);
IoUtils.CreateDir(outpath);
List<Task> tasks = new List<Task>();
if (ai.AiName == Implementations.rifeCuda.AiName)
tasks.Add(AiProcess.RunRifeCuda(current.framesFolder, current.interpFactor, current.model.dir));
tasks.Add(AiProcess.RunRifeCuda(currentSettings.framesFolder, currentSettings.interpFactor, currentSettings.model.dir));
if (ai.AiName == Implementations.rifeNcnn.AiName)
tasks.Add(AiProcess.RunRifeNcnn(current.framesFolder, outpath, current.interpFactor, current.model.dir));
tasks.Add(AiProcess.RunRifeNcnn(currentSettings.framesFolder, outpath, currentSettings.interpFactor, currentSettings.model.dir));
if (ai.AiName == Implementations.rifeNcnnVs.AiName)
tasks.Add(AiProcess.RunRifeNcnnVs(current.framesFolder, outpath, current.interpFactor, current.model.dir));
tasks.Add(AiProcess.RunRifeNcnnVs(currentSettings.framesFolder, outpath, currentSettings.interpFactor, currentSettings.model.dir));
if (ai.AiName == Implementations.flavrCuda.AiName)
tasks.Add(AiProcess.RunFlavrCuda(current.framesFolder, current.interpFactor, current.model.dir));
tasks.Add(AiProcess.RunFlavrCuda(currentSettings.framesFolder, currentSettings.interpFactor, currentSettings.model.dir));
if (ai.AiName == Implementations.dainNcnn.AiName)
tasks.Add(AiProcess.RunDainNcnn(current.framesFolder, outpath, current.interpFactor, current.model.dir, Config.GetInt(Config.Key.dainNcnnTilesize, 512)));
tasks.Add(AiProcess.RunDainNcnn(currentSettings.framesFolder, outpath, currentSettings.interpFactor, currentSettings.model.dir, Config.GetInt(Config.Key.dainNcnnTilesize, 512)));
if (ai.AiName == Implementations.xvfiCuda.AiName)
tasks.Add(AiProcess.RunXvfiCuda(current.framesFolder, current.interpFactor, current.model.dir));
tasks.Add(AiProcess.RunXvfiCuda(currentSettings.framesFolder, currentSettings.interpFactor, currentSettings.model.dir));
if (currentlyUsingAutoEnc)
{
@ -235,7 +226,7 @@ namespace Flowframes
public static void Cancel(string reason = "", bool noMsgBox = false)
{
if (current == null)
if (currentSettings == null)
return;
canceled = true;
@ -244,18 +235,18 @@ namespace Flowframes
AiProcess.Kill();
AvProcess.Kill();
if (!current.stepByStep && !Config.GetBool(Config.Key.keepTempFolder))
if (!currentSettings.stepByStep && !Config.GetBool(Config.Key.keepTempFolder))
{
if (!BatchProcessing.busy && IoUtils.GetAmountOfFiles(Path.Combine(current.tempFolder, Paths.resumeDir), true) > 0)
if (!BatchProcessing.busy && IoUtils.GetAmountOfFiles(Path.Combine(currentSettings.tempFolder, Paths.resumeDir), true) > 0)
{
DialogResult dialogResult = MessageBox.Show($"Delete the temp folder (Yes) or keep it for resuming later (No)?", "Delete temporary files?", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(current.tempFolder); });
Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(currentSettings.tempFolder); });
}
else
{
Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(current.tempFolder); });
Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(currentSettings.tempFolder); });
}
}
@ -275,7 +266,7 @@ namespace Flowframes
Logger.Log("Deleting temporary files...");
try
{
await Task.Run(async () => { Directory.Delete(current.tempFolder, true); });
await Task.Run(async () => { Directory.Delete(currentSettings.tempFolder, true); });
}
catch (Exception e)
{

View File

@ -20,22 +20,22 @@ namespace Flowframes.Main
canceled = false;
Program.mainForm.SetWorking(true);
if(current == null)
if(currentSettings == null)
{
Logger.Log($"[SBS] Getting new current settings", true);
current = Program.mainForm.GetCurrentSettings();
currentSettings = Program.mainForm.GetCurrentSettings();
}
else
{
Logger.Log($"[SBS] Updating current settings", true);
current = Program.mainForm.UpdateCurrentSettings(current);
currentSettings = Program.mainForm.UpdateCurrentSettings(currentSettings);
}
current.RefreshAlpha();
current.stepByStep = true;
currentSettings.RefreshAlpha();
currentSettings.stepByStep = true;
if (!InterpolateUtils.InputIsValid(current.inPath, current.outPath, current.inFps, current.interpFactor, current.outMode, current.tempFolder)) return; // General input checks
if (!InterpolateUtils.CheckPathValid(current.inPath)) return; // Check if input path/file is valid
if (!InterpolateUtils.InputIsValid(currentSettings)) return; // General input checks
if (!InterpolateUtils.CheckPathValid(currentSettings.inPath)) return; // Check if input path/file is valid
if (step.Contains("Extract Frames"))
await ExtractFramesStep();
@ -56,25 +56,23 @@ namespace Flowframes.Main
public static async Task ExtractFramesStep()
{
if (!(await IoUtils.TryDeleteIfExistsAsync(current.framesFolder)))
if (!(await IoUtils.TryDeleteIfExistsAsync(currentSettings.framesFolder)))
{
UiUtils.ShowMessageBox("Failed to delete existing frames folder - Make sure no file is opened in another program!", UiUtils.MessageType.Error);
return;
}
currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(current.inPath);
await GetFrames();
await PostProcessFrames(true);
}
public static async Task InterpolateStep()
{
if (!InterpolateUtils.CheckAiAvailable(current.ai, current.model)) return;
if (!InterpolateUtils.CheckAiAvailable(currentSettings.ai, currentSettings.model)) return;
current.framesFolder = Path.Combine(current.tempFolder, Paths.framesDir);
currentSettings.framesFolder = Path.Combine(currentSettings.tempFolder, Paths.framesDir);
if (IoUtils.GetAmountOfFiles(current.framesFolder, false, "*") < 2)
if (IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false, "*") < 2)
{
if (Config.GetBool(Config.Key.sbsRunPreviousStepIfNeeded))
{
@ -82,33 +80,31 @@ namespace Flowframes.Main
await ExtractFramesStep();
}
if (IoUtils.GetAmountOfFiles(current.framesFolder, false, "*") < 2)
if (IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false, "*") < 2)
{
UiUtils.ShowMessageBox("There are no extracted frames that can be interpolated!\nDid you run the extraction step?", UiUtils.MessageType.Error);
return;
}
}
if (!(await IoUtils.TryDeleteIfExistsAsync(current.interpFolder)))
if (!(await IoUtils.TryDeleteIfExistsAsync(currentSettings.interpFolder)))
{
UiUtils.ShowMessageBox("Failed to delete existing frames folder - Make sure no file is opened in another program!", UiUtils.MessageType.Error);
return;
}
currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(current.inPath);
if (Config.GetBool(Config.Key.sbsAllowAutoEnc) && !(await InterpolateUtils.CheckEncoderValid(current.outFps.GetFloat()))) return;
if (Config.GetBool(Config.Key.sbsAllowAutoEnc) && !(await InterpolateUtils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return;
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
await RunAi(current.interpFolder, current.ai, true);
await RunAi(currentSettings.interpFolder, currentSettings.ai, true);
await Task.Run(async () => { await FrameRename.Unrename(); }); // Get timestamps back
Program.mainForm.SetProgress(0);
}
public static async Task CreateOutputVid()
{
if (IoUtils.GetAmountOfFiles(current.interpFolder, false) < 2)
if (IoUtils.GetAmountOfFiles(currentSettings.interpFolder, false) < 2)
{
if (Config.GetBool(Config.Key.sbsRunPreviousStepIfNeeded))
{
@ -116,16 +112,16 @@ namespace Flowframes.Main
await InterpolateStep();
}
if (IoUtils.GetAmountOfFiles(current.interpFolder, false) < 2)
if (IoUtils.GetAmountOfFiles(currentSettings.interpFolder, false) < 2)
{
Cancel($"There are no interpolated frames to encode!\n\nDid you delete the folder?");
return;
}
}
if (!(await InterpolateUtils.CheckEncoderValid(current.outFps.GetFloat()))) return;
if (!(await InterpolateUtils.CheckEncoderValid(currentSettings.outFps.GetFloat()))) return;
string[] outFrames = IoUtils.GetFilesSorted(current.interpFolder, current.interpExt);
string[] outFrames = IoUtils.GetFilesSorted(currentSettings.interpFolder, currentSettings.interpExt);
if (outFrames.Length > 0 && !IoUtils.CheckImageValid(outFrames[0]))
{
@ -134,7 +130,7 @@ namespace Flowframes.Main
return;
}
await Export.ExportFrames(current.interpFolder, current.outPath, current.outMode, true);
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outMode, true);
}
public static async Task Reset()

View File

@ -28,20 +28,20 @@ namespace Flowframes.Main
try
{
lastFrameNum--; // We have to do this as extracted frames start at 0, not 1
bool frameFolderInput = IoUtils.IsPathDirectory(I.current.inPath);
string targetPath = Path.Combine(I.current.framesFolder, lastFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + I.current.framesExt);
bool frameFolderInput = IoUtils.IsPathDirectory(I.currentSettings.inPath);
string targetPath = Path.Combine(I.currentSettings.framesFolder, lastFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + I.currentSettings.framesExt);
if (File.Exists(targetPath)) return;
Size res = IoUtils.GetImage(IoUtils.GetFilesSorted(I.current.framesFolder, false).First()).Size;
Size res = IoUtils.GetImage(IoUtils.GetFilesSorted(I.currentSettings.framesFolder, false).First()).Size;
if (frameFolderInput)
{
string lastFramePath = IoUtils.GetFilesSorted(I.current.inPath, false).Last();
string lastFramePath = IoUtils.GetFilesSorted(I.currentSettings.inPath, false).Last();
await FfmpegExtract.ExtractLastFrame(lastFramePath, targetPath, res);
}
else
{
await FfmpegExtract.ExtractLastFrame(I.current.inPath, targetPath, res);
await FfmpegExtract.ExtractLastFrame(I.currentSettings.inPath, targetPath, res);
}
}
catch (Exception e)
@ -95,44 +95,43 @@ namespace Flowframes.Main
return Path.Combine(basePath, Path.GetFileNameWithoutExtension(inPath).StripBadChars().Remove(" ").Trunc(30, false) + "-temp");
}
public static bool InputIsValid(string inDir, string outDir, Fraction fpsIn, float factor, I.OutMode outMode, string tempFolder)
public static bool InputIsValid(InterpSettings s)
{
try
{
bool passes = true;
bool isFile = !IoUtils.IsPathDirectory(inDir);
float fpsOut = fpsIn.GetFloat() * factor;
bool isFile = !IoUtils.IsPathDirectory(s.inPath);
if ((passes && isFile && !IoUtils.IsFileValid(inDir)) || (!isFile && !IoUtils.IsDirValid(inDir)))
if ((passes && isFile && !IoUtils.IsFileValid(s.inPath)) || (!isFile && !IoUtils.IsDirValid(s.inPath)))
{
UiUtils.ShowMessageBox("Input path is not valid!");
passes = false;
}
if (passes && !IoUtils.IsDirValid(outDir))
if (passes && !IoUtils.IsDirValid(s.outPath))
{
UiUtils.ShowMessageBox("Output path is not valid!");
passes = false;
}
if (passes && tempFolder.StartsWith(@"\\"))
if (passes && s.tempFolder.StartsWith(@"\\"))
{
UiUtils.ShowMessageBox("Flowframes does not support UNC/Network paths as a temp folder!\nPlease use a local path instead.");
passes = false;
}
if (passes && fpsOut < 1f || fpsOut > 1000f)
if (passes && s.outFps.GetFloat() < 1f || s.outFps.GetFloat() > 1000f)
{
string imgSeqNote = isFile ? "" : "\n\nWhen using an image sequence as input, you always have to specify the frame rate manually.";
UiUtils.ShowMessageBox($"Invalid output frame rate ({fpsOut}).\nMust be 1-1000.{imgSeqNote}");
UiUtils.ShowMessageBox($"Invalid output frame rate ({s.outFps.GetFloat()}).\nMust be 1-1000.{imgSeqNote}");
passes = false;
}
string fpsLimitValue = Config.Get(Config.Key.maxFps);
float fpsLimit = (fpsLimitValue.Contains("/") ? new Fraction(Config.Get(Config.Key.maxFps)).GetFloat() : fpsLimitValue.GetFloat());
if (outMode == I.OutMode.VidGif && fpsOut > 50 && !(fpsLimit > 0 && fpsLimit <= 50))
Logger.Log($"Warning: GIF will be encoded at 50 FPS instead of {fpsOut} as the format doesn't support frame rates that high.");
if (s.outMode == I.OutMode.VidGif && s.outFps.GetFloat() > 50 && !(fpsLimit > 0 && fpsLimit <= 50))
Logger.Log($"Warning: GIF will be encoded at 50 FPS instead of {s.outFps.GetFloat()} as the format doesn't support frame rates that high.");
if (!passes)
I.Cancel("Invalid settings detected.", true);
@ -162,7 +161,7 @@ namespace Flowframes.Main
return false;
}
if (I.current.ai.AiName.ToUpper().Contains("CUDA") && NvApi.gpuList.Count < 1)
if (I.currentSettings.ai.AiName.ToUpper().Contains("CUDA") && NvApi.gpuList.Count < 1)
{
UiUtils.ShowMessageBox("Warning: No Nvidia GPU was detected. CUDA might fall back to CPU!\n\nTry an NCNN implementation instead if you don't have an Nvidia GPU.", UiUtils.MessageType.Error);
@ -178,7 +177,7 @@ namespace Flowframes.Main
public static bool CheckDeleteOldTempFolder()
{
if (!IoUtils.TryDeleteIfExists(I.current.tempFolder))
if (!IoUtils.TryDeleteIfExists(I.currentSettings.tempFolder))
{
UiUtils.ShowMessageBox("Failed to remove an existing temp folder of this video!\nMake sure you didn't open any frames in an editor.", UiUtils.MessageType.Error);
I.Cancel();
@ -224,7 +223,7 @@ namespace Flowframes.Main
public static async Task<bool> CheckEncoderValid (float encodeFps)
{
string enc = FfmpegUtils.GetEnc(FfmpegUtils.GetCodec(I.current.outMode));
string enc = FfmpegUtils.GetEnc(FfmpegUtils.GetCodec(I.currentSettings.outMode));
float maxAv1Fps = 240;
@ -334,7 +333,12 @@ namespace Flowframes.Main
public static async Task<bool> UseUhd()
{
return (await GetOutputResolution(I.current.inPath, false)).Height >= Config.GetInt(Config.Key.uhdThresh);
return UseUhd(await GetOutputResolution(I.currentSettings.inPath, false));
}
public static bool UseUhd(Size outputRes)
{
return outputRes.Height >= Config.GetInt(Config.Key.uhdThresh);
}
public static void FixConsecutiveSceneFrames(string sceneFramesPath, string sourceFramesPath)
@ -361,7 +365,7 @@ namespace Flowframes.Main
}
foreach (string frame in sceneFramesToDelete)
IoUtils.TryDeleteIfExists(Path.Combine(sceneFramesPath, frame + I.current.framesExt));
IoUtils.TryDeleteIfExists(Path.Combine(sceneFramesPath, frame + I.currentSettings.framesExt));
}
public static int GetRoundedInterpFramesPerInputFrame(float factor, bool roundDown = true)

View File

@ -30,10 +30,10 @@ namespace Flowframes.Main
public static void Save ()
{
if (timeSinceLastSave.IsRunning && timeSinceLastSave.ElapsedMilliseconds < (timeBetweenSaves * 1000f).RoundToInt()) return;
int frames = (int)Math.Round((float)InterpolationProgress.interpolatedInputFramesCount / I.current.interpFactor) - safetyDelayFrames;
int frames = (int)Math.Round((float)InterpolationProgress.interpolatedInputFramesCount / I.currentSettings.interpFactor) - safetyDelayFrames;
if (frames < 1) return;
timeSinceLastSave.Restart();
Directory.CreateDirectory(Path.Combine(I.current.tempFolder, Paths.resumeDir));
Directory.CreateDirectory(Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir));
SaveState(frames);
SaveInterpSettings();
SaveFilenameMap();
@ -42,13 +42,13 @@ namespace Flowframes.Main
static void SaveState (int frames)
{
ResumeState state = new ResumeState(I.currentlyUsingAutoEnc, frames);
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, resumeFilename);
string filePath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, resumeFilename);
File.WriteAllText(filePath, state.ToString());
}
static async Task SaveFilenameMap ()
{
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename);
string filePath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, filenameMapFilename);
if (File.Exists(filePath) && IoUtils.GetFilesize(filePath) > 0)
return;
@ -68,8 +68,8 @@ namespace Flowframes.Main
static void SaveInterpSettings ()
{
string filepath = Path.Combine(I.current.tempFolder, Paths.resumeDir, interpSettingsFilename);
File.WriteAllText(filepath, I.current.Serialize());
string filepath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, interpSettingsFilename);
File.WriteAllText(filepath, I.currentSettings.Serialize());
}
// public static void LoadTempFolder (string tempFolderPath)
@ -106,7 +106,7 @@ namespace Flowframes.Main
static void LoadFilenameMap()
{
List<string> files = new List<string>();
string filePath = Path.Combine(I.current.tempFolder, Paths.resumeDir, filenameMapFilename);
string filePath = Path.Combine(I.currentSettings.tempFolder, Paths.resumeDir, filenameMapFilename);
string[] fileLines = File.ReadAllLines(filePath);
foreach (string line in fileLines)

View File

@ -106,6 +106,48 @@ namespace Flowframes
return $"-hide_banner -stats -loglevel {loglevel} -y";
}
public class FfprobeSettings
{
public string Args { get; set; } = "";
public LogMode LoggingMode { get; set; } = LogMode.Hidden;
public string LogLevel { get; set; } = "panic";
public bool SetBusy { get; set; } = false;
}
public static async Task<string> RunFfprobe(FfprobeSettings settings)
{
bool show = Config.GetInt(Config.Key.cmdDebugMode) > 0;
string processOutput = "";
Process ffprobe = OsUtils.NewProcess(!show);
NmkdStopwatch timeSinceLastOutput = new NmkdStopwatch();
ffprobe.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffprobe -v {settings.LogLevel} {settings.Args}";
if (settings.LoggingMode != LogMode.Hidden) Logger.Log("Running FFprobe...", false);
Logger.Log($"ffprobe -v {settings.LogLevel} {settings.Args}", true, false, "ffmpeg");
if (!show)
{
string[] ignore = new string[0];
ffprobe.OutputDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", settings.LoggingMode, false); timeSinceLastOutput.sw.Restart(); };
ffprobe.ErrorDataReceived += (sender, outLine) => { AvOutputHandler.LogOutput(outLine.Data, ref processOutput, "ffmpeg", settings.LoggingMode, false); timeSinceLastOutput.sw.Restart(); };
}
ffprobe.Start();
ffprobe.PriorityClass = ProcessPriorityClass.BelowNormal;
if (!show)
{
ffprobe.BeginOutputReadLine();
ffprobe.BeginErrorReadLine();
}
while (!ffprobe.HasExited) await Task.Delay(10);
while (timeSinceLastOutput.ElapsedMs < 200) await Task.Delay(50);
return processOutput;
}
public static async Task<string> RunFfprobe(string args, LogMode logMode = LogMode.Hidden, string loglevel = "quiet")
{
bool show = Config.GetInt(Config.Key.cmdDebugMode) > 0;

View File

@ -13,7 +13,7 @@ namespace Flowframes.Media
public static async Task MergeStreamsFromInput (string inputVideo, string interpVideo, string tempFolder, bool shortest)
{
if (!File.Exists(inputVideo) && !I.current.inputIsFrames)
if (!File.Exists(inputVideo) && !I.currentSettings.inputIsFrames)
{
Logger.Log("Warning: Input video file not found, can't copy audio/subtitle streams to output video!");
return;
@ -29,9 +29,9 @@ namespace Flowframes.Media
string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt);
bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.current.outMode, GetAudioCodecs(inputVideo));
bool slowmo = I.current.outItsScale != 0 && I.current.outItsScale != 1;
string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.current.outMode, I.current.outItsScale);
bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.currentSettings.outMode, GetAudioCodecs(inputVideo));
bool slowmo = I.currentSettings.outItsScale != 0 && I.currentSettings.outItsScale != 1;
string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.currentSettings.outMode, I.currentSettings.outItsScale);
if (!audioCompat && !slowmo)
Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg");
@ -46,7 +46,7 @@ namespace Flowframes.Media
if (!subs || (subs && !Utils.ContainerSupportsSubs(containerExt)))
subArgs = "-sn";
bool isMkv = I.current.outMode == I.OutMode.VidMkv;
bool isMkv = I.currentSettings.outMode == I.OutMode.VidMkv;
string mkvFix = isMkv ? "-max_interleave_delta 0" : ""; // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/
string metaArg = (isMkv && meta) ? "-map 1:t?" : ""; // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/
string shortestArg = shortest ? "-shortest" : "";

View File

@ -25,7 +25,7 @@ namespace Flowframes
public static int GetPadding ()
{
return (Interpolate.current.ai.AiName == Implementations.flavrCuda.AiName) ? 8 : 2; // FLAVR input needs to be divisible by 8
return (Interpolate.currentSettings.ai.AiName == Implementations.flavrCuda.AiName) ? 8 : 2; // FLAVR input needs to be divisible by 8
}
public static string GetPadFilter ()
@ -74,11 +74,13 @@ namespace Flowframes
DeleteSource(inputFile);
}
public static long GetDuration(string inputFile)
public static async Task<long> GetDurationMs(string inputFile)
{
Logger.Log($"GetDuration({inputFile}) - Reading Duration using ffprobe.", true, false, "ffmpeg");
string args = $" -v panic -select_streams v:0 -show_entries format=duration -of csv=s=x:p=0 -sexagesimal {inputFile.Wrap()}";
string output = GetFfprobeOutput(args);
string args = $"-select_streams v:0 -show_entries format=duration -of csv=s=x:p=0 -sexagesimal {inputFile.Wrap()}";
FfprobeSettings settings = new FfprobeSettings() { Args = args };
string output = await RunFfprobe(settings);
return FormatUtils.TimestampToMs(output);
}

View File

@ -20,7 +20,7 @@ namespace Flowframes.Media
IoUtils.RenameExistingFile(outPath);
Directory.CreateDirectory(outPath.GetParentDir());
string[] encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode), (Interpolate.current.ScaledResolution.IsEmpty ? Interpolate.current.InputResolution : Interpolate.current.ScaledResolution), Interpolate.current.outFps.GetFloat());
string[] encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode), (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);

View File

@ -26,7 +26,7 @@ namespace Flowframes.Media
if (inputIsFrames)
{
string concatFile = Path.Combine(Paths.GetDataPath(), "png-scndetect-concat-temp.ini");
FfmpegUtils.CreateConcatFile(inPath, concatFile, Filetypes.imagesInterpCompat);
FfmpegUtils.CreateConcatFile(inPath, concatFile, Filetypes.imagesInterpCompat.ToList());
inArg = $"-f concat -safe 0 -i {concatFile.Wrap()}";
}
@ -34,10 +34,10 @@ namespace Flowframes.Media
string rateArg = (rate.GetFloat() > 0) ? $"-r {rate}" : "";
string args = $"-vsync 0 {GetTrimArg(true)} {inArg} {GetImgArgs(format)} {rateArg} {scnDetect} -frame_pts 1 -s 256x144 {GetTrimArg(false)} \"{outDir}/%{Padding.inputFrames}d{format}\"";
LogMode logMode = await Interpolate.GetCurrentInputFrameCount() > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
LogMode logMode = Interpolate.currentMediaFile.FrameCount > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
await RunFfmpeg(args, logMode, inputIsFrames ? "panic" : "warning", true);
bool hiddenLog = await Interpolate.GetCurrentInputFrameCount() <= 50;
bool hiddenLog = Interpolate.currentMediaFile.FrameCount <= 50;
int amount = IoUtils.GetAmountOfFiles(outDir, false);
Logger.Log($"Detected {amount} scene {(amount == 1 ? "change" : "changes")}.".Replace(" 0 ", " no "), false, !hiddenLog);
}
@ -83,7 +83,7 @@ namespace Flowframes.Media
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)} -vsync 0 {rateArg} -frame_pts 1 {vf} {sizeStr} {GetTrimArg(false)} \"{framesDir}/%{Padding.inputFrames}d{format}\"";
LogMode logMode = await Interpolate.GetCurrentInputFrameCount() > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
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);
@ -202,7 +202,7 @@ namespace Flowframes.Media
return false;
}
Interpolate.current.framesExt = files.First().Extension;
Interpolate.currentSettings.framesExt = files.First().Extension;
Logger.Log($"Sequence compatible!", true);
return true;
}
@ -213,7 +213,7 @@ namespace Flowframes.Media
Logger.Log($"ImportImages() - Alpha: {alpha} - Size: {size} - Format: {format}", true, false, "ffmpeg");
IoUtils.CreateDir(outPath);
string concatFile = Path.Combine(Paths.GetDataPath(), "import-concat-temp.ini");
FfmpegUtils.CreateConcatFile(inPath, concatFile, Filetypes.imagesInterpCompat);
FfmpegUtils.CreateConcatFile(inPath, concatFile, Filetypes.imagesInterpCompat.ToList());
string inArg = $"-f concat -safe 0 -i {concatFile.Wrap()}";
string linksDir = Path.Combine(concatFile + Paths.symlinksSuffix);

View File

@ -1,17 +1,176 @@
using Flowframes.IO;
using Flowframes.Data;
using Flowframes.Data.Streams;
using Flowframes.IO;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Flowframes.Media.GetVideoInfo;
using Stream = Flowframes.Data.Streams.Stream;
namespace Flowframes.Media
{
class FfmpegUtils
{
public enum Codec { H264, H265, H264Nvenc, H265Nvenc, Av1, Vp9, ProRes, AviRaw, Gif }
private readonly static FfprobeMode showStreams = FfprobeMode.ShowStreams;
private readonly static FfprobeMode showFormat = FfprobeMode.ShowFormat;
public static async Task<int> GetStreamCount(string path)
{
Logger.Log($"GetStreamCount({path})", true);
string output = await GetFfmpegInfoAsync(path, "Stream #0:");
if (string.IsNullOrWhiteSpace(output.Trim()))
return 0;
return output.SplitIntoLines().Where(x => x.MatchesWildcard("*Stream #0:*: *: *")).Count();
}
public static async Task<List<Stream>> GetStreams(string path, bool progressBar, int streamCount, Fraction defaultFps, bool countFrames)
{
List<Stream> streamList = new List<Stream>();
try
{
string output = await GetFfmpegInfoAsync(path, "Stream #0:");
string[] streams = output.SplitIntoLines().Where(x => x.MatchesWildcard("*Stream #0:*: *: *")).ToArray();
foreach (string streamStr in streams)
{
try
{
int idx = streamStr.Split(':')[1].Split('[')[0].Split('(')[0].GetInt();
bool def = await GetFfprobeInfoAsync(path, showStreams, "DISPOSITION:default", idx) == "1";
if (progressBar)
Program.mainForm.SetProgress(FormatUtils.RatioInt(idx + 1, streamCount));
if (streamStr.Contains(": Video:"))
{
string lang = await GetFfprobeInfoAsync(path, showStreams, "TAG:language", idx);
string title = await GetFfprobeInfoAsync(path, showStreams, "TAG:title", idx);
string codec = await GetFfprobeInfoAsync(path, showStreams, "codec_name", idx);
string codecLong = await GetFfprobeInfoAsync(path, showStreams, "codec_long_name", idx);
string pixFmt = (await GetFfprobeInfoAsync(path, showStreams, "pix_fmt", idx)).ToUpper();
int kbits = (await GetFfprobeInfoAsync(path, showStreams, "bit_rate", idx)).GetInt() / 1024;
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));
Fraction fps = path.IsConcatFile() ? defaultFps : await IoUtils.GetVideoFramerate(path);
int frameCount = countFrames ? await GetFrameCountCached.GetFrameCountAsync(path) : 0;
VideoStream vStream = new VideoStream(lang, title, codec, codecLong, pixFmt, kbits, res, sar, dar, fps, frameCount);
vStream.Index = idx;
vStream.IsDefault = def;
Logger.Log($"Added video stream: {vStream}", true);
streamList.Add(vStream);
continue;
}
if (streamStr.Contains(": Audio:"))
{
string lang = await GetFfprobeInfoAsync(path, showStreams, "TAG:language", idx);
string title = await GetFfprobeInfoAsync(path, showStreams, "TAG:title", idx);
string codec = await GetFfprobeInfoAsync(path, showStreams, "codec_name", idx);
string profile = await GetFfprobeInfoAsync(path, showStreams, "profile", idx);
if (codec.ToLower() == "dts" && profile != "unknown") codec = profile;
string codecLong = await GetFfprobeInfoAsync(path, showStreams, "codec_long_name", idx);
int kbits = (await GetFfprobeInfoAsync(path, showStreams, "bit_rate", idx)).GetInt() / 1024;
int sampleRate = (await GetFfprobeInfoAsync(path, showStreams, "sample_rate", idx)).GetInt();
int channels = (await GetFfprobeInfoAsync(path, showStreams, "channels", idx)).GetInt();
string layout = (await GetFfprobeInfoAsync(path, showStreams, "channel_layout", idx));
AudioStream aStream = new AudioStream(lang, title, codec, codecLong, kbits, sampleRate, channels, layout);
aStream.Index = idx;
aStream.IsDefault = def;
Logger.Log($"Added audio stream: {aStream}", true);
streamList.Add(aStream);
continue;
}
if (streamStr.Contains(": Subtitle:"))
{
string lang = await GetFfprobeInfoAsync(path, showStreams, "TAG:language", idx);
string title = await GetFfprobeInfoAsync(path, showStreams, "TAG:title", idx);
string codec = await GetFfprobeInfoAsync(path, showStreams, "codec_name", idx);
string codecLong = await GetFfprobeInfoAsync(path, showStreams, "codec_long_name", idx);
bool bitmap = await IsSubtitleBitmapBased(path, idx, codec);
SubtitleStream sStream = new SubtitleStream(lang, title, codec, codecLong, bitmap);
sStream.Index = idx;
sStream.IsDefault = def;
Logger.Log($"Added subtitle stream: {sStream}", true);
streamList.Add(sStream);
continue;
}
if (streamStr.Contains(": Data:"))
{
string codec = await GetFfprobeInfoAsync(path, showStreams, "codec_name", idx);
string codecLong = await GetFfprobeInfoAsync(path, showStreams, "codec_long_name", idx);
DataStream dStream = new DataStream(codec, codecLong);
dStream.Index = idx;
dStream.IsDefault = def;
Logger.Log($"Added data stream: {dStream}", true);
streamList.Add(dStream);
continue;
}
if (streamStr.Contains(": Attachment:"))
{
string codec = await GetFfprobeInfoAsync(path, showStreams, "codec_name", idx);
string codecLong = await GetFfprobeInfoAsync(path, showStreams, "codec_long_name", idx);
string filename = await GetFfprobeInfoAsync(path, showStreams, "TAG:filename", idx);
string mimeType = await GetFfprobeInfoAsync(path, showStreams, "TAG:mimetype", idx);
AttachmentStream aStream = new AttachmentStream(codec, codecLong, filename, mimeType);
aStream.Index = idx;
aStream.IsDefault = def;
Logger.Log($"Added attachment stream: {aStream}", true);
streamList.Add(aStream);
continue;
}
Logger.Log($"Unknown stream (not vid/aud/sub/dat/att): {streamStr}", true);
Stream stream = new Stream { Codec = "Unknown", CodecLong = "Unknown", Index = idx, IsDefault = def, Type = Stream.StreamType.Unknown };
streamList.Add(stream);
}
catch (Exception e)
{
Logger.Log($"Error scanning stream: {e.Message}\n{e.StackTrace}");
}
}
}
catch (Exception e)
{
Logger.Log($"GetStreams Exception: {e.Message}\n{e.StackTrace}", true);
}
Logger.Log($"Video Streams: {string.Join(", ", streamList.Where(x => x.Type == Stream.StreamType.Video).Select(x => string.IsNullOrWhiteSpace(x.Title) ? "No Title" : x.Title))}", true);
Logger.Log($"Audio Streams: {string.Join(", ", streamList.Where(x => x.Type == Stream.StreamType.Audio).Select(x => string.IsNullOrWhiteSpace(x.Title) ? "No Title" : x.Title))}", true);
Logger.Log($"Subtitle Streams: {string.Join(", ", streamList.Where(x => x.Type == Stream.StreamType.Subtitle).Select(x => string.IsNullOrWhiteSpace(x.Title) ? "No Title" : x.Title))}", true);
if (progressBar)
Program.mainForm.SetProgress(0);
return streamList;
}
public static async Task<bool> IsSubtitleBitmapBased(string path, int streamIndex, string codec = "")
{
if (codec == "ssa" || codec == "ass" || codec == "mov_text" || codec == "srt" || codec == "subrip" || codec == "text" || codec == "webvtt")
return false;
if (codec == "dvdsub" || codec == "dvd_subtitle" || codec == "pgssub" || codec == "hdmv_pgs_subtitle" || codec.StartsWith("dvb_"))
return true;
// If codec was not listed above, manually check if it's compatible by trying to encode it:
//string ffmpegCheck = await GetFfmpegOutputAsync(path, $"-map 0:{streamIndex} -c:s srt -t 0 -f null -");
//return ffmpegCheck.Contains($"encoding currently only possible from text to text or bitmap to bitmap");
return false;
}
public enum Codec { H264, H265, H264Nvenc, H265Nvenc, Av1, Vp9, ProRes, AviRaw, Gif }
public static Codec GetCodec(Interpolate.OutMode mode)
{
@ -330,20 +489,42 @@ namespace Flowframes.Media
return supported;
}
public static void CreateConcatFile(string inputFilesDir, string outputPath, string[] validExtensions = null)
public static int CreateConcatFile(string inputFilesDir, string outputPath, List<string> validExtensions = null)
{
if (IoUtils.GetAmountOfFiles(inputFilesDir, false) < 1)
return 0;
Directory.CreateDirectory(outputPath.GetParentDir());
if (validExtensions == null)
validExtensions = new List<string>();
validExtensions = validExtensions.Select(x => x.Remove(".").ToLower()).ToList(); // Ignore "." in extensions
string concatFileContent = "";
string[] files = IoUtils.GetFilesSorted(inputFilesDir);
int fileCount = 0;
foreach (string file in files)
foreach (string file in files.Where(x => validExtensions.Contains(Path.GetExtension(x).Replace(".", "").ToLower())))
{
if (validExtensions != null && !validExtensions.Contains(Path.GetExtension(file).ToLower()))
continue;
fileCount++;
concatFileContent += $"file '{file.Replace(@"\", "/")}'\n";
}
File.WriteAllText(outputPath, concatFileContent);
return fileCount;
}
public static Size SizeFromString(string str, char delimiter = ':')
{
try
{
string[] nums = str.Remove(" ").Trim().Split(delimiter);
return new Size(nums[0].GetInt(), nums[1].GetInt());
}
catch
{
return new Size();
}
}
}
}

View File

@ -17,21 +17,22 @@ namespace Flowframes.Media
static Dictionary<QueryInfo, string> cmdCache = new Dictionary<QueryInfo, string>();
private static string GetAvPath()
public static async Task<string> GetFfmpegInfoAsync(string path, string lineFilter = "", bool noCache = false)
{
return Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
return await GetFfmpegOutputAsync(path, "", lineFilter, noCache);
}
public static async Task<string> GetFfmpegInfoAsync(string path, string lineFilter = "")
public static async Task<string> GetFfmpegOutputAsync(string path, string args, string lineFilter = "", bool noCache = false)
{
return await GetFfmpegOutputAsync(path, "", lineFilter);
return await GetFfmpegOutputAsync(path, "", args, lineFilter, noCache);
}
public static async Task<string> GetFfmpegOutputAsync(string path, string args, string lineFilter = "")
public static async Task<string> GetFfmpegOutputAsync(string path, string argsIn, string argsOut, string lineFilter = "", bool noCache = false)
{
Process process = OsUtils.NewProcess(true);
process.StartInfo.Arguments = $"/C cd /D {GetAvPath().Wrap()} & ffmpeg.exe -hide_banner -y -stats -i {path.Wrap()} {args}";
return await GetInfoAsync(path, process, lineFilter, false);
process.StartInfo.Arguments = $"/C cd /D {GetAvPath().Wrap()} & " +
$"ffmpeg.exe -hide_banner -y {argsIn} {path.GetConcStr()} -i {path.Wrap()} {argsOut}";
return await GetInfoAsync(path, process, lineFilter, noCache);
}
public static async Task<string> GetFfprobeInfoAsync(string path, FfprobeMode mode, string lineFilter = "", int streamIndex = -1, bool stripKeyName = true)
@ -39,39 +40,64 @@ namespace Flowframes.Media
Process process = OsUtils.NewProcess(true);
string showFormat = mode == FfprobeMode.ShowBoth || mode == FfprobeMode.ShowFormat ? "-show_format" : "";
string showStreams = mode == FfprobeMode.ShowBoth || mode == FfprobeMode.ShowStreams ? "-show_streams" : "";
string streamSelect = (streamIndex >= 0) ? $"-select_streams {streamIndex}" : "";
process.StartInfo.Arguments = $"/C cd /D {GetAvPath().Wrap()} & ffprobe -v quiet {showFormat} {showStreams} {streamSelect} {path.Wrap()}";
return await GetInfoAsync(path, process, lineFilter, stripKeyName);
process.StartInfo.Arguments = $"/C cd /D {GetAvPath().Wrap()} & " +
$"ffprobe -v quiet {path.GetConcStr()} {showFormat} {showStreams} {path.Wrap()}";
string output = await GetInfoAsync(path, process, lineFilter, streamIndex, stripKeyName);
return output;
}
static async Task<string> GetInfoAsync(string path, Process process, string lineFilter, bool stripKeyName = true)
static async Task<string> GetInfoAsync(string path, Process process, string lineFilter, bool noCache = false) // for ffmpeg
{
string output = await GetOutputCached(path, process);
string output = await GetOutputCached(path, process, noCache);
if (!string.IsNullOrWhiteSpace(lineFilter.Trim()))
output = string.Join("\n", output.SplitIntoLines().Where(x => x.Contains(lineFilter)).ToArray());
return output;
}
static async Task<string> GetInfoAsync(string path, Process process, string lineFilter, int streamIndex = -1, bool stripKeyName = true, bool noCache = false) // for ffprobe
{
string output = await GetOutputCached(path, process, noCache);
try
{
if (streamIndex >= 0)
output = output.Split("[/STREAM]")[streamIndex];
}
catch
{
Logger.Log($"output.Split(\"[/STREAM]\")[{streamIndex}] failed! Can't access index {streamIndex} because array only has {output.Split("[/STREAM]").Length} items.", true);
return "";
}
if (!string.IsNullOrWhiteSpace(lineFilter.Trim()))
{
if (stripKeyName)
{
List<string> filtered = output.SplitIntoLines().Where(x => x.Contains(lineFilter)).ToList(); // Filter
List<string> filtered = output.SplitIntoLines().Where(x => x.ToLower().Contains(lineFilter.ToLower())).ToList(); // Filter
filtered = filtered.Select(x => string.Join("", x.Split('=').Skip(1))).ToList(); // Ignore everything before (and including) the first '=' sign
output = string.Join("\n", filtered);
}
else
{
output = string.Join("\n", output.SplitIntoLines().Where(x => x.Contains(lineFilter)).ToArray());
output = string.Join("\n", output.SplitIntoLines().Where(x => x.ToLower().Contains(lineFilter.ToLower())).ToArray());
}
}
return output;
}
static async Task<string> GetOutputCached(string path, Process process)
static async Task<string> GetOutputCached(string path, Process process, bool noCache = false)
{
long filesize = IoUtils.GetFilesize(path);
QueryInfo hash = new QueryInfo(path, filesize, process.StartInfo.Arguments);
if (filesize > 0 && CacheContains(hash, ref cmdCache))
if (!noCache && filesize > 0 && CacheContains(hash, ref cmdCache))
{
Logger.Log($"GetVideoInfo: '{process.StartInfo.FileName} {process.StartInfo.Arguments}' cached, won't re-run.", true, false, "ffmpeg");
return GetFromCache(hash, ref cmdCache);
@ -100,5 +126,15 @@ namespace Flowframes.Media
return "";
}
public static void ClearCache()
{
cmdCache.Clear();
}
private static string GetAvPath()
{
return Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
}
}
}

View File

@ -17,8 +17,8 @@ namespace Flowframes.MiscUtils
public static async Task Rename()
{
importFilenames = IoUtils.GetFilesSorted(Interpolate.current.framesFolder).Select(x => Path.GetFileName(x)).ToArray();
await IoUtils.RenameCounterDir(Interpolate.current.framesFolder, 0, Padding.inputFramesRenamed);
importFilenames = IoUtils.GetFilesSorted(Interpolate.currentSettings.framesFolder).Select(x => Path.GetFileName(x)).ToArray();
await IoUtils.RenameCounterDir(Interpolate.currentSettings.framesFolder, 0, Padding.inputFramesRenamed);
framesAreRenamed = true;
}
@ -27,11 +27,11 @@ namespace Flowframes.MiscUtils
Stopwatch sw = new Stopwatch();
sw.Restart();
string[] files = IoUtils.GetFilesSorted(Interpolate.current.framesFolder);
string[] files = IoUtils.GetFilesSorted(Interpolate.currentSettings.framesFolder);
for (int i = 0; i < files.Length; i++)
{
string movePath = Path.Combine(Interpolate.current.framesFolder, importFilenames[i]);
string movePath = Path.Combine(Interpolate.currentSettings.framesFolder, importFilenames[i]);
File.Move(files[i], movePath);
if (sw.ElapsedMilliseconds > 100)

View File

@ -48,7 +48,7 @@ namespace Flowframes.Os
processTime.Restart();
lastAiProcess = proc;
AiProcessSuspend.SetRunning(true);
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.current.framesFolder : inPath;
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.currentSettings.framesFolder : inPath;
hasShownError = false;
}
@ -56,7 +56,7 @@ namespace Flowframes.Os
{
lastAiProcess = proc;
AiProcessSuspend.SetRunning(true);
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.current.framesFolder : inPath;
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.currentSettings.framesFolder : inPath;
hasShownError = false;
}
@ -97,10 +97,10 @@ namespace Flowframes.Os
return;
}
int interpFramesFiles = IoUtils.GetAmountOfFiles(Interpolate.current.interpFolder, false, "*" + Interpolate.current.interpExt);
int interpFramesFiles = IoUtils.GetAmountOfFiles(Interpolate.currentSettings.interpFolder, false, "*" + Interpolate.currentSettings.interpExt);
int interpFramesCount = interpFramesFiles + InterpolationProgress.deletedFramesCount;
if (!Interpolate.current.ai.Piped)
if (!Interpolate.currentSettings.ai.Piped)
InterpolationProgress.UpdateInterpProgress(interpFramesCount, InterpolationProgress.targetFrames);
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}. Peak Output FPS: {InterpolationProgress.peakFpsOut.ToString("0.00")}";
@ -114,7 +114,7 @@ namespace Flowframes.Os
Logger.Log(logStr);
processTime.Stop();
if (!Interpolate.current.ai.Piped && interpFramesCount < 3)
if (!Interpolate.currentSettings.ai.Piped && interpFramesCount < 3)
{
string[] logLines = File.ReadAllLines(Path.Combine(Paths.GetLogPath(), lastLogName + ".txt"));
string log = string.Join("\n", logLines.Reverse().Take(10).Reverse().Select(x => x.Split("]: ").Last()).ToList());
@ -195,7 +195,7 @@ namespace Flowframes.Os
Process rifePy = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(rifePy, 3500);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
SetProgressCheck(Path.Combine(Interpolate.currentSettings.tempFolder, outDir), interpFactor);
rifePy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.PkgDir).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running RIFE (CUDA){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
@ -253,7 +253,7 @@ namespace Flowframes.Os
Process flavrPy = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(flavrPy, 4000);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
SetProgressCheck(Path.Combine(Interpolate.currentSettings.tempFolder, outDir), interpFactor);
flavrPy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.flavrCuda.PkgDir).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running FLAVR (CUDA)...", false);
@ -338,10 +338,10 @@ namespace Flowframes.Os
try
{
bool uhd = await InterpolateUtils.UseUhd();
Logger.Log($"Running RIFE (NCNN-VS){(uhd ? " (UHD Mode)" : "")}...", false);
Size scaledSize = await InterpolateUtils.GetOutputResolution(Interpolate.currentSettings.inPath, false);
Logger.Log($"Running RIFE (NCNN-VS){(InterpolateUtils.UseUhd(scaledSize) ? " (UHD Mode)" : "")}...", false);
await RunRifeNcnnVsProcess(framesPath, factor, outPath, mdl, uhd, rt);
await RunRifeNcnnVsProcess(framesPath, factor, outPath, mdl, scaledSize, rt);
}
catch (Exception e)
{
@ -352,10 +352,9 @@ namespace Flowframes.Os
await AiFinished("RIFE", true);
}
static async Task RunRifeNcnnVsProcess(string inPath, float factor, string outPath, string mdl, bool uhd, bool rt = false)
static async Task RunRifeNcnnVsProcess(string inPath, float factor, string outPath, string mdl, Size res, bool rt = false)
{
IoUtils.CreateDir(outPath);
string logFileName = "rife-ncnn-vs-log";
Process rifeNcnnVs = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
Logger.Log($"Note: RIFE-NCNN-VS is experimental and may not work as expected with specific Flowframes features, such as FPS limiting and image sequence exporting.");
@ -367,26 +366,26 @@ namespace Flowframes.Os
}
else
{
SetProgressCheck(Interpolate.currentInputFrameCount, factor, logFileName);
SetProgressCheck(Interpolate.currentMediaFile.FrameCount, factor, Implementations.rifeNcnnVs.LogFilename);
AiStarted(rifeNcnnVs, 1500, inPath);
}
string avDir = Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
string rtArgs = $"-window_title \"Flowframes Realtime Interpolation ({Interpolate.current.inFps.GetString()} FPS x{factor} = {Interpolate.current.outFps.GetString()} FPS - {mdl})\" -autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} ";
string rtArgs = $"-window_title \"Flowframes Realtime Interpolation ({Interpolate.currentSettings.inFps.GetString()} FPS x{factor} = {Interpolate.currentSettings.outFps.GetString()} FPS - {mdl})\" -autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} ";
Interpolate.current.FullOutPath = Path.Combine(Interpolate.current.outPath, await IoUtils.GetCurrentExportFilename(false, true));
string encArgs = FfmpegUtils.GetEncArgs(FfmpegUtils.GetCodec(Interpolate.current.outMode), (Interpolate.current.ScaledResolution.IsEmpty ? Interpolate.current.InputResolution : Interpolate.current.ScaledResolution), Interpolate.current.outFps.GetFloat(), true).FirstOrDefault();
string ffmpegArgs = rt ? $"{Path.Combine(avDir, "ffplay").Wrap()} {rtArgs} - " : $"{Path.Combine(avDir, "ffmpeg").Wrap()} -y -i pipe: {encArgs} {Interpolate.current.FullOutPath.Wrap()}";
Interpolate.currentSettings.FullOutPath = Path.Combine(Interpolate.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, true));
string encArgs = FfmpegUtils.GetEncArgs(FfmpegUtils.GetCodec(Interpolate.currentSettings.outMode), (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat(), true).FirstOrDefault();
string ffmpegArgs = rt ? $"{Path.Combine(avDir, "ffplay").Wrap()} {rtArgs} - " : $"{Path.Combine(avDir, "ffmpeg").Wrap()} -y -i pipe: {encArgs} {Interpolate.currentSettings.FullOutPath.Wrap()}";
string pkgDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
VapourSynthUtils.VsSettings vsSettings = new VapourSynthUtils.VsSettings()
{
InterpSettings = Interpolate.current,
InterpSettings = Interpolate.currentSettings,
ModelDir = mdl,
Factor = factor,
Res = InterpolateUtils.GetOutputResolution(Interpolate.current.InputResolution, true, true),
Uhd = uhd,
Res = InterpolateUtils.GetOutputResolution(Interpolate.currentSettings.InputResolution, true, true),
Uhd = InterpolateUtils.UseUhd(res),
GpuId = Config.Get(Config.Key.ncnnGpus).Split(',')[0].GetInt(),
SceneDetectSensitivity = Config.GetBool(Config.Key.scnDetect) ? Config.GetFloat(Config.Key.scnDetectValue) * 0.7f : 0f,
Loop = Config.GetBool(Config.Key.enableLoop),
@ -508,7 +507,7 @@ namespace Flowframes.Os
Process xvfiPy = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(xvfiPy, 3500);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
SetProgressCheck(Path.Combine(Interpolate.currentSettings.tempFolder, outDir), interpFactor);
xvfiPy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {pkgPath.Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running XVFI (CUDA)...", false);
@ -539,9 +538,8 @@ namespace Flowframes.Os
Stopwatch sw = new Stopwatch();
sw.Restart();
string logFilename = ai.AiName.Replace("_", "-").ToLower() + "-log";
lastLogName = logFilename;
Logger.Log(line, true, false, logFilename);
lastLogName = ai.LogFilename;
Logger.Log(line, true, false, ai.LogFilename);
string lastLogLines = string.Join("\n", Logger.GetSessionLogLastLines(lastLogName, 6).Select(x => $"[{x.Split("]: [").Skip(1).FirstOrDefault()}"));
@ -553,7 +551,7 @@ namespace Flowframes.Os
if (!hasShownError && err && line.ToLower().Contains("modulenotfounderror"))
{
hasShownError = true;
UiUtils.ShowMessageBox($"A python module is missing.\nCheck {logFilename} for details.\n\n{line}", UiUtils.MessageType.Error);
UiUtils.ShowMessageBox($"A python module is missing.\nCheck {ai.LogFilename} for details.\n\n{line}", UiUtils.MessageType.Error);
}
if (!hasShownError && line.ToLower().Contains("no longer supports this gpu"))
@ -565,7 +563,7 @@ namespace Flowframes.Os
if (!hasShownError && line.ToLower().Contains("error(s) in loading state_dict"))
{
hasShownError = true;
string msg = (Interpolate.current.ai.AiName == Implementations.flavrCuda.AiName) ? "\n\nFor FLAVR, you need to select the correct model for each scale!" : "";
string msg = (Interpolate.currentSettings.ai.AiName == Implementations.flavrCuda.AiName) ? "\n\nFor FLAVR, you need to select the correct model for each scale!" : "";
UiUtils.ShowMessageBox($"Error loading the AI model!\n\n{line}{msg}", UiUtils.MessageType.Error);
}
@ -593,7 +591,7 @@ namespace Flowframes.Os
if (!hasShownError && err && line.Contains("vkAllocateMemory failed"))
{
hasShownError = true;
bool usingDain = (Interpolate.current.ai.AiName == Implementations.dainNcnn.AiName);
bool usingDain = (Interpolate.currentSettings.ai.AiName == Implementations.dainNcnn.AiName);
string msg = usingDain ? "\n\nTry reducing the tile size in the AI settings." : "\n\nTry a lower resolution (Settings -> Max Video Size).";
UiUtils.ShowMessageBox($"Vulkan ran out of memory!\n\n{line}{msg}", UiUtils.MessageType.Error);
}
@ -652,7 +650,7 @@ namespace Flowframes.Os
static string GetNcnnPattern()
{
return $"%0{Padding.interpFrames}d{Interpolate.current.interpExt}";
return $"%0{Padding.interpFrames}d{Interpolate.currentSettings.interpExt}";
}
static string GetNcnnTilesize(int tilesize)

View File

@ -38,7 +38,7 @@ namespace Flowframes.Os
string mdlPath = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir, s.ModelDir).Replace(@"\", "/").Wrap();
bool sc = s.SceneDetectSensitivity >= 0.01f;
long frameCount = (long)Interpolate.currentInputFrameCount;
long frameCount = (long)Interpolate.currentMediaFile.FrameCount;
bool trim = QuickSettingsTab.trimEnabled;
long srcTrimStartFrame = trim ? (long)(Math.Round(FormatUtils.TimestampToMs(QuickSettingsTab.trimStart) / 1000f * s.InterpSettings.inFps.GetFloat())) : 0;

View File

@ -94,11 +94,11 @@ namespace Flowframes
{
try
{
if (Interpolate.current == null || Interpolate.current.tempFolder.Length < 3)
if (Interpolate.currentSettings == null || Interpolate.currentSettings.tempFolder.Length < 3)
return;
string drivePath = Interpolate.current.tempFolder.Substring(0, 2);
long mb = IoUtils.GetDiskSpace(Interpolate.current.tempFolder);
string drivePath = Interpolate.currentSettings.tempFolder.Substring(0, 2);
long mb = IoUtils.GetDiskSpace(Interpolate.currentSettings.tempFolder);
Logger.Log($"Disk space check for '{drivePath}/': {(mb / 1024f).ToString("0.0")} GB free.", true);

View File

@ -53,7 +53,7 @@ namespace Flowframes.Ui
Program.mainForm.SetTab("preview");
firstProgUpd = false;
string lastFramePath = currentOutdir + "\\" + lastFrame.ToString("00000000") + I.current.interpExt;
string lastFramePath = currentOutdir + "\\" + lastFrame.ToString("00000000") + I.currentSettings.interpExt;
if (lastFrame > 1)
UpdateInterpProgress(lastFrame, targetFrames, lastFramePath);
@ -79,8 +79,8 @@ namespace Flowframes.Ui
{
try
{
string ncnnStr = I.current.ai.AiName.Contains("NCNN") ? " done" : "";
Regex frameRegex = new Regex($@"(?<=.)\d*(?={I.current.interpExt}{ncnnStr})");
string ncnnStr = I.currentSettings.ai.AiName.Contains("NCNN") ? " done" : "";
Regex frameRegex = new Regex($@"(?<=.)\d*(?={I.currentSettings.interpExt}{ncnnStr})");
if (!frameRegex.IsMatch(output)) return;
lastFrame = Math.Max(int.Parse(frameRegex.Match(output).Value), lastFrame);
}
@ -132,7 +132,7 @@ namespace Flowframes.Ui
public static void UpdateInterpProgress(int frames, int target, string latestFramePath = "")
{
if (I.canceled) return;
interpolatedInputFramesCount = ((frames / I.current.interpFactor).RoundToInt() - 1);
interpolatedInputFramesCount = ((frames / I.currentSettings.interpFactor).RoundToInt() - 1);
//ResumeUtils.Save();
frames = frames.Clamp(0, target);
int percent = (int)Math.Round(((float)frames / target) * 100f);
@ -177,7 +177,7 @@ namespace Flowframes.Ui
public static async Task DeleteInterpolatedInputFrames()
{
interpolatedInputFramesCount = 0;
string[] inputFrames = IoUtils.GetFilesSorted(I.current.framesFolder);
string[] inputFrames = IoUtils.GetFilesSorted(I.currentSettings.framesFolder);
for (int i = 0; i < inputFrames.Length; i++)
{

View File

@ -38,9 +38,10 @@ namespace Flowframes.Ui
Logger.Log("Your file seems to be on an HDD or USB device. It is recommended to interpolate videos on an SSD drive for best performance.");
Logger.Log("Importing file...");
Program.mainForm.currInDuration = FfmpegCommands.GetDuration(path);
Interpolate.currentMediaFile = new MediaFile(path);
await Interpolate.currentMediaFile.Initialize();
Program.mainForm.currInDuration = Interpolate.currentMediaFile.DurationMs;
Program.mainForm.currInDurationCut = Program.mainForm.currInDuration;
int frameCount = await GetFrameCountCached.GetFrameCountAsync(path);
string fpsStr = "Not Found";
Fraction fps = (await IoUtils.GetFpsFolderOrVideo(path));
Program.mainForm.currInFpsDetected = fps;
@ -49,10 +50,10 @@ namespace Flowframes.Ui
if (fps.GetFloat() > 0)
fpsStr = $"{fps} (~{fps.GetFloat()})";
Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {frameCount}", false, true);
Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {Interpolate.currentMediaFile.FrameCount}", false, true);
Program.mainForm.GetInputFpsTextbox().ReadOnly = (fps.GetFloat() > 0 && !Config.GetBool("allowCustomInputRate", false));
Program.mainForm.currInFps = fps;
Program.mainForm.currInFrames = frameCount;
Program.mainForm.currInFrames = Interpolate.currentMediaFile.FrameCount;
Program.mainForm.UpdateInputInfo();
CheckExistingFolder(path, outputTbox.Text.Trim());
await Task.Delay(10);
@ -93,13 +94,13 @@ namespace Flowframes.Ui
static void CheckExistingFolder (string inpath, string outpath)
{
if (Interpolate.current == null || !Interpolate.current.stepByStep) return;
if (Interpolate.currentSettings == null || !Interpolate.currentSettings.stepByStep) return;
string tmpFolder = InterpolateUtils.GetTempFolderLoc(inpath, outpath);
if (Directory.Exists(tmpFolder))
{
int scnFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.scenesDir), false, "*" + Interpolate.current.interpExt); // TODO: Make this work if the frames extension was changed
int scnFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.scenesDir), false, "*" + Interpolate.currentSettings.interpExt); // TODO: Make this work if the frames extension was changed
string scnFrames = scnFrmAmount > 0 ? $"{scnFrmAmount} scene frames" : "no scene frames";
int srcFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.framesDir), false, "*" + Interpolate.current.interpExt);
int srcFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.framesDir), false, "*" + Interpolate.currentSettings.interpExt);
string srcFrames = srcFrmAmount > 1 ? $"{srcFrmAmount} source frames" : "no source frames";
int interpFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.interpDir), false);
string interpFrames = interpFrmAmount > 2 ? $"{interpFrmAmount} interpolated frames" : "no interpolated frames";
@ -118,8 +119,8 @@ namespace Flowframes.Ui
{
Size res = new Size();
if(path == Interpolate.current?.inPath)
res = Interpolate.current.InputResolution;
if(path == Interpolate.currentSettings?.inPath)
res = Interpolate.currentSettings.InputResolution;
else
res = await GetMediaResolutionCached.GetSizeAsync(path);

View File

@ -0,0 +1,425 @@
using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Utilities
{
class ColorDataUtils
{
// public static async Task<VideoColorData> GetColorData(string path)
// {
// VideoColorData data = new VideoColorData();
//
// AvProcess.FfprobeSettings settings = new AvProcess.FfprobeSettings() { Args = $"-show_frames -select_streams v:0 -read_intervals \"%+#1\" {path.Wrap()}", LogLevel = "quiet" };
// string infoFfprobe = await AvProcess.RunFfprobe(settings);
//
// string[] linesFfprobe = infoFfprobe.SplitIntoLines();
//
// foreach (string line in linesFfprobe)
// {
// if (line.Contains("color_transfer="))
// data.ColorTransfer = GetColorTransfer(line.Split('=').Last());
//
// else if (line.Contains("color_space="))
// data.ColorMatrixCoeffs = GetMatrixCoeffs(line.Split('=').Last());
//
// else if (line.Contains("color_primaries="))
// data.ColorPrimaries = GetColorPrimaries(line.Split('=').Last());
//
// else if (line.Contains("color_range="))
// data.ColorRange = GetColorRange(line.Split('=').Last());
//
// else if (line.Contains("red_x="))
// data.RedX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("red_y="))
// data.RedY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("green_x="))
// data.GreenX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("green_y="))
// data.GreenY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("blue_x="))
// data.BlueY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("blue_y="))
// data.BlueX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("white_point_x="))
// data.WhiteY = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("white_point_y="))
// data.WhiteX = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_luminance="))
// data.LumaMax = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("min_luminance="))
// data.LumaMin = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_content="))
// data.MaxCll = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
//
// else if (line.Contains("max_average="))
// data.MaxFall = line.Contains("/") ? FractionToFloat(line.Split('=').Last()) : line.Split('=').Last();
// }
//
// string infoMkvinfo = await AvProcess.RunMkvInfo($"{path.Wrap()}", OS.NmkoderProcess.ProcessType.Secondary);
//
// if (infoMkvinfo.Contains("+ Video track"))
// {
// string[] lines = infoMkvinfo.Split("+ Video track")[1].Split("+ Track")[0].Split("+ Tags")[0].SplitIntoLines();
//
// foreach (string line in lines)
// {
// if (line.Contains("+ Colour transfer:"))
// data.ColorTransfer = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour matrix coefficients:"))
// data.ColorMatrixCoeffs = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour primaries:"))
// data.ColorPrimaries = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Colour range:"))
// data.ColorRange = ValidateNumber(line.Split(':')[1]).GetInt();
//
// else if (line.Contains("+ Red colour coordinate x:"))
// data.RedX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Red colour coordinate y:"))
// data.RedY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Green colour coordinate x:"))
// data.GreenX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Green colour coordinate y:"))
// data.GreenY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Blue colour coordinate y:"))
// data.BlueY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Blue colour coordinate x:"))
// data.BlueX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ White colour coordinate y:"))
// data.WhiteY = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ White colour coordinate x:"))
// data.WhiteX = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum luminance:"))
// data.LumaMax = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Minimum luminance:"))
// data.LumaMin = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum content light:"))
// data.MaxCll = ValidateNumber(line.Split(':')[1]);
//
// else if (line.Contains("+ Maximum frame light:"))
// data.MaxFall = ValidateNumber(line.Split(':')[1]);
// }
// }
//
// return data;
// }
private static string FractionToFloat(string fracString)
{
string[] fracNums = fracString.Split('/');
return ((float)fracNums[0].GetInt() / (float)fracNums[1].GetInt()).ToString("0.#######", new CultureInfo("en-US"));
}
private static string ValidateNumber(string numStr)
{
return Double.Parse(numStr, NumberStyles.Float, CultureInfo.InvariantCulture).ToString("0.#######", new CultureInfo("en-US"));
}
// public static async Task SetColorData(string path, VideoColorData d)
// {
// try
// {
// string tmpPath = IoUtils.FilenameSuffix(path, ".tmp");
//
// List<string> args = new List<string>();
//
// args.Add($"-o {tmpPath.Wrap()}");
// args.Add($"--colour-matrix 0:{d.ColorMatrixCoeffs}");
// args.Add($"--colour-transfer-characteristics 0:{d.ColorTransfer}");
// args.Add($"--colour-primaries 0:{d.ColorPrimaries}");
// args.Add($"--colour-range 0:{d.ColorRange}");
// if (!string.IsNullOrWhiteSpace(d.LumaMax)) args.Add($"--max-luminance 0:{d.LumaMax}");
// if (!string.IsNullOrWhiteSpace(d.LumaMin)) args.Add($"--min-luminance 0:{d.LumaMin}");
// if (!string.IsNullOrWhiteSpace(d.RedX)) args.Add($"--chromaticity-coordinates 0:{d.RedX},{d.RedY},{d.GreenX},{d.GreenY},{d.BlueX},{d.BlueY}");
// if (!string.IsNullOrWhiteSpace(d.RedX)) args.Add($"--white-colour-coordinates 0:{d.WhiteX},{d.WhiteY}");
// if (!string.IsNullOrWhiteSpace(d.MaxCll)) args.Add($"--max-content-light 0:{d.MaxCll}");
// if (!string.IsNullOrWhiteSpace(d.MaxFall)) args.Add($"--max-frame-light 0:{d.MaxFall}");
// args.Add($"{path.Wrap()}");
//
// await AvProcess.RunMkvMerge(string.Join(" ", args), OS.NmkoderProcess.ProcessType.Primary, true);
//
// if (!File.Exists(tmpPath))
// {
// Logger.Log($"Error: Muxing failed.");
// return;
// }
//
// int filesizeDiffKb = (int)((Math.Abs(new FileInfo(path).Length - new FileInfo(tmpPath).Length)) / 1024);
// double filesizeFactor = (double)(new FileInfo(tmpPath).Length) / (double)(new FileInfo(path).Length);
// Logger.Log($"{MethodBase.GetCurrentMethod().DeclaringType}: Filesize ratio of remuxed file against original: {filesizeFactor}", true);
//
// if (filesizeDiffKb > 1024 && (filesizeFactor < 0.95d || filesizeFactor > 1.05d))
// {
// Logger.Log($"Warning: Output file size differs by >1MB is not within 5% of the original file's size! Won't delete original to be sure.");
// }
// else
// {
// File.Delete(path);
// File.Move(tmpPath, path);
// }
// }
// catch (Exception e)
// {
// Logger.Log($"SetColorData Error: {e.Message}\n{e.StackTrace}");
// }
// }
public static int GetColorPrimaries(string s) // Defined by the "Color primaries" section of ISO/IEC 23091-4/ITU-T H.273
{
s = s.Trim().ToLower();
if (s == "bt709") return 1;
if (s == "bt470m") return 4;
if (s == "bt470bg") return 5;
if (s == "bt601") return 6;
if (s == "smpte240m") return 7;
if (s == "film") return 8;
if (s == "bt2020") return 9;
if (s == "smpte428") return 10;
if (s == "smpte431") return 11;
if (s == "smpte432") return 12;
return 2; // Fallback: 2 = Unspecified
}
public static int GetColorTransfer(string s) // Defined by the "Transfer characteristics" section of ISO/IEC 23091-4/ITU-T H.273
{
s = s.Trim().ToLower();
if (s == "bt709") return 1;
if (s == "gamma22" || s == "bt470m") return 4;
if (s == "gamma28" || s == "bt470bg") return 5; // BT.470 System B, G (historical)
if (s == "bt601" || s == "smpte170m") return 6; // BT.601
if (s == "smpte240m") return 7; // SMPTE 240 M
if (s == "linear") return 8; // Linear
//if (s == "?") return 9; // Logarithmic(100 : 1 range)
//if (s == "?") return 10; // Logarithmic (100 * Sqrt(10) : 1 range)
if (s == "iec61966-2-4") return 11; // IEC 61966-2-4
if (s == "bt1361" || s == "bt1361e") return 12; // BT.1361
if (s == "srgb") return 13; // SRGB
if (s == "bt2020-10") return 14; // BT.2020 10-bit systems
if (s == "bt2020-12") return 15; // BT.2020 12-bit systems
if (s == "smpte2084") return 16; // SMPTE ST 2084, ITU BT.2100 PQ
if (s == "smpte428") return 17; // SMPTE ST 428
if (s == "bt2100") return 18; // BT.2100 HLG, ARIB STD-B67
return 2; // Fallback: 2 = Unspecified
}
public static int GetMatrixCoeffs(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
{
s = s.Trim().ToLower();
if (s == "bt709") return 1;
if (s == "fcc") return 4; // US FCC 73.628
if (s == "bt470bg") return 5; // BT.470 System B, G (historical)
if (s == "bt601" || s == "smpte170m") return 6; // BT.601
if (s == "smpte240m") return 7; // SMPTE 240 M
if (s == "ycgco") return 8; // YCgCo
if (s == "bt2020ncl" || s == "bt2020nc") return 9; // BT.2020 non-constant luminance, BT.2100 YCbCr
if (s == "bt2020") return 10; // BT.2020 constant luminance
if (s == "smpte2085") return 11; // SMPTE ST 2085 YDzDx
// 12: MC_CHROMAT_NCL - Chromaticity-derived non-constant luminance
// 13: MC_CHROMAT_CL - Chromaticity-derived constant luminance
// 14: MC_ICTCP BT.2100 - ICtCp
return 2; // Fallback: 2 = Unspecified
}
public static int GetColorRange(string s) // Defined by the "Matrix coefficients" section of ISO/IEC 23091-4/ITU-T H.27
{
s = s.Trim().ToLower();
if (s == "tv") return 1; // TV
if (s == "pc") return 2; // PC/Full
return 0; // Fallback: Unspecified
}
public static string FormatForAom(string colorspace)
{
return colorspace.Replace("bt2020-10", "bt2020-10bit").Replace("bt2020-12", "bt2020-12bit");
}
#region Get string from int
public static string GetColorPrimariesString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "bt470m";
case 5: return "bt470bg";
case 6: return "bt601";
case 7: return "smpte240m";
case 8: return "film";
case 9: return "bt2020";
case 10: return "smpte428";
case 11: return "smpte431";
case 12: return "smpte432";
}
return "";
}
public static string GetColorTransferString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "gamma22"; // "bt470m"
case 5: return "gamma28"; // "bt470bg"
case 6: return "bt601"; // "smpte170m"
case 7: return "smpte240m";
case 8: return "linear";
case 11: return "iec61966-2-4";
case 12: return "bt1361";
case 13: return "srgb";
case 14: return "bt2020-10";
case 15: return "bt2020-12";
case 16: return "smpte2084";
case 17: return "smpte428";
case 18: return "bt2100";
}
return "";
}
public static string GetColorMatrixCoeffsString(int n)
{
switch (n)
{
case 1: return "bt709";
case 4: return "fcc";
case 5: return "bt470bg";
case 6: return "bt601";
case 7: return "smpte240m";
case 8: return "ycgco";
case 9: return "bt2020ncl";
case 10: return "bt2020";
}
return "";
}
public static string GetColorRangeString(int n)
{
switch (n)
{
case 1: return "tv";
case 2: return "pc";
}
return "";
}
#endregion
#region Get friendly name from int
public static string GetColorPrimariesName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "BT.470 System B, G (historical)";
case 5: return "BT.470 System M (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240";
case 8: return "Generic film (color filters using illuminant C)";
case 9: return "BT.2020, BT.2100";
case 10: return "SMPTE 428 (CIE 1921 XYZ)";
case 11: return "SMPTE RP 431-2";
case 12: return "SMPTE EG 432-1";
case 22: return "EBU Tech. 3213-E";
}
return "Unknown";
}
public static string GetColorTransferName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "BT.470 System B, G (historical)";
case 5: return "BT.470 System M (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240 M";
case 8: return "Linear";
case 9: return "Logarithmic (100 : 1 range)";
case 10: return "Logarithmic (100 * Sqrt(10) : 1 range)";
case 11: return "IEC 61966-2-4";
case 12: return "BT.1361";
case 13: return "sRGB or sYCC";
case 14: return "BT.2020 10-bit systems";
case 15: return "BT.2020 12-bit systems";
case 16: return "SMPTE ST 2084, ITU BT.2100 PQ";
case 17: return "SMPTE ST 428";
case 18: return "BT.2100 HLG, ARIB STD-B67";
}
return "Unknown";
}
public static string GetColorMatrixCoeffsName(int n)
{
switch (n)
{
case 1: return "BT.709";
case 2: return "Unspecified";
case 4: return "US FCC 73.628";
case 5: return "BT.470 System B, G (historical)";
case 6: return "BT.601";
case 7: return "SMPTE 240 M";
case 8: return "YCgCo";
case 9: return "BT.2020 non-constant luminance, BT.2100 YCbCr";
case 10: return "BT.2020 constant luminance";
case 11: return "SMPTE ST 2085 YDzDx";
case 12: return "Chromaticity-derived non-constant luminance";
case 13: return "Chromaticity-derived constant luminance";
case 14: return "BT.2100 ICtCp";
}
return "Unknown";
}
public static string GetColorRangeName(int n)
{
switch (n)
{
case 0: return "Unspecified";
case 1: return "TV (Limited)";
case 2: return "PC (Full)";
}
return "Unknown";
}
#endregion
}
}