mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-23 19:59:31 +01:00
Fully implement image sequence export with NCNN VS, rework stuff
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
public class Quality
|
||||
{
|
||||
public enum Common { Lossless, VeryHigh, High, Medium, Low, VeryLow, Custom }
|
||||
public enum JpegWebm { ImgMax, ImgHigh, ImgMed, ImgLow, ImgLowest }
|
||||
public enum ProResProfile { Proxy, Lt, Standard, Hq, Quad4, Quad4Xq }
|
||||
public enum GifColors { Max256, High128, Medium64, Low32, VeryLow16 }
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Flowframes
|
||||
bool alphaModel = model.SupportsAlpha;
|
||||
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
|
||||
bool gifOutput = outSettings.Encoder == Enums.Encoding.Encoder.Gif;
|
||||
bool proResAlpha = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && Config.GetInt(Config.Key.proResProfile) > 3; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
|
||||
bool proResAlpha = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
|
||||
bool outputSupportsAlpha = pngOutput || gifOutput || proResAlpha;
|
||||
string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).ToLowerInvariant() : Path.GetExtension(inPath).ToLowerInvariant();
|
||||
alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov"));
|
||||
@@ -194,8 +194,8 @@ namespace Flowframes
|
||||
public void RefreshExtensions(FrameType type = FrameType.Both)
|
||||
{
|
||||
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
|
||||
bool aviHqChroma = outSettings.Format == Enums.Output.Format.Avi && outSettings.PixelFormat != Enums.Encoding.PixelFormat.Yuv420P; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
|
||||
bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && Config.GetInt(Config.Key.proResProfile) > 3; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE
|
||||
bool aviHqChroma = outSettings.Format == Enums.Output.Format.Avi && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
|
||||
bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
|
||||
|
||||
bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma;
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ namespace Flowframes.Data
|
||||
{ Enums.Encoding.Quality.GifColors.Medium64.ToString(), "Medium (64)" },
|
||||
{ Enums.Encoding.Quality.GifColors.Low32.ToString(), "Low (32)" },
|
||||
{ Enums.Encoding.Quality.GifColors.VeryLow16.ToString(), "Very Low (16)" },
|
||||
{ Enums.Encoding.Quality.JpegWebm.ImgMax.ToString(), "Maximum" },
|
||||
{ Enums.Encoding.Quality.JpegWebm.ImgHigh.ToString(), "High" },
|
||||
{ Enums.Encoding.Quality.JpegWebm.ImgMed.ToString(), "Medium" },
|
||||
{ Enums.Encoding.Quality.JpegWebm.ImgLow.ToString(), "Low" },
|
||||
{ Enums.Encoding.Quality.JpegWebm.ImgLowest.ToString(), "Lowest" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,6 +372,14 @@ namespace Flowframes
|
||||
return s.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public static string Upper(this string s)
|
||||
{
|
||||
if (s == null)
|
||||
return s;
|
||||
|
||||
return s.ToUpperInvariant();
|
||||
}
|
||||
|
||||
public static EncoderInfoVideo GetInfo (this Enums.Encoding.Encoder enc)
|
||||
{
|
||||
return OutputUtils.GetEncoderInfoVideo(enc);
|
||||
|
||||
@@ -379,6 +379,7 @@ namespace Flowframes
|
||||
|
||||
AiProcessSuspend.Reset();
|
||||
|
||||
|
||||
if (Interpolate.currentSettings.outSettings.Format == Enums.Output.Format.Realtime)
|
||||
{
|
||||
await Interpolate.Realtime();
|
||||
|
||||
@@ -259,15 +259,6 @@ namespace Flowframes.IO
|
||||
if (key == Key.jpegFrames) return WriteDefault(key, "True");
|
||||
// Video Export
|
||||
if (key == Key.minOutVidLength) return WriteDefault(key, "5");
|
||||
if (key == Key.h264Crf) return WriteDefault(key, "20");
|
||||
if (key == Key.h265Crf) return WriteDefault(key, "24");
|
||||
if (key == Key.av1Crf) return WriteDefault(key, "28");
|
||||
if (key == Key.vp9Crf) return WriteDefault(key, "28");
|
||||
if (key == Key.proResProfile) return WriteDefault(key, "2");
|
||||
if (key == Key.aviCodec) return WriteDefault(key, "ffv1");
|
||||
if (key == Key.imgSeqFormat) return WriteDefault(key, "PNG");
|
||||
if (key == Key.aviColors) return WriteDefault(key, "yuv420p");
|
||||
if (key == Key.gifColors) return WriteDefault(key, "128 (High)");
|
||||
if (key == Key.gifDitherType) return WriteDefault(key, "bayer");
|
||||
if (key == Key.minVidLength) return WriteDefault(key, "5");
|
||||
// AI
|
||||
@@ -312,9 +303,6 @@ namespace Flowframes.IO
|
||||
autoEncMode,
|
||||
autoEncSafeBufferCuda,
|
||||
autoEncSafeBufferNcnn,
|
||||
av1Crf,
|
||||
aviCodec,
|
||||
aviColors,
|
||||
clearLogOnInput,
|
||||
cmdDebugMode,
|
||||
compressedPyVersion,
|
||||
@@ -334,12 +322,7 @@ namespace Flowframes.IO
|
||||
ffprobeFrameCount,
|
||||
fixOutputDuration,
|
||||
frameOrderDebug,
|
||||
gifColors,
|
||||
gifDitherType,
|
||||
h264Crf,
|
||||
h265Crf,
|
||||
imgSeqFormat,
|
||||
imgSeqQuality,
|
||||
imgSeqSampleCount,
|
||||
jpegFrames,
|
||||
jpegInterp,
|
||||
@@ -358,13 +341,10 @@ namespace Flowframes.IO
|
||||
maxVidHeight,
|
||||
minOutVidLength,
|
||||
minVidLength,
|
||||
mp4Enc,
|
||||
mpdecimateMode,
|
||||
ncnnGpus,
|
||||
ncnnThreads,
|
||||
opusBitrate,
|
||||
pixFmt,
|
||||
proResProfile,
|
||||
processingMode,
|
||||
rifeCudaBufferSize,
|
||||
rifeCudaFp16,
|
||||
@@ -379,7 +359,6 @@ namespace Flowframes.IO
|
||||
tempFolderLoc,
|
||||
torchGpus,
|
||||
uhdThresh,
|
||||
vp9Crf,
|
||||
vsRtShowOsd,
|
||||
vsUseLsmash
|
||||
}
|
||||
|
||||
@@ -500,12 +500,11 @@ namespace Flowframes.IO
|
||||
/// <summary>
|
||||
/// Add ".old" suffix to an existing file to avoid it getting overwritten. If one already exists, it will be ".old.old" etc.
|
||||
/// </summary>
|
||||
public static void RenameExistingFile(string path)
|
||||
public static void RenameExistingFileOrDir(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string ext = Path.GetExtension(path);
|
||||
string renamedPath = path;
|
||||
@@ -515,9 +514,19 @@ namespace Flowframes.IO
|
||||
|
||||
File.Move(path, renamedPath);
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
string renamedPath = path;
|
||||
|
||||
while (Directory.Exists(renamedPath))
|
||||
renamedPath = renamedPath + ".old";
|
||||
|
||||
Directory.Move(path, renamedPath);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Logger.Log($"RenameExistingFile: Failed to rename '{path}': {e.Message}", true);
|
||||
Logger.Log($"RenameExistingFileOrDir: Failed to rename '{path}': {e.Message}", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Flowframes.Main
|
||||
|
||||
public static async Task ExportFrames(string path, string outFolder, OutputSettings exportSettings, bool stepByStep)
|
||||
{
|
||||
if(Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
|
||||
if (Config.GetInt(Config.Key.sceneChangeFillMode) == 1)
|
||||
{
|
||||
string frameFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
|
||||
await Blend.BlendSceneChanges(frameFile);
|
||||
@@ -97,17 +97,25 @@ namespace Flowframes.Main
|
||||
}
|
||||
else
|
||||
{
|
||||
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, true));
|
||||
IoUtils.RenameExistingFile(s.FullOutPath);
|
||||
bool imageSequence = s.outSettings.Encoder.GetInfo().IsImageSequence;
|
||||
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, !imageSequence));
|
||||
IoUtils.RenameExistingFileOrDir(s.FullOutPath);
|
||||
|
||||
if (imageSequence)
|
||||
{
|
||||
Directory.CreateDirectory(s.FullOutPath);
|
||||
s.FullOutPath += $"/%{Padding.interpFrames}d.{s.outSettings.Encoder.GetInfo().OverideExtension}";
|
||||
}
|
||||
|
||||
return $"{extraArgsIn} -i pipe: {extraArgsOut} {encArgs} {s.FullOutPath.Wrap()}";
|
||||
}
|
||||
}
|
||||
|
||||
static async Task ExportImageSequence (string framesPath, bool stepByStep)
|
||||
static async Task ExportImageSequence(string framesPath, bool stepByStep)
|
||||
{
|
||||
Program.mainForm.SetStatus("Copying output frames...");
|
||||
string desiredFormat = Config.Get(Config.Key.imgSeqFormat).ToUpper();
|
||||
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath)[0]).Remove(".").ToUpper();
|
||||
Enums.Encoding.Encoder desiredFormat = I.currentSettings.outSettings.Encoder;
|
||||
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.currentSettings.outFps.GetFloat() > maxFps.GetFloat();
|
||||
@@ -118,54 +126,25 @@ namespace Flowframes.Main
|
||||
{
|
||||
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)}'...");
|
||||
Logger.Log($"Exporting {desiredFormat.ToString().Upper()} frames to '{Path.GetFileName(outputFolderPath)}'...");
|
||||
|
||||
if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format
|
||||
if (desiredFormat.GetInfo().OverideExtension.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.currentSettings.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat));
|
||||
await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.currentSettings.outFps, new Fraction(), desiredFormat, OutputUtils.GetImgSeqQ(I.currentSettings.outSettings));
|
||||
}
|
||||
|
||||
if (fpsLimit)
|
||||
{
|
||||
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.currentSettings.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat));
|
||||
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));
|
||||
}
|
||||
|
||||
if (!stepByStep)
|
||||
await IoUtils.DeleteContentsOfDirAsync(I.currentSettings.interpFolder);
|
||||
}
|
||||
|
||||
static int GetImgSeqQ (string format)
|
||||
{
|
||||
if(format.ToLowerInvariant() == "jpg" || format.ToLowerInvariant() == "jpeg")
|
||||
{
|
||||
switch (Config.GetInt(Config.Key.imgSeqQuality))
|
||||
{
|
||||
case 0: return 1;
|
||||
case 1: return 3;
|
||||
case 2: return 5;
|
||||
case 3: return 11;
|
||||
case 4: return 31;
|
||||
}
|
||||
}
|
||||
|
||||
if (format.ToLowerInvariant() == "webp")
|
||||
{
|
||||
switch (Config.GetInt(Config.Key.imgSeqQuality))
|
||||
{
|
||||
case 0: return 100;
|
||||
case 1: return 90;
|
||||
case 2: return 75;
|
||||
case 3: return 40;
|
||||
case 4: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static async Task CopyOutputFrames(string framesPath, string framesFile, string outputFolderPath, int startNo, bool dontMove, bool hideLog)
|
||||
{
|
||||
IoUtils.CreateDir(outputFolderPath);
|
||||
@@ -221,7 +200,7 @@ namespace Flowframes.Main
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task MuxPipedVideo (string inputVideo, string outputPath)
|
||||
public static async Task MuxPipedVideo(string inputVideo, string outputPath)
|
||||
{
|
||||
await MuxOutputVideo(inputVideo, Path.Combine(outputPath, outputPath));
|
||||
await Loop(outputPath, await GetLoopTimes());
|
||||
@@ -237,7 +216,7 @@ namespace Flowframes.Main
|
||||
|
||||
NmkdStopwatch sw = new NmkdStopwatch();
|
||||
|
||||
if(!isBackup)
|
||||
if (!isBackup)
|
||||
Program.mainForm.SetStatus("Merging video chunks...");
|
||||
|
||||
try
|
||||
@@ -283,10 +262,10 @@ 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
|
||||
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.currentSettings.inPath, outPath, isBackup, !isBackup);
|
||||
|
||||
if(!isBackup)
|
||||
if (!isBackup)
|
||||
await Loop(outPath, await GetLoopTimes());
|
||||
}
|
||||
|
||||
@@ -310,7 +289,7 @@ namespace Flowframes.Main
|
||||
|
||||
if (settings.Encoder.GetInfo().IsImageSequence) // Image Sequence output mode, not video
|
||||
{
|
||||
string desiredFormat = Config.Get(Config.Key.imgSeqFormat);
|
||||
string desiredFormat = settings.Encoder.GetInfo().OverideExtension;
|
||||
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper();
|
||||
|
||||
if (!dontEncodeFullFpsVid)
|
||||
@@ -318,20 +297,20 @@ namespace Flowframes.Main
|
||||
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
|
||||
if (chunkNo == 1) // Only check for existing folder on first chunk, otherwise each chunk makes a new folder
|
||||
IoUtils.RenameExistingFolder(outFolderPath);
|
||||
|
||||
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.currentSettings.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
|
||||
await FfmpegEncode.FramesToFrames(concatFile, outFolderPath, startNo, I.currentSettings.outFps, new Fraction(), settings.Encoder, OutputUtils.GetImgSeqQ(settings), AvProcess.LogMode.Hidden);
|
||||
}
|
||||
|
||||
if (fpsLimit)
|
||||
{
|
||||
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.currentSettings.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden);
|
||||
await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.currentSettings.outFps, maxFps, settings.Encoder, OutputUtils.GetImgSeqQ(settings), AvProcess.LogMode.Hidden);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -384,7 +363,7 @@ namespace Flowframes.Main
|
||||
if (!Config.GetBool(Config.Key.keepAudio) && !Config.GetBool(Config.Key.keepAudio))
|
||||
return;
|
||||
|
||||
if(showLog)
|
||||
if (showLog)
|
||||
Program.mainForm.SetStatus("Muxing audio/subtitles into video...");
|
||||
|
||||
if (I.currentSettings.inputIsFrames)
|
||||
|
||||
@@ -61,10 +61,15 @@ namespace Flowframes
|
||||
if (!currentlyUsingAutoEnc)
|
||||
{
|
||||
if (currentSettings.ai.Piped)
|
||||
{
|
||||
if(!currentSettings.outSettings.Encoder.GetInfo().IsImageSequence)
|
||||
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outSettings, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0)
|
||||
await Task.Run(async () => { await FrameRename.Unrename(); });
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Flowframes
|
||||
if(showLog)
|
||||
Logger.Log($"Merging videos...", false, Logger.GetLastLine().Contains("frame"));
|
||||
|
||||
IoUtils.RenameExistingFile(outPath);
|
||||
IoUtils.RenameExistingFileOrDir(outPath);
|
||||
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
|
||||
string vfrFilename = Path.GetFileName(concatFile);
|
||||
string args = $" {loopStr} -f concat -i {vfrFilename} -fps_mode cfr -c copy -movflags +faststart -fflags +genpts {outPath.Wrap()}";
|
||||
@@ -60,7 +60,7 @@ namespace Flowframes
|
||||
string ext = Path.GetExtension(inputFile);
|
||||
string loopSuffix = Config.Get(Config.Key.exportNamePatternLoop).Replace("[LOOPS]", $"{times}").Replace("[PLAYS]", $"{times + 1}");
|
||||
string outpath = $"{pathNoExt}{loopSuffix}{ext}";
|
||||
IoUtils.RenameExistingFile(outpath);
|
||||
IoUtils.RenameExistingFileOrDir(outpath);
|
||||
string args = $" -stream_loop {times} -i {inputFile.Wrap()} -c copy {outpath.Wrap()}";
|
||||
await RunFfmpeg(args, LogMode.Hidden);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Flowframes.Media
|
||||
if (logMode != LogMode.Hidden)
|
||||
Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS...");
|
||||
|
||||
IoUtils.RenameExistingFile(outPath);
|
||||
IoUtils.RenameExistingFileOrDir(outPath);
|
||||
Directory.CreateDirectory(outPath.GetParentDir());
|
||||
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
|
||||
|
||||
@@ -106,12 +106,11 @@ namespace Flowframes.Media
|
||||
return Path.GetExtension(File.ReadAllLines(concatFilePath).FirstOrDefault().Split('\'')[1]);
|
||||
}
|
||||
|
||||
public static async Task FramesToFrames(string framesFile, string outDir, int startNo, Fraction fps, Fraction resampleFps, string format = "png", int lossyQ = 1, LogMode logMode = LogMode.OnlyLastLine)
|
||||
public static async Task FramesToFrames(string framesFile, string outDir, int startNo, Fraction fps, Fraction resampleFps, Enums.Encoding.Encoder format = Enums.Encoding.Encoder.Png, int lossyQ = 1, LogMode logMode = LogMode.OnlyLastLine)
|
||||
{
|
||||
Directory.CreateDirectory(outDir);
|
||||
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
|
||||
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
|
||||
format = format.ToLowerInvariant();
|
||||
|
||||
if (Config.GetBool(Config.Key.allowSymlinkEncoding, true) && Symlinks.SymlinksAllowed())
|
||||
{
|
||||
@@ -122,9 +121,9 @@ namespace Flowframes.Media
|
||||
string sn = $"-start_number {startNo}";
|
||||
string rate = fps.ToString().Replace(",", ".");
|
||||
string vf = (resampleFps.GetFloat() < 0.1f) ? "" : $"-vf fps=fps={resampleFps}";
|
||||
string compression = format == "png" ? pngCompr : $"-q:v {lossyQ}";
|
||||
string codec = format == "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}\"";
|
||||
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);
|
||||
IoUtils.TryDeleteIfExists(linksDir);
|
||||
}
|
||||
|
||||
@@ -255,7 +255,8 @@ namespace Flowframes.Media
|
||||
|
||||
if (enc == Encoder.ProResKs)
|
||||
{
|
||||
args.Add($"-profile:v {Config.GetInt(Config.Key.proResProfile)}");
|
||||
var profile = ParseUtils.GetEnum<Quality.ProResProfile>(settings.Quality, true, Strings.VideoQuality);
|
||||
args.Add($"-profile:v {OutputUtils.ProresProfiles[profile]}");
|
||||
}
|
||||
|
||||
if (enc == Encoder.Gif)
|
||||
@@ -263,6 +264,18 @@ namespace Flowframes.Media
|
||||
args.Add("-gifflags -offsetting");
|
||||
}
|
||||
|
||||
if (enc == Encoder.Jpeg)
|
||||
{
|
||||
var qualityLevel = ParseUtils.GetEnum<Quality.JpegWebm>(settings.Quality, true, Strings.VideoQuality);
|
||||
args.Add($"-q:v {OutputUtils.JpegQuality[qualityLevel]}");
|
||||
}
|
||||
|
||||
if (enc == Encoder.Webp)
|
||||
{
|
||||
var qualityLevel = ParseUtils.GetEnum<Quality.JpegWebm>(settings.Quality, true, Strings.VideoQuality);
|
||||
args.Add($"-q:v {OutputUtils.WebpQuality[qualityLevel]}");
|
||||
}
|
||||
|
||||
return new string[] { string.Join(" ", args) };
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Win32Interop.Enums;
|
||||
using static Flowframes.Data.Enums.Encoding;
|
||||
using Encoder = Flowframes.Data.Enums.Encoding.Encoder;
|
||||
using PixFmt = Flowframes.Data.Enums.Encoding.PixelFormat;
|
||||
@@ -10,6 +11,8 @@ namespace Flowframes.MiscUtils
|
||||
{
|
||||
internal class OutputUtils
|
||||
{
|
||||
public static readonly List<PixFmt> AlphaFormats = new List<PixFmt> { PixFmt.Rgba, PixFmt.Yuva420P, PixFmt.Yuva444P10Le };
|
||||
|
||||
public static EncoderInfoVideo GetEncoderInfoVideo(Encoder encoder)
|
||||
{
|
||||
if (encoder == Encoder.X264)
|
||||
@@ -195,6 +198,8 @@ namespace Flowframes.MiscUtils
|
||||
Codec = Codec.Jpeg,
|
||||
Name = "mjpeg",
|
||||
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P },
|
||||
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
|
||||
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
|
||||
IsImageSequence = true,
|
||||
OverideExtension = "jpg",
|
||||
};
|
||||
@@ -207,6 +212,8 @@ namespace Flowframes.MiscUtils
|
||||
Codec = Codec.Webp,
|
||||
Name = "libwebp",
|
||||
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuva420P },
|
||||
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
|
||||
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
|
||||
IsImageSequence = true,
|
||||
OverideExtension = "webp",
|
||||
};
|
||||
@@ -282,5 +289,46 @@ namespace Flowframes.MiscUtils
|
||||
{ Quality.Common.Low, 32 },
|
||||
{ Quality.Common.VeryLow, 40 },
|
||||
};
|
||||
|
||||
public static Dictionary<Quality.ProResProfile, string> ProresProfiles = new Dictionary<Quality.ProResProfile, string>
|
||||
{
|
||||
{ Quality.ProResProfile.Proxy, "proxy" },
|
||||
{ Quality.ProResProfile.Lt, "proxy" },
|
||||
{ Quality.ProResProfile.Standard, "standard" },
|
||||
{ Quality.ProResProfile.Hq, "hq" },
|
||||
{ Quality.ProResProfile.Quad4, "4444" },
|
||||
{ Quality.ProResProfile.Quad4Xq, "4444xq" },
|
||||
};
|
||||
|
||||
public static Dictionary<Quality.JpegWebm, int> JpegQuality = new Dictionary<Quality.JpegWebm, int>
|
||||
{
|
||||
{ Quality.JpegWebm.ImgMax, 1 },
|
||||
{ Quality.JpegWebm.ImgHigh, 3 },
|
||||
{ Quality.JpegWebm.ImgMed, 5 },
|
||||
{ Quality.JpegWebm.ImgLow, 11 },
|
||||
{ Quality.JpegWebm.ImgLowest, 31 },
|
||||
};
|
||||
|
||||
public static Dictionary<Quality.JpegWebm, int> WebpQuality = new Dictionary<Quality.JpegWebm, int>
|
||||
{
|
||||
{ Quality.JpegWebm.ImgMax, 100 },
|
||||
{ Quality.JpegWebm.ImgHigh, 90 },
|
||||
{ Quality.JpegWebm.ImgMed, 75 },
|
||||
{ Quality.JpegWebm.ImgLow, 40 },
|
||||
{ Quality.JpegWebm.ImgLowest, 0 },
|
||||
};
|
||||
|
||||
public static int GetImgSeqQ (OutputSettings settings)
|
||||
{
|
||||
var qualityLevel = ParseUtils.GetEnum<Quality.JpegWebm>(settings.Quality, true, Strings.VideoQuality);
|
||||
|
||||
if (settings.Encoder == Encoder.Jpeg)
|
||||
return JpegQuality[qualityLevel];
|
||||
|
||||
if (settings.Encoder == Encoder.Webp)
|
||||
return WebpQuality[qualityLevel];
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user