Fully implement image sequence export with NCNN VS, rework stuff

This commit is contained in:
n00mkrad
2023-02-20 19:30:23 +01:00
parent 8e5c3a618c
commit 59ac292a34
13 changed files with 146 additions and 99 deletions

View File

@@ -18,6 +18,7 @@
public class Quality public class Quality
{ {
public enum Common { Lossless, VeryHigh, High, Medium, Low, VeryLow, Custom } 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 ProResProfile { Proxy, Lt, Standard, Hq, Quad4, Quad4Xq }
public enum GifColors { Max256, High128, Medium64, Low32, VeryLow16 } public enum GifColors { Max256, High128, Medium64, Low32, VeryLow16 }
} }

View File

@@ -176,7 +176,7 @@ namespace Flowframes
bool alphaModel = model.SupportsAlpha; bool alphaModel = model.SupportsAlpha;
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png; bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool gifOutput = outSettings.Encoder == Enums.Encoding.Encoder.Gif; 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; bool outputSupportsAlpha = pngOutput || gifOutput || proResAlpha;
string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).ToLowerInvariant() : Path.GetExtension(inPath).ToLowerInvariant(); 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")); alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov"));
@@ -194,8 +194,8 @@ namespace Flowframes
public void RefreshExtensions(FrameType type = FrameType.Both) public void RefreshExtensions(FrameType type = FrameType.Both)
{ {
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png; 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 aviHqChroma = outSettings.Format == Enums.Output.Format.Avi && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && Config.GetInt(Config.Key.proResProfile) > 3; // TODO: CHECK IF WORKS WITH NEW ENCODING SETTINGS CODE bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma; bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma;

View File

@@ -70,6 +70,11 @@ namespace Flowframes.Data
{ Enums.Encoding.Quality.GifColors.Medium64.ToString(), "Medium (64)" }, { Enums.Encoding.Quality.GifColors.Medium64.ToString(), "Medium (64)" },
{ Enums.Encoding.Quality.GifColors.Low32.ToString(), "Low (32)" }, { Enums.Encoding.Quality.GifColors.Low32.ToString(), "Low (32)" },
{ Enums.Encoding.Quality.GifColors.VeryLow16.ToString(), "Very Low (16)" }, { 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" },
}; };
} }
} }

View File

