mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-16 16:37:48 +01:00
135 lines
7.4 KiB
C#
135 lines
7.4 KiB
C#
using Flowframes.Data;
|
|
using Flowframes.IO;
|
|
using Flowframes.MiscUtils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Media;
|
|
using static Flowframes.AvProcess;
|
|
using Utils = Flowframes.Media.FfmpegUtils;
|
|
|
|
namespace Flowframes.Media
|
|
{
|
|
partial class FfmpegEncode : FfmpegCommands
|
|
{
|
|
public static async Task FramesToVideo(string framesFile, string outPath, OutputSettings settings, Fraction fps, Fraction resampleFps, float itsScale, VidExtraData extraData, LogMode logMode = LogMode.OnlyLastLine, bool isChunk = false)
|
|
{
|
|
if (logMode != LogMode.Hidden)
|
|
Logger.Log((resampleFps.GetFloat() <= 0) ? "Encoding video..." : $"Encoding video resampled to {resampleFps.GetString()} FPS...");
|
|
|
|
IoUtils.RenameExistingFile(outPath);
|
|
Directory.CreateDirectory(outPath.GetParentDir());
|
|
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.GetFloat());
|
|
|
|
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
|
|
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
|
|
|
|
if (Config.GetBool(Config.Key.allowSymlinkEncoding, true) && Symlinks.SymlinksAllowed())
|
|
{
|
|
if (await Symlinks.MakeSymlinksForEncode(framesFile, linksDir, Padding.interpFrames))
|
|
inArg = $"-i \"{linksDir}/%{Padding.interpFrames}d{GetConcatFileExt(framesFile)}\"";
|
|
}
|
|
|
|
string args = "";
|
|
|
|
for (int i = 0; i < encArgs.Length; i++)
|
|
{
|
|
string pre = i == 0 ? "" : $" && ffmpeg {AvProcess.GetFfmpegDefaultArgs()}";
|
|
string post = (i == 0 && encArgs.Length > 1) ? $"-f null -" : outPath.Wrap();
|
|
args += $"{pre} {GetFfmpegExportArgsIn(fps, itsScale)} {inArg} {encArgs[i]} {GetFfmpegExportArgsOut(resampleFps, extraData, settings, isChunk)} {post} ";
|
|
}
|
|
|
|
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, !isChunk);
|
|
IoUtils.TryDeleteIfExists(linksDir);
|
|
}
|
|
|
|
public static string GetFfmpegExportArgsIn(Fraction fps, float itsScale)
|
|
{
|
|
fps = fps / new Fraction(itsScale);
|
|
return $"-r {fps}";
|
|
}
|
|
|
|
public static string GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false)
|
|
{
|
|
List<string> filters = new List<string>();
|
|
string extraArgs = Config.Get(Config.Key.ffEncArgs);
|
|
|
|
if (resampleFps.GetFloat() >= 0.1f)
|
|
filters.Add($"fps=fps={resampleFps}");
|
|
|
|
if (Config.GetBool(Config.Key.keepColorSpace) && extraData.HasAllValues())
|
|
{
|
|
Logger.Log($"Applying color transfer ({extraData.colorSpace}).", true, false, "ffmpeg");
|
|
filters.Add($"scale=out_color_matrix={extraData.colorSpace}");
|
|
extraArgs += $" -colorspace {extraData.colorSpace} -color_primaries {extraData.colorPrimaries} -color_trc {extraData.colorTransfer} -color_range:v {extraData.colorRange.Wrap()}";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(extraData.displayRatio) && !extraData.displayRatio.MatchesWildcard("*N/A*"))
|
|
extraArgs += $" -aspect {extraData.displayRatio}";
|
|
|
|
if (!isChunk && settings.Format == Enums.Output.Format.Mp4)
|
|
extraArgs += $" -movflags +faststart";
|
|
|
|
if(settings.Format == Enums.Output.Format.Gif)
|
|
{
|
|
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First();
|
|
int colors = OutputUtils.GetGifColors(ParseUtils.GetEnum<Enums.Encoding.Quality.GifColors>(settings.Quality, true, Strings.VideoQuality));
|
|
string paletteFilter = $"\"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither={dither}\"";
|
|
filters.Add(paletteFilter);
|
|
}
|
|
|
|
filters.Add(GetPadFilter());
|
|
return filters.Count > 0 ? $"-vf {string.Join(",", filters)}" : "" + $" {extraArgs}";
|
|
}
|
|
|
|
public static string GetConcatFileExt(string concatFilePath)
|
|
{
|
|
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)
|
|
{
|
|
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())
|
|
{
|
|
if (await Symlinks.MakeSymlinksForEncode(framesFile, linksDir, Padding.interpFrames))
|
|
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.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}\"";
|
|
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, "error", true);
|
|
IoUtils.TryDeleteIfExists(linksDir);
|
|
}
|
|
|
|
public static async Task FramesToGifConcat(string framesFile, string outPath, Fraction rate, bool palette, int colors, Fraction resampleFps, float itsScale, LogMode logMode = LogMode.OnlyLastLine)
|
|
{
|
|
if (rate.GetFloat() > 50f && (resampleFps.GetFloat() > 50f || resampleFps.GetFloat() < 1))
|
|
resampleFps = new Fraction(50, 1); // Force limit framerate as encoding above 50 will cause problems
|
|
|
|
if (logMode != LogMode.Hidden)
|
|
Logger.Log((resampleFps.GetFloat() <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.GetFloat().ToString().Replace(",", ".")} FPS...");
|
|
|
|
string framesFilename = Path.GetFileName(framesFile);
|
|
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First();
|
|
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither={dither}\"" : "";
|
|
string fpsFilter = (resampleFps.GetFloat() <= 0) ? "" : $"fps=fps={resampleFps}";
|
|
string vf = FormatUtils.ConcatStrings(new string[] { paletteFilter, fpsFilter });
|
|
string extraArgs = Config.Get(Config.Key.ffEncArgs);
|
|
rate = rate / new Fraction(itsScale);
|
|
string args = $"-f concat -r {rate} -i {framesFilename.Wrap()} -gifflags -offsetting {vf} {extraArgs} {outPath.Wrap()}";
|
|
await RunFfmpeg(args, framesFile.GetParentDir(), LogMode.OnlyLastLine, "error");
|
|
}
|
|
}
|
|
}
|