Fix image sequence import, fix JPEG export args, include img ext in frame output dir name

This commit is contained in:
N00MKRAD
2024-11-08 11:54:26 +01:00
parent 71da171156
commit 70e17dcabc
9 changed files with 85 additions and 55 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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)