@@ -372,6 +372,14 @@ namespace Flowframes
return s.ToLowerInvariant(); 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) public static EncoderInfoVideo GetInfo (this Enums.Encoding.Encoder enc)
{ {
return OutputUtils.GetEncoderInfoVideo(enc); return OutputUtils.GetEncoderInfoVideo(enc);

View File

@@ -379,6 +379,7 @@ namespace Flowframes
AiProcessSuspend.Reset(); AiProcessSuspend.Reset();
if (Interpolate.currentSettings.outSettings.Format == Enums.Output.Format.Realtime) if (Interpolate.currentSettings.outSettings.Format == Enums.Output.Format.Realtime)
{ {
await Interpolate.Realtime(); await Interpolate.Realtime();

View File

@@ -259,15 +259,6 @@ namespace Flowframes.IO
if (key == Key.jpegFrames) return WriteDefault(key, "True"); if (key == Key.jpegFrames) return WriteDefault(key, "True");
// Video Export // Video Export
if (key == Key.minOutVidLength) return WriteDefault(key, "5"); 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.gifDitherType) return WriteDefault(key, "bayer");
if (key == Key.minVidLength) return WriteDefault(key, "5"); if (key == Key.minVidLength) return WriteDefault(key, "5");
// AI // AI
@@ -312,9 +303,6 @@ namespace Flowframes.IO
autoEncMode, autoEncMode,
autoEncSafeBufferCuda, autoEncSafeBufferCuda,
autoEncSafeBufferNcnn, autoEncSafeBufferNcnn,
av1Crf,
aviCodec,
aviColors,
clearLogOnInput, clearLogOnInput,
cmdDebugMode, cmdDebugMode,
compressedPyVersion, compressedPyVersion,
@@ -334,12 +322,7 @@ namespace Flowframes.IO
ffprobeFrameCount, ffprobeFrameCount,
fixOutputDuration, fixOutputDuration,
frameOrderDebug, frameOrderDebug,
gifColors,
gifDitherType, gifDitherType,
h264Crf,
h265Crf,
imgSeqFormat,
imgSeqQuality,
imgSeqSampleCount, imgSeqSampleCount,
jpegFrames, jpegFrames,
jpegInterp, jpegInterp,
@@ -358,13 +341,10 @@ namespace Flowframes.IO
maxVidHeight, maxVidHeight,
minOutVidLength, minOutVidLength,
minVidLength, minVidLength,
mp4Enc,
mpdecimateMode, mpdecimateMode,
ncnnGpus, ncnnGpus,
ncnnThreads, ncnnThreads,
opusBitrate, opusBitrate,
pixFmt,
proResProfile,
processingMode, processingMode,
rifeCudaBufferSize, rifeCudaBufferSize,
rifeCudaFp16, rifeCudaFp16,
@@ -379,7 +359,6 @@ namespace Flowframes.IO
tempFolderLoc, tempFolderLoc,
torchGpus, torchGpus,
uhdThresh, uhdThresh,
vp9Crf,
vsRtShowOsd, vsRtShowOsd,
vsUseLsmash vsUseLsmash
} }

View File

@@ -500,24 +500,33 @@ namespace Flowframes.IO
/// <summary> /// <summary>
/// Add ".old" suffix to an existing file to avoid it getting overwritten. If one already exists, it will be ".old.old" etc. /// Add ".old" suffix to an existing file to avoid it getting overwritten. If one already exists, it will be ".old.old" etc.
/// </summary> /// </summary>
public static void RenameExistingFile(string path) public static void RenameExistingFileOrDir(string path)
{ {
if (!File.Exists(path))
return;
try try
{ {
string ext = Path.GetExtension(path); if (File.Exists(path))
string renamedPath = path; {
string ext = Path.GetExtension(path);
string renamedPath = path;
while (File.Exists(renamedPath)) while (File.Exists(renamedPath))
renamedPath = Path.ChangeExtension(renamedPath, null) + ".old" + ext; renamedPath = Path.ChangeExtension(renamedPath, null) + ".old" + ext;
File.Move(path, renamedPath); 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) catch(Exception e)
{ {
Logger.Log($"RenameExistingFile: Failed to rename '{path}': {e.Message}", true); Logger.Log($"RenameExistingFileOrDir: Failed to rename '{path}': {e.Message}", true);
} }
} }

View File

@@ -20,16 +20,16 @@ namespace Flowframes.Main
{ {
class Export class Export
{ {
public static async Task ExportFrames(string path, string outFolder, OutputSettings exportSettings, bool stepByStep) 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)); string frameFile = Path.Combine(I.currentSettings.tempFolder, Paths.GetFrameOrderFilename(I.currentSettings.interpFactor));
await Blend.BlendSceneChanges(frameFile); await Blend.BlendSceneChanges(frameFile);
} }
if (exportSettings.Encoder.GetInfo().IsImageSequence) // Copy interp frames out of temp folder and skip video export for image seq export if (exportSettings.Encoder.GetInfo().IsImageSequence) // Copy interp frames out of temp folder and skip video export for image seq export
{ {
try try
@@ -90,24 +90,32 @@ namespace Flowframes.Main
{ {
encArgs = $"-pix_fmt yuv444p"; encArgs = $"-pix_fmt yuv444p";
return return
$"{extraArgsIn} -i pipe: {encArgs} {extraArgsOut} -f yuv4mpegpipe - | ffplay - " + $"{extraArgsIn} -i pipe: {encArgs} {extraArgsOut} -f yuv4mpegpipe - | ffplay - " +
$"-autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} " + $"-autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} " +
$"-window_title \"Flowframes Realtime Interpolation ({s.inFps.GetString()} FPS x{s.interpFactor} = {s.outFps.GetString()} FPS) ({s.model.Name})\" "; $"-window_title \"Flowframes Realtime Interpolation ({s.inFps.GetString()} FPS x{s.interpFactor} = {s.outFps.GetString()} FPS) ({s.model.Name})\" ";
} }
else else
{ {
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, true)); bool imageSequence = s.outSettings.Encoder.GetInfo().IsImageSequence;
IoUtils.RenameExistingFile(s.FullOutPath); 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()}"; 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..."); Program.mainForm.SetStatus("Copying output frames...");
string desiredFormat = Config.Get(Config.Key.imgSeqFormat).ToUpper(); Enums.Encoding.Encoder desiredFormat = I.currentSettings.outSettings.Encoder;
string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath)[0]).Remove(".").ToUpper(); string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath, "*.*")[0]).Remove(".").ToUpper();
string max = Config.Get(Config.Key.maxFps); string max = Config.Get(Config.Key.maxFps);
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat()); Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
bool fpsLimit = maxFps.GetFloat() > 0f && I.currentSettings.outFps.GetFloat() > maxFps.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)); string outputFolderPath = Path.Combine(I.currentSettings.outPath, await IoUtils.GetCurrentExportFilename(false, false));
IoUtils.RenameExistingFolder(outputFolderPath); 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); await CopyOutputFrames(framesPath, framesFile, outputFolderPath, 1, fpsLimit, false);
else // Encode if frames are not in desired format 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) if (fpsLimit)
{ {
string outputFolderPath = Path.Combine(I.currentSettings.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)..."); 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, GetImgSeqQ(desiredFormat)); await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.currentSettings.outFps, maxFps, desiredFormat, OutputUtils.GetImgSeqQ(I.currentSettings.outSettings));
} }
if (!stepByStep) if (!stepByStep)
await IoUtils.DeleteContentsOfDirAsync(I.currentSettings.interpFolder); 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) static async Task CopyOutputFrames(string framesPath, string framesFile, string outputFolderPath, int startNo, bool dontMove, bool hideLog)
{ {
IoUtils.CreateDir(outputFolderPath); 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 MuxOutputVideo(inputVideo, Path.Combine(outputPath, outputPath));
await Loop(outputPath, await GetLoopTimes()); await Loop(outputPath, await GetLoopTimes());
@@ -235,9 +214,9 @@ namespace Flowframes.Main
return; return;
} }
NmkdStopwatch sw = new NmkdStopwatch(); NmkdStopwatch sw = new NmkdStopwatch();
if(!isBackup) if (!isBackup)
Program.mainForm.SetStatus("Merging video chunks..."); Program.mainForm.SetStatus("Merging video chunks...");
try try
@@ -279,14 +258,14 @@ namespace Flowframes.Main
{ {
outPath = IoUtils.FilenameSuffix(outPath, Paths.backupSuffix); outPath = IoUtils.FilenameSuffix(outPath, Paths.backupSuffix);
await IoUtils.TryDeleteIfExistsAsync(outPath); await IoUtils.TryDeleteIfExistsAsync(outPath);
} }
await FfmpegCommands.ConcatVideos(framesFile, outPath, -1, !isBackup); 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); await MuxOutputVideo(I.currentSettings.inPath, outPath, isBackup, !isBackup);
if(!isBackup) if (!isBackup)
await Loop(outPath, await GetLoopTimes()); await Loop(outPath, await GetLoopTimes());
} }
@@ -310,7 +289,7 @@ namespace Flowframes.Main
if (settings.Encoder.GetInfo().IsImageSequence) // Image Sequence output mode, not video 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(); string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper();
if (!dontEncodeFullFpsVid) if (!dontEncodeFullFpsVid)
@@ -318,20 +297,20 @@ namespace Flowframes.Main
string outFolderPath = Path.Combine(I.currentSettings.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; 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); IoUtils.RenameExistingFolder(outFolderPath);
if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format
await CopyOutputFrames(interpDir, concatFile, outFolderPath, startNo, fpsLimit, true); await CopyOutputFrames(interpDir, concatFile, outFolderPath, startNo, fpsLimit, true);
else // Encode if frames are not in desired format 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) if (fpsLimit)
{ {
string outputFolderPath = Path.Combine(I.currentSettings.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; 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 else
@@ -384,7 +363,7 @@ namespace Flowframes.Main
if (!Config.GetBool(Config.Key.keepAudio) && !Config.GetBool(Config.Key.keepAudio)) if (!Config.GetBool(Config.Key.keepAudio) && !Config.GetBool(Config.Key.keepAudio))
return; return;
if(showLog) if (showLog)
Program.mainForm.SetStatus("Muxing audio/subtitles into video..."); Program.mainForm.SetStatus("Muxing audio/subtitles into video...");
if (I.currentSettings.inputIsFrames) if (I.currentSettings.inputIsFrames)

View File

@@ -61,9 +61,14 @@ namespace Flowframes
if (!currentlyUsingAutoEnc) if (!currentlyUsingAutoEnc)
{ {
if (currentSettings.ai.Piped) if (currentSettings.ai.Piped)
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath); {
if(!currentSettings.outSettings.Encoder.GetInfo().IsImageSequence)
await Export.MuxPipedVideo(currentSettings.inPath, currentSettings.FullOutPath);
}
else else
{
await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outSettings, false); await Export.ExportFrames(currentSettings.interpFolder, currentSettings.outPath, currentSettings.outSettings, false);
}
} }
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0) if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder) && IoUtils.GetAmountOfFiles(currentSettings.framesFolder, false) > 0)

