mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-16 16:37:48 +01:00
Fix image sequence import, fix JPEG export args, include img ext in frame output dir name
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace Flowframes.Data
|
||||
{
|
||||
@@ -59,13 +58,27 @@ namespace Flowframes.Data
|
||||
return;
|
||||
}
|
||||
|
||||
text = text.Replace(':', '/'); // Replace colon with slash in case someone thinks it's a good idea to write a fraction like that
|
||||
string[] numbers = text.Split('/');
|
||||
|
||||
// Check if split is only 1 items (probably integer number)
|
||||
// If split is only 1 item, it's a single number, not a fraction
|
||||
if (numbers.Length == 1)
|
||||
{
|
||||
Numerator = numbers[0].GetFloat().RoundToInt();
|
||||
float numFloat = numbers[0].GetFloat();
|
||||
int numInt = numFloat.RoundToInt();
|
||||
|
||||
// If parsed float is equal to the rounded int, it's a whole number
|
||||
if (numbers[0].GetFloat().EqualsRoughly(numInt))
|
||||
{
|
||||
Numerator = numInt;
|
||||
Denominator = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use float constructor if not a whole number
|
||||
this = new Fraction(numFloat);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,11 +74,12 @@ namespace Flowframes.Data
|
||||
Size = GetSize();
|
||||
}
|
||||
|
||||
public async Task InitializeSequence()
|
||||
public void InitializeSequence()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SequenceInitialized) return;
|
||||
if (SequenceInitialized)
|
||||
return;
|
||||
|
||||
string seqPath = Path.Combine(Paths.GetFrameSeqPath(), CreationTime.ToString(), "frames.concat");
|
||||
string chosenExt = IoUtils.GetUniqueExtensions(SourcePath).FirstOrDefault();
|
||||
@@ -101,7 +102,7 @@ namespace Flowframes.Data
|
||||
try
|
||||
{
|
||||
if (IsDirectory && !SequenceInitialized)
|
||||
await InitializeSequence();
|
||||
InitializeSequence();
|
||||
|
||||
await LoadFormatInfo(ImportPath);
|
||||
AllStreams = await FfmpegUtils.GetStreams(ImportPath, progressBar, StreamCount, InputRate, countFrames, this);
|
||||
@@ -122,11 +123,19 @@ namespace Flowframes.Data
|
||||
|
||||
private async Task LoadFormatInfo(string path)
|
||||
{
|
||||
StreamCount = await FfmpegUtils.GetStreamCount(path);
|
||||
|
||||
// If input is a sequence, there's not much metadata to check
|
||||
if (path.IsConcatFile())
|
||||
{
|
||||
DurationMs = (long)(FileCount * 1000 / ((Fraction)InputRate).Float); // Estimate duration using specified FPS and frame count
|
||||
return;
|
||||
}
|
||||
|
||||
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, this);
|
||||
FfmpegCommands.CheckVfr(path, this);
|
||||
StreamCount = await FfmpegUtils.GetStreamCount(path);
|
||||
TotalKbits = (await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "bit_rate")).GetInt() / 1000;
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace Flowframes.IO
|
||||
if (key == Key.maxVidHeight) return WriteDefault(key, "2160");
|
||||
if (key == Key.clearLogOnInput) return WriteDefault(key, "True");
|
||||
if (key == Key.tempDirCustom) return WriteDefault(key, "D:/");
|
||||
if (key == Key.exportNamePattern) return WriteDefault(key, "[NAME]-[FACTOR]x-[AI]-[MODEL]-[FPS]fps");
|
||||
if (key == Key.exportNamePattern) return WriteDefault(key, "[NAME]-[FACTOR]x-[MODEL]-[FPS]fps");
|
||||
if (key == Key.exportNamePatternLoop) return WriteDefault(key, "-Loop[LOOPS]");
|
||||
// Interpolation
|
||||
if (key == Key.dedupThresh) return WriteDefault(key, "2");
|
||||
|
||||
@@ -594,7 +594,7 @@ namespace Flowframes.IO
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetCurrentExportFilename(bool fpsLimit, bool withExt)
|
||||
public static async Task<string> GetCurrentExportFilename(bool fpsLimit, bool isImgSeq = false, bool includeExt = true)
|
||||
{
|
||||
InterpSettings curr = Interpolate.currentSettings;
|
||||
string max = Config.Get(Config.Key.maxFps);
|
||||
@@ -605,7 +605,7 @@ namespace Flowframes.IO
|
||||
string pattern = Config.Get(Config.Key.exportNamePattern);
|
||||
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;
|
||||
bool addFpsLimitSuffix = fpsLimit && (!pattern.Contains("[FPS]") && !pattern.Contains("[ROUNDFPS]")) && encodeBoth;
|
||||
string filename = pattern;
|
||||
|
||||
filename = filename.Replace("[NAME]", inName);
|
||||
@@ -618,11 +618,17 @@ namespace Flowframes.IO
|
||||
filename = filename.Replace("[RES]", $"{outRes.Width}x{outRes.Height}");
|
||||
filename = filename.Replace("[H]", $"{outRes.Height}p");
|
||||
|
||||
if (addSuffix)
|
||||
if (addFpsLimitSuffix)
|
||||
{
|
||||
filename += Paths.fpsLimitSuffix;
|
||||
}
|
||||
|
||||
if (withExt)
|
||||
filename += FfmpegUtils.GetExt(curr.outSettings);
|
||||
if (includeExt)
|
||||
{
|
||||
string ext = FfmpegUtils.GetExt(curr.outSettings);
|
||||
ext = isImgSeq ? ext.Replace(".", "-") : ext;
|
||||
filename += ext;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Padding = Flowframes.Data.Padding;
|
||||
using I = Flowframes.Interpolate;
|
||||
using System.Diagnostics;
|
||||
@@ -20,7 +19,8 @@ namespace Flowframes.Main
|
||||
{
|
||||
class Export
|
||||
{
|
||||
|
||||
private static string MaxFps = Config.Get(Config.Key.maxFps);
|
||||
private static Fraction MaxFpsFrac = new Fraction(MaxFps);
|
||||
|
||||
public static async Task ExportFrames(string path, string outFolder, OutputSettings exportSettings, bool stepByStep)
|
||||
{
|
||||
@@ -55,16 +55,15 @@ namespace Flowframes.Main
|
||||
|
||||
try
|
||||
{
|
||||
string max = Config.Get(Config.Key.maxFps);
|
||||
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
|
||||
bool fpsLimit = maxFps.Float > 0f && I.currentSettings.outFps.Float > maxFps.Float;
|
||||
bool fpsLimit = MaxFpsFrac.Float > 0f && I.currentSettings.outFps.Float > MaxFpsFrac.Float;
|
||||
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
|
||||
string exportPath = Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(fpsLimit));
|
||||
|
||||
if (!dontEncodeFullFpsVid)
|
||||
await Encode(exportSettings, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(false, true)), I.currentSettings.outFps, new Fraction());
|
||||
await Encode(exportSettings, path, exportPath, I.currentSettings.outFps, new Fraction());
|
||||
|
||||
if (fpsLimit)
|
||||
await Encode(exportSettings, path, Path.Combine(outFolder, await IoUtils.GetCurrentExportFilename(true, true)), I.currentSettings.outFps, maxFps);
|
||||
await Encode(exportSettings, path, exportPath, I.currentSettings.outFps, MaxFpsFrac);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -77,18 +76,13 @@ namespace Flowframes.Main
|
||||
{
|
||||
InterpSettings s = I.currentSettings;
|
||||
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings, (s.ScaledResolution.IsEmpty ? s.InputResolution : s.ScaledResolution), s.outFps.Float, true).FirstOrDefault();
|
||||
|
||||
string max = Config.Get(Config.Key.maxFps);
|
||||
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
|
||||
bool fpsLimit = maxFps.Float > 0f && s.outFps.Float > maxFps.Float;
|
||||
|
||||
// Logger.Log($"VFR Ratio: {I.currentMediaFile.VideoStreams.First().FpsInfo.VfrRatio} ({I.currentMediaFile.VideoStreams.First().FpsInfo.Fps} FPS Specified, {I.currentMediaFile.VideoStreams.First().FpsInfo.SpecifiedFps} FPS Avg)");
|
||||
|
||||
bool fpsLimit = MaxFpsFrac.Float > 0f && s.outFps.Float > MaxFpsFrac.Float;
|
||||
bool gifInput = I.currentMediaFile.Format.Upper() == "GIF"; // If input is GIF, we don't need to check the color space etc
|
||||
VidExtraData extraData = gifInput ? new VidExtraData() : await FfmpegCommands.GetVidExtraInfo(s.inPath);
|
||||
string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale, extraData.Rotation);
|
||||
string extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings);
|
||||
string extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? MaxFpsFrac : new Fraction(), extraData, s.outSettings);
|
||||
|
||||
// For EXR, force bt709 input flags. Not sure if this really does anything, EXR
|
||||
if (s.outSettings.Encoder == Enums.Encoding.Encoder.Exr)
|
||||
{
|
||||
extraArgsIn += " -color_trc bt709 -color_primaries bt709 -colorspace bt709";
|
||||
@@ -108,7 +102,7 @@ namespace Flowframes.Main
|
||||
else
|
||||
{
|
||||
bool imageSequence = s.outSettings.Encoder.GetInfo().IsImageSequence;
|
||||
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, !imageSequence));
|
||||
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, isImgSeq: imageSequence));
|
||||
IoUtils.RenameExistingFileOrDir(s.FullOutPath);
|
||||
|
||||
if (imageSequence)
|
||||
@@ -126,15 +120,15 @@ namespace Flowframes.Main
|
||||
Program.mainForm.SetStatus("Copying output frames...");
|
||||
Enums.Encoding.Encoder desiredFormat = I.currentSettings.outSettings.Encoder;
|
||||
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath, "*.*")[0]).Remove(".").Upper();
|
||||
string max = Config.Get(Config.Key.maxFps);
|
||||
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
|
||||
|
||||
Fraction maxFps = new Fraction(MaxFps);
|
||||
bool fpsLimit = maxFps.Float > 0f && I.currentSettings.outFps.Float > maxFps.Float;
|
||||
bool dontEncodeFullFpsSeq = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
|
||||
bool encodeFullFpsSeq = !(fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0);
|
||||
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
|
||||
|
||||
if (!dontEncodeFullFpsSeq)
|
||||
if (encodeFullFpsSeq)
|
||||
{
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, false));
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit: false, isImgSeq: true));
|
||||
IoUtils.RenameExistingFolder(outputFolderPath);
|
||||
Logger.Log($"Exporting {desiredFormat.ToString().Upper()} frames to '{Path.GetFileName(outputFolderPath)}'...");
|
||||
|
||||
@@ -146,7 +140,7 @@ namespace Flowframes.Main
|
||||
|
||||
if (fpsLimit)
|
||||
{
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(true, false));
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit: true, isImgSeq: true));
|
||||
Logger.Log($"Exporting {desiredFormat.ToString().Upper()} frames to '{Path.GetFileName(outputFolderPath)}' (Resampled to {maxFps} FPS)...");
|
||||
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.currentSettings.outFps, maxFps, desiredFormat, OutputUtils.GetImgSeqQ(I.currentSettings.outSettings));
|
||||
}
|
||||
@@ -244,7 +238,7 @@ namespace Flowframes.Main
|
||||
File.WriteAllText(tempConcatFile, concatFileContent);
|
||||
Logger.Log($"CreateVideo: Running MergeChunks() for frames file '{Path.GetFileName(tempConcatFile)}'", true);
|
||||
bool fpsLimit = dir.Name.Contains(Paths.fpsLimitSuffix);
|
||||
string outPath = Path.Combine(baseOutPath, await IoUtils.GetCurrentExportFilename(fpsLimit, true));
|
||||
string outPath = Path.Combine(baseOutPath, await IoUtils.GetCurrentExportFilename(fpsLimit));
|
||||
await MergeChunks(tempConcatFile, outPath, isBackup);
|
||||
|
||||
if (!isBackup)
|
||||
@@ -290,9 +284,7 @@ namespace Flowframes.Main
|
||||
if (Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
|
||||
await Blend.BlendSceneChanges(concatFile, false);
|
||||
|
||||
string max = Config.Get(Config.Key.maxFps);
|
||||
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
|
||||
bool fpsLimit = maxFps.Float != 0 && I.currentSettings.outFps.Float > maxFps.Float;
|
||||
bool fpsLimit = MaxFpsFrac.Float != 0 && I.currentSettings.outFps.Float > MaxFpsFrac.Float;
|
||||
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
|
||||
|
||||
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
|
||||
@@ -304,7 +296,7 @@ namespace Flowframes.Main
|
||||
|
||||
if (!dontEncodeFullFpsVid)
|
||||
{
|
||||
string outFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, false));
|
||||
string outFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit: false, isImgSeq: true));
|
||||
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
|
||||
@@ -318,9 +310,9 @@ namespace Flowframes.Main
|
||||
|
||||
if (fpsLimit)
|
||||
{
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(true, false));
|
||||
string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit: true, isImgSeq: true));
|
||||
int startNumber = IoUtils.GetAmountOfFiles(outputFolderPath, false) + 1;
|
||||
await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.currentSettings.outFps, maxFps, settings.Encoder, OutputUtils.GetImgSeqQ(settings), AvProcess.LogMode.Hidden);
|
||||
await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.currentSettings.outFps, MaxFpsFrac, settings.Encoder, OutputUtils.GetImgSeqQ(settings), AvProcess.LogMode.Hidden);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -333,7 +325,7 @@ namespace Flowframes.Main
|
||||
string filename = Path.GetFileName(outPath);
|
||||
string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix;
|
||||
outPath = Path.Combine(newParentDir, filename);
|
||||
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, maxFps, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
|
||||
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, MaxFpsFrac, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace Flowframes
|
||||
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(currentSettings.framesFolder)} - currentInputFrameCount = {currentMediaFile.FrameCount} - extractedFrames = {extractedFrames}.\n\nYour input file might be incompatible.");
|
||||
Cancel($"Frame extraction failed!\nExtracted {extractedFrames} frames - Frames Folder exists: {Directory.Exists(currentSettings.framesFolder)} - Current Frame Count = {currentMediaFile.FrameCount}.\n\nYour input file might be incompatible.");
|
||||
}
|
||||
|
||||
if (Config.GetInt(Config.Key.dedupMode) == 1)
|
||||
|
||||
@@ -94,6 +94,9 @@ namespace Flowframes
|
||||
|
||||
public static async Task<long> GetDurationMs(string inputFile, MediaFile mediaFile, bool demuxInsteadOfPacketTs = false, bool allowDurationFromMetadata = true)
|
||||
{
|
||||
if (mediaFile.IsDirectory)
|
||||
return 0;
|
||||
|
||||
if (allowDurationFromMetadata)
|
||||
{
|
||||
Logger.Log($"GetDuration({inputFile}) - Reading duration by checking metadata.", true, false, "ffmpeg");
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Win32Interop.Enums;
|
||||
using static Flowframes.AvProcess;
|
||||
using Utils = Flowframes.Media.FfmpegUtils;
|
||||
|
||||
@@ -131,13 +132,19 @@ namespace Flowframes.Media
|
||||
inArg = $"-i {Path.GetFileName(framesFile) + Paths.symlinksSuffix}/%{Padding.interpFrames}d{GetConcatFileExt(framesFile)}";
|
||||
}
|
||||
|
||||
string sn = $"-start_number {startNo}";
|
||||
string rate = fps.ToString().Replace(",", ".");
|
||||
string vf = (resampleFps.Float < 0.1f) ? "" : $"-vf fps=fps={resampleFps}";
|
||||
string compression = format == Enums.Encoding.Encoder.Png ? pngCompr : $"-q:v {lossyQ}";
|
||||
string codec = format == Enums.Encoding.Encoder.Webp ? "-c:v libwebp" : ""; // Specify libwebp to avoid putting all frames into single animated WEBP
|
||||
string args = $"-r {rate} {inArg} {codec} {compression} {sn} {vf} -fps_mode passthrough \"{outDir}/%{Padding.interpFrames}d.{format.GetInfo().OverideExtension}\"";
|
||||
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", true);
|
||||
var ffArgs = new List<string>()
|
||||
{
|
||||
$"-r {fps.ToString().Replace(",", ".")}", // Rate
|
||||
inArg,
|
||||
format == Enums.Encoding.Encoder.Webp ? "-c:v libwebp" : "", // Codec - Specify libwebp to avoid putting all frames into single animated WEBP
|
||||
format == Enums.Encoding.Encoder.Png ? pngCompr : $"-q:v {lossyQ}", // Compression
|
||||
$"-start_number {startNo}",
|
||||
resampleFps.Float < 0.1f ? "" : $"-vf fps=fps={resampleFps}", // FPS Resample
|
||||
"-fps_mode passthrough",
|
||||
$"{outDir}/%{Padding.interpFrames}d.{format.GetInfo().OverideExtension}".Wrap(),
|
||||
};
|
||||
|
||||
await RunFfmpeg(string.Join(" ", ffArgs.Where(s => s.IsNotEmpty())), framesFile.GetParentDir(), logMode, "error", true);
|
||||
IoUtils.TryDeleteIfExists(linksDir);
|
||||
}
|
||||
|
||||
|
||||
@@ -351,7 +351,7 @@ namespace Flowframes.Media
|
||||
if (enc == Encoder.Jpeg)
|
||||
{
|
||||
var qualityLevel = ParseUtils.GetEnum<Quality.JpegWebm>(settings.Quality, true, Strings.VideoQuality);
|
||||
args.Add($"-q:v {OutputUtils.JpegQuality[qualityLevel]}");
|
||||
args.Add($"-q:v {OutputUtils.JpegQuality[qualityLevel]} -color_range full");
|
||||
}
|
||||
|
||||
if (enc == Encoder.Webp)
|
||||
|
||||
Reference in New Issue
Block a user