mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-21 18:59:26 +01:00
Fixed VFR frame drops, bigger AutoEnc safety buffer for NCNN, cleanup
This commit is contained in:
@@ -13,7 +13,7 @@ namespace Flowframes
|
|||||||
{
|
{
|
||||||
static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p";
|
static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p";
|
||||||
|
|
||||||
static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart -vf \"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
|
static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart";
|
||||||
static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
|
static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
|
||||||
static string pngComprArg = "-compression_level 3";
|
static string pngComprArg = "-compression_level 3";
|
||||||
|
|
||||||
@@ -39,14 +39,11 @@ namespace Flowframes
|
|||||||
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
||||||
IOUtils.CreateDir(frameFolderPath);
|
IOUtils.CreateDir(frameFolderPath);
|
||||||
string timecodeStr = timecodes ? "-copyts -r 1000 -frame_pts true" : "";
|
string timecodeStr = timecodes ? "-copyts -r 1000 -frame_pts true" : "";
|
||||||
string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"," : "";
|
string scnDetect = sceneDetect ? $"-vf \"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"" : "";
|
||||||
|
string mpStr = deDupe ? ((Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr) : "";
|
||||||
|
string vf = (scnDetect.Length > 2 || mpStr.Length > 2) ? $"-vf {scnDetect},{mpStr}".ListCommaFix() : "";
|
||||||
string pad = Padding.inputFrames.ToString();
|
string pad = Padding.inputFrames.ToString();
|
||||||
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{divisionFilter} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
|
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} {vf} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
|
||||||
if (deDupe)
|
|
||||||
{
|
|
||||||
string mpStr = (Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr;
|
|
||||||
args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} -vf {scnDetect}{mpStr},{divisionFilter} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
|
|
||||||
}
|
|
||||||
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
|
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
|
||||||
await AvProcess.RunFfmpeg(args, logMode);
|
await AvProcess.RunFfmpeg(args, logMode);
|
||||||
await Task.Delay(1);
|
await Task.Delay(1);
|
||||||
@@ -99,39 +96,15 @@ namespace Flowframes
|
|||||||
DeleteSource(inputDir);
|
DeleteSource(inputDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate)
|
public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine)
|
||||||
{
|
{
|
||||||
|
if(logMode != AvProcess.LogMode.Hidden)
|
||||||
Logger.Log($"Encoding MP4 video with CRF {crf}...");
|
Logger.Log($"Encoding MP4 video with CRF {crf}...");
|
||||||
string enc = useH265 ? "libx265" : "libx264";
|
string enc = useH265 ? "libx265" : "libx264";
|
||||||
string presetStr = $"-preset {Config.Get("ffEncPreset")}";
|
string presetStr = $"-preset {Config.Get("ffEncPreset")}";
|
||||||
string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
|
|
||||||
string vfrFilename = Path.GetFileName(framesFile);
|
string vfrFilename = Path.GetFileName(framesFile);
|
||||||
|
string args = $"-vsync 2 -f concat -i {vfrFilename} -r {fps.ToString().Replace(",", ".")} -c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
|
||||||
string args = $" {vsyncStr} -f concat ";
|
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode);
|
||||||
if (inRate)
|
|
||||||
args += $"-r {fps.ToString().Replace(",", ".")} -i {vfrFilename} ";
|
|
||||||
else
|
|
||||||
args += $"-i {vfrFilename} -r {fps.ToString().Replace(",", ".")} ";
|
|
||||||
|
|
||||||
args += $"-c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
|
|
||||||
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task FramesToMp4VfrChunk(string framesFile, string outPath, bool useH265, int crf, float fps, bool inRate)
|
|
||||||
{
|
|
||||||
string enc = useH265 ? "libx265" : "libx264";
|
|
||||||
string presetStr = $"-preset {Config.Get("ffEncPreset")}";
|
|
||||||
string vsyncStr = Config.GetInt("vfrMode") == 0 ? "-vsync 1" : "-vsync 2";
|
|
||||||
string vfrFilename = Path.GetFileName(framesFile);
|
|
||||||
|
|
||||||
string args = $" {vsyncStr} -f concat ";
|
|
||||||
if (inRate)
|
|
||||||
args += $"-r {fps.ToString().Replace(",", ".")} -i {vfrFilename} ";
|
|
||||||
else
|
|
||||||
args += $"-i {vfrFilename} -r {fps.ToString().Replace(",", ".")} ";
|
|
||||||
|
|
||||||
args += $"-c:v {enc} -crf {crf} {presetStr} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
|
|
||||||
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.Hidden);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1)
|
public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1)
|
||||||
@@ -158,8 +131,7 @@ namespace Flowframes
|
|||||||
public static async void FramesToApng(string inputDir, bool opti, int fps, string prefix, bool delSrc = false)
|
public static async void FramesToApng(string inputDir, bool opti, int fps, string prefix, bool delSrc = false)
|
||||||
{
|
{
|
||||||
int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, "*.png")[0], prefix);
|
int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, "*.png")[0], prefix);
|
||||||
string filter = "";
|
string filter = opti ? "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"" : "";
|
||||||
if(opti) filter = "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"";
|
|
||||||
string args = "-framerate " + fps + " -i \"" + inputDir + "\\" + prefix + "%0" + nums + "d.png\" -f apng -plays 0 " + filter + " \"" + inputDir + "-anim.png\"";
|
string args = "-framerate " + fps + " -i \"" + inputDir + "\\" + prefix + "%0" + nums + "d.png\" -f apng -plays 0 " + filter + " \"" + inputDir + "-anim.png\"";
|
||||||
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
||||||
if (delSrc)
|
if (delSrc)
|
||||||
|
|||||||
@@ -155,5 +155,10 @@ namespace Flowframes
|
|||||||
|
|
||||||
return str.Remove(place, stringToReplace.Length).Insert(place, replaceWith);
|
return str.Remove(place, stringToReplace.Length).Insert(place, replaceWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ListCommaFix (this string str)
|
||||||
|
{
|
||||||
|
return str.Replace(" , ", " ").Replace(" ,", " ").Replace(", ", " ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,8 +177,7 @@ namespace Flowframes.Magick
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string testStr = "";
|
string testStr = testRun ? " [TestRun]" : "";
|
||||||
if (testRun) testStr = " [TestRun]";
|
|
||||||
|
|
||||||
if (Interpolate.canceled) return;
|
if (Interpolate.canceled) return;
|
||||||
if (skipped)
|
if (skipped)
|
||||||
@@ -238,16 +237,13 @@ namespace Flowframes.Magick
|
|||||||
string line = dupeFrameLines[i];
|
string line = dupeFrameLines[i];
|
||||||
sourceFrameNum++;
|
sourceFrameNum++;
|
||||||
|
|
||||||
string paddedFilename = "";
|
|
||||||
string sourceFramePath = "";
|
|
||||||
|
|
||||||
string[] kvp = line.Split(':');
|
string[] kvp = line.Split(':');
|
||||||
int currentInFrame = kvp[0].GetInt();
|
int currentInFrame = kvp[0].GetInt();
|
||||||
int currentDupesAmount = kvp[1].GetInt();
|
int currentDupesAmount = kvp[1].GetInt();
|
||||||
|
|
||||||
// Copy Source Frame
|
// Copy Source Frame
|
||||||
paddedFilename = sourceFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + $".{ext}";
|
string paddedFilename = sourceFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + $".{ext}";
|
||||||
sourceFramePath = Path.Combine(path, paddedFilename);
|
string sourceFramePath = Path.Combine(path, paddedFilename);
|
||||||
if(debugLog) Logger.Log("[Source] Moving " + Path.GetFileName(sourceFramePath) + " => " + outFrameNum + $".{ext}");
|
if(debugLog) Logger.Log("[Source] Moving " + Path.GetFileName(sourceFramePath) + " => " + outFrameNum + $".{ext}");
|
||||||
if (!IOUtils.TryCopy(sourceFramePath, Path.Combine(tempSubFolder, outFrameNum + $".{ext}")))
|
if (!IOUtils.TryCopy(sourceFramePath, Path.Combine(tempSubFolder, outFrameNum + $".{ext}")))
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Flowframes.Main
|
|||||||
static string interpFramesFolder;
|
static string interpFramesFolder;
|
||||||
static string videoChunksFolder;
|
static string videoChunksFolder;
|
||||||
public static int chunkSize = 125; // Encode every n frames
|
public static int chunkSize = 125; // Encode every n frames
|
||||||
public static int safetyBufferFrames = 25;
|
public static int safetyBufferFrames = 50; // Ignore latest n frames to avoid using images that haven't been fully encoded yet
|
||||||
public static List<string> encodedFrames = new List<string>();
|
public static List<string> encodedFrames = new List<string>();
|
||||||
public static List<string> unencodedFrames = new List<string>();
|
public static List<string> unencodedFrames = new List<string>();
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ namespace Flowframes.Main
|
|||||||
unencodedFrames.Clear();
|
unencodedFrames.Clear();
|
||||||
|
|
||||||
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.currentFramesPath, false, "*.png") * Interpolate.lastInterpFactor);
|
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.currentFramesPath, false, "*.png") * Interpolate.lastInterpFactor);
|
||||||
|
safetyBufferFrames = Interpolate.lastAi.aiName.ToUpper().Contains("NCNN") ? 60 : 30; // Use bigger safety buffer for NCNN
|
||||||
|
Logger.Log($"Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true);
|
||||||
|
|
||||||
int videoIndex = 1;
|
int videoIndex = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -69,16 +69,7 @@ namespace Flowframes.Main
|
|||||||
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.lastInterpFactor}x.ini");
|
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.lastInterpFactor}x.ini");
|
||||||
|
|
||||||
if (mode == i.OutMode.VidGif)
|
if (mode == i.OutMode.VidGif)
|
||||||
{
|
|
||||||
await FFmpegCommands.FramesToGifVfr(vfrFile, outPath, true, Config.GetInt("gifColors"));
|
await FFmpegCommands.FramesToGifVfr(vfrFile, outPath, true, Config.GetInt("gifColors"));
|
||||||
// TODO: Remove old code once new code works well
|
|
||||||
// if (new DirectoryInfo(framesPath).GetFiles()[0].Extension != ".png")
|
|
||||||
// {
|
|
||||||
// Logger.Log("Converting output frames to PNG to encode with Gifski...");
|
|
||||||
// await Converter.Convert(framesPath, ImageMagick.MagickFormat.Png00, 20, "png", false);
|
|
||||||
// }
|
|
||||||
// await GifskiCommands.CreateGifFromFrames(i.currentOutFps.RoundToInt(), Config.GetInt("gifskiQ"), framesPath, outPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == i.OutMode.VidMp4)
|
if (mode == i.OutMode.VidMp4)
|
||||||
{
|
{
|
||||||
@@ -86,7 +77,7 @@ namespace Flowframes.Main
|
|||||||
bool h265 = Config.GetInt("mp4Enc") == 1;
|
bool h265 = Config.GetInt("mp4Enc") == 1;
|
||||||
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
|
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
|
||||||
|
|
||||||
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps, i.constantFrameRate);
|
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps);
|
||||||
await MergeAudio(i.lastInputPath, outPath);
|
await MergeAudio(i.lastInputPath, outPath);
|
||||||
|
|
||||||
if (changeFps > 0)
|
if (changeFps > 0)
|
||||||
@@ -95,8 +86,6 @@ namespace Flowframes.Main
|
|||||||
Program.mainForm.SetStatus("Creating video with desired frame rate...");
|
Program.mainForm.SetStatus("Creating video with desired frame rate...");
|
||||||
await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid);
|
await FFmpegCommands.ConvertFramerate(outPath, currentOutFile, h265, crf, changeFps, !keepOriginalFpsVid);
|
||||||
await MergeAudio(i.lastInputPath, currentOutFile);
|
await MergeAudio(i.lastInputPath, currentOutFile);
|
||||||
//if (looptimes > 0)
|
|
||||||
// await Loop(newOutPath, looptimes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (looptimes > 0)
|
if (looptimes > 0)
|
||||||
@@ -164,7 +153,7 @@ namespace Flowframes.Main
|
|||||||
string vfrFile = Path.Combine(i.currentTempDir, $"vfr-chunk-temp.ini");
|
string vfrFile = Path.Combine(i.currentTempDir, $"vfr-chunk-temp.ini");
|
||||||
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum * 2).Take(framesAmount * 2));
|
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum * 2).Take(framesAmount * 2));
|
||||||
|
|
||||||
await FFmpegCommands.FramesToMp4VfrChunk(vfrFile, outPath, h265, crf, i.currentOutFps, i.constantFrameRate);
|
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, i.currentOutFps, AvProcess.LogMode.Hidden);
|
||||||
IOUtils.TryDeleteIfExists(vfrFile);
|
IOUtils.TryDeleteIfExists(vfrFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,27 +106,10 @@ namespace Flowframes
|
|||||||
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, Paths.scenesDir));
|
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(currentTempDir, Paths.scenesDir));
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||||
Size resolution = IOUtils.GetVideoRes(inPath);
|
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, Utils.GetOutputResolution(inPath));
|
||||||
int maxHeight = Config.GetInt("maxVidHeight");
|
|
||||||
if (resolution.Height > maxHeight)
|
|
||||||
{
|
|
||||||
float factor = (float)maxHeight / resolution.Height;
|
|
||||||
int width = (resolution.Width * factor).RoundToInt();
|
|
||||||
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
|
|
||||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, new Size(width, maxHeight));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if (AvProcess.lastOutputFfmpeg.ToLower().Contains("invalid"))
|
|
||||||
{
|
|
||||||
Cancel("Failed to read input video.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (extractAudio)
|
if (extractAudio)
|
||||||
{
|
{
|
||||||
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
|
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
|
||||||
@@ -163,11 +146,6 @@ namespace Flowframes
|
|||||||
else
|
else
|
||||||
MagickDedupe.ClearCache();
|
MagickDedupe.ClearCache();
|
||||||
|
|
||||||
int frameCountAfterDedupe = IOUtils.GetAmountOfFiles(currentFramesPath, false, "*.png");
|
|
||||||
int dupesPercent = 100 - (((float)frameCountAfterDedupe / currentInputFrameCount) * 100f).RoundToInt();
|
|
||||||
constantFrameRate = dupesPercent < 5f; // Ignore VFR timings for CFR input. TODO: Figure out how to avoid dupes when using VFR timings
|
|
||||||
Logger.Log($"{dupesPercent}% of frames are dupes, so constantFrameRate = {constantFrameRate}", true);
|
|
||||||
|
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped
|
bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped
|
||||||
|
|||||||
@@ -101,22 +101,12 @@ namespace Flowframes.Main
|
|||||||
InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error");
|
InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AiProcess.filenameMap.Clear();
|
AiProcess.filenameMap.Clear();
|
||||||
bool extractAudio = true;
|
bool extractAudio = true;
|
||||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||||
Size resolution = IOUtils.GetVideoRes(currentInPath);
|
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false, InterpolateUtils.GetOutputResolution(currentInPath));
|
||||||
int maxHeight = Config.GetInt("maxVidHeight");
|
|
||||||
if (resolution.Height > maxHeight)
|
|
||||||
{
|
|
||||||
float factor = (float)maxHeight / resolution.Height;
|
|
||||||
int width = (resolution.Width * factor).RoundToInt();
|
|
||||||
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
|
|
||||||
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false, new Size(width, maxHeight));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await FFmpegCommands.VideoToFrames(currentInPath, currentFramesPath, Config.GetInt("dedupMode") == 2, false);
|
|
||||||
}
|
|
||||||
if (extractAudio)
|
if (extractAudio)
|
||||||
{
|
{
|
||||||
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
|
string audioFile = Path.Combine(currentTempDir, "audio.m4a");
|
||||||
|
|||||||
@@ -260,5 +260,30 @@ namespace Flowframes.Main
|
|||||||
MessageBox.Show(msg, title);
|
MessageBox.Show(msg, title);
|
||||||
Logger.Log("Message: " + msg, true);
|
Logger.Log("Message: " + msg, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Size GetOutputResolution (string inputPath)
|
||||||
|
{
|
||||||
|
Size resolution = IOUtils.GetVideoRes(inputPath);
|
||||||
|
int maxHeight = RoundDiv2(Config.GetInt("maxVidHeight"));
|
||||||
|
if (resolution.Height > maxHeight)
|
||||||
|
{
|
||||||
|
float factor = (float)maxHeight / resolution.Height;
|
||||||
|
Logger.Log($"Un-rounded downscaled size: {(resolution.Width * factor).ToString("0.00")}x{Config.GetInt("maxVidHeight")}", true);
|
||||||
|
int width = RoundDiv2((resolution.Width * factor).RoundToInt());
|
||||||
|
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
|
||||||
|
return new Size(width, maxHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int RoundDiv2(int n) // Round to a number that's divisible by 2 (for h264)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user