View File

@@ -47,7 +47,7 @@ namespace Flowframes
if(showLog) if(showLog)
Logger.Log($"Merging videos...", false, Logger.GetLastLine().Contains("frame")); Logger.Log($"Merging videos...", false, Logger.GetLastLine().Contains("frame"));
IoUtils.RenameExistingFile(outPath); IoUtils.RenameExistingFileOrDir(outPath);
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : ""; string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
string vfrFilename = Path.GetFileName(concatFile); string vfrFilename = Path.GetFileName(concatFile);
string args = $" {loopStr} -f concat -i {vfrFilename} -fps_mode cfr -c copy -movflags +faststart -fflags +genpts {outPath.Wrap()}"; 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 ext = Path.GetExtension(inputFile);
string loopSuffix = Config.Get(Config.Key.exportNamePatternLoop).Replace("[LOOPS]", $"{times}").Replace("[PLAYS]", $"{times + 1}"); string loopSuffix = Config.Get(Config.Key.exportNamePatternLoop).Replace("[LOOPS]", $"{times}").Replace("[PLAYS]", $"{times + 1}");
string outpath = $"{pathNoExt}{loopSuffix}{ext}"; string outpath = $"{pathNoExt}{loopSuffix}{ext}";
IoUtils.RenameExistingFile(outpath); IoUtils.RenameExistingFileOrDir(outpath);
string args = $" -stream_loop {times} -i {inputFile.Wrap()} -c copy {outpath.Wrap()}"; string args = $" -stream_loop {times} -i {inputFile.Wrap()} -c copy {outpath.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden); await RunFfmpeg(args, LogMode.Hidden);

View File

@@ -20,7 +20,7 @@ namespace Flowframes.Media
if (logMode != LogMode.Hidden) if (logMode != LogMode.Hidden)
Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS..."); Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS...");
IoUtils.RenameExistingFile(outPath); IoUtils.RenameExistingFileOrDir(outPath);
Directory.CreateDirectory(outPath.GetParentDir()); Directory.CreateDirectory(outPath.GetParentDir());
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat()); 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]); 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); Directory.CreateDirectory(outDir);
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}"; string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix); string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
format = format.ToLowerInvariant();
if (Config.GetBool(Config.Key.allowSymlinkEncoding, true) && Symlinks.SymlinksAllowed()) if (Config.GetBool(Config.Key.allowSymlinkEncoding, true) && Symlinks.SymlinksAllowed())
{ {
@@ -122,9 +121,9 @@ namespace Flowframes.Media
string sn = $"-start_number {startNo}"; string sn = $"-start_number {startNo}";
string rate = fps.ToString().Replace(",", "."); string rate = fps.ToString().Replace(",", ".");
string vf = (resampleFps.GetFloat() < 0.1f) ? "" : $"-vf fps=fps={resampleFps}"; string vf = (resampleFps.GetFloat() < 0.1f) ? "" : $"-vf fps=fps={resampleFps}";
string compression = format == "png" ? pngCompr : $"-q:v {lossyQ}"; string compression = format == Enums.Encoding.Encoder.Png ? pngCompr : $"-q:v {lossyQ}";
string codec = format == "webp" ? "-c:v libwebp" : ""; // Specify libwebp to avoid putting all frames into single animated WEBP 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}\""; 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); await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", true);
IoUtils.TryDeleteIfExists(linksDir); IoUtils.TryDeleteIfExists(linksDir);
} }

View File

@@ -255,7 +255,8 @@ namespace Flowframes.Media
if (enc == Encoder.ProResKs) 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) if (enc == Encoder.Gif)
@@ -263,6 +264,18 @@ namespace Flowframes.Media
args.Add("-gifflags -offsetting"); 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) }; return new string[] { string.Join(" ", args) };
} }

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Win32Interop.Enums;
using static Flowframes.Data.Enums.Encoding; using static Flowframes.Data.Enums.Encoding;
using Encoder = Flowframes.Data.Enums.Encoding.Encoder; using Encoder = Flowframes.Data.Enums.Encoding.Encoder;
using PixFmt = Flowframes.Data.Enums.Encoding.PixelFormat; using PixFmt = Flowframes.Data.Enums.Encoding.PixelFormat;
@@ -10,6 +11,8 @@ namespace Flowframes.MiscUtils
{ {
internal class OutputUtils internal class OutputUtils
{ {
public static readonly List<PixFmt> AlphaFormats = new List<PixFmt> { PixFmt.Rgba, PixFmt.Yuva420P, PixFmt.Yuva444P10Le };
public static EncoderInfoVideo GetEncoderInfoVideo(Encoder encoder) public static EncoderInfoVideo GetEncoderInfoVideo(Encoder encoder)
{ {
if (encoder == Encoder.X264) if (encoder == Encoder.X264)
@@ -195,6 +198,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.Jpeg, Codec = Codec.Jpeg,
Name = "mjpeg", Name = "mjpeg",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P }, PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuv422P, PixFmt.Yuv444P },
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
IsImageSequence = true, IsImageSequence = true,
OverideExtension = "jpg", OverideExtension = "jpg",
}; };
@@ -207,6 +212,8 @@ namespace Flowframes.MiscUtils
Codec = Codec.Webp, Codec = Codec.Webp,
Name = "libwebp", Name = "libwebp",
PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuva420P }, PixelFormats = new List<PixFmt>() { PixFmt.Yuv420P, PixFmt.Yuva420P },
QualityLevels = ParseUtils.GetEnumStrings<Quality.JpegWebm>(),
QualityDefault = (int)Quality.JpegWebm.ImgHigh,
IsImageSequence = true, IsImageSequence = true,
OverideExtension = "webp", OverideExtension = "webp",
}; };
@@ -282,5 +289,46 @@ namespace Flowframes.MiscUtils
{ Quality.Common.Low, 32 }, { Quality.Common.Low, 32 },
{ Quality.Common.VeryLow, 40 }, { 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;
}
} }
} }