mirror of https://github.com/n00mkrad/flowframes
Fixed GIF quantizing and some other export weirdness
This commit is contained in:
parent
33115cdbc2
commit
9b77f06dbe
|
@ -13,7 +13,7 @@
|
|||
{
|
||||
public enum Codec { H264, H265, AV1, VP9, ProRes, Gif, Png, Jpeg, Webp, Ffv1, Huffyuv, Magicyuv, Rawvideo }
|
||||
public enum Encoder { X264, X265, SvtAv1, VpxVp9, Nvenc264, Nvenc265, NvencAv1, ProResKs, Gif, Png, Jpeg, Webp, Ffv1, Huffyuv, Magicyuv, Rawvideo }
|
||||
public enum PixelFormat { Yuv420P, Yuva420P, Yuv420P10Le, Yuv422P, Yuv422P10Le, Yuv444P, Yuv444P10Le, Yuva444P10Le, Rgb24, Rgba, Rgb8 };
|
||||
public enum PixelFormat { Yuv420P, Yuva420P, Yuv420P10Le, Yuv422P, Yuv422P10Le, Yuv444P, Yuv444P10Le, Yuva444P10Le, Rgb24, Rgba, Pal8 };
|
||||
|
||||
public class Quality
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Flowframes.Data
|
|||
{ Enums.Encoding.Encoder.Nvenc264.ToString(), "h264 NVENC" },
|
||||
{ Enums.Encoding.Encoder.Nvenc265.ToString(), "h265 NVENC" },
|
||||
{ Enums.Encoding.Encoder.NvencAv1.ToString(), "AV1 NVENC" },
|
||||
{ Enums.Encoding.Encoder.Gif.ToString(), "Animation" },
|
||||
{ Enums.Encoding.Encoder.Gif.ToString(), "GIF" },
|
||||
{ Enums.Encoding.Encoder.Png.ToString(), "PNG" },
|
||||
{ Enums.Encoding.Encoder.Jpeg.ToString(), "JPEG" },
|
||||
{ Enums.Encoding.Encoder.Webp.ToString(), "WEBP" },
|
||||
|
@ -47,7 +47,7 @@ namespace Flowframes.Data
|
|||
{ Enums.Encoding.PixelFormat.Yuv444P10Le.ToString(), "YUV 4:4:4 10-bit" },
|
||||
{ Enums.Encoding.PixelFormat.Yuva444P10Le.ToString(), "YUVA 4:4:4 10-bit" },
|
||||
{ Enums.Encoding.PixelFormat.Rgb24.ToString(), "RGB 8-bit" },
|
||||
{ Enums.Encoding.PixelFormat.Rgb8.ToString(), "RGB 256-color" },
|
||||
{ Enums.Encoding.PixelFormat.Pal8.ToString(), "256-color Palette" },
|
||||
{ Enums.Encoding.PixelFormat.Rgba.ToString(), "RGBA 8-bit" },
|
||||
};
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ namespace Flowframes.IO
|
|||
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 (Recommended)");
|
||||
if (key == Key.gifDitherType) return WriteDefault(key, "bayer");
|
||||
if (key == Key.minVidLength) return WriteDefault(key, "5");
|
||||
// AI
|
||||
if (key == Key.uhdThresh) return WriteDefault(key, "1600");
|
||||
|
|
|
@ -83,8 +83,8 @@ namespace Flowframes.Main
|
|||
bool fpsLimit = maxFps.GetFloat() > 0f && s.outFps.GetFloat() > maxFps.GetFloat();
|
||||
|
||||
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(s.inPath);
|
||||
string extraArgsIn = FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale);
|
||||
string extraArgsOut = FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings);
|
||||
string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale);
|
||||
string extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings);
|
||||
|
||||
if (ffplay)
|
||||
{
|
||||
|
@ -99,7 +99,7 @@ namespace Flowframes.Main
|
|||
{
|
||||
s.FullOutPath = Path.Combine(s.outPath, await IoUtils.GetCurrentExportFilename(fpsLimit, true));
|
||||
IoUtils.RenameExistingFile(s.FullOutPath);
|
||||
return $"{extraArgsIn} -i pipe: {encArgs} {extraArgsOut} {s.FullOutPath.Wrap()}";
|
||||
return $"{extraArgsIn} -i pipe: {extraArgsOut} {encArgs} {s.FullOutPath.Wrap()}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,33 @@ namespace Flowframes
|
|||
return processOutput;
|
||||
}
|
||||
|
||||
public static string RunFfmpegSync(string args, string workingDir = "", LogMode logMode = LogMode.Hidden, string loglevel = "warning")
|
||||
{
|
||||
Process ffmpeg = OsUtils.NewProcess(true);
|
||||
lastAvProcess = ffmpeg;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(loglevel))
|
||||
loglevel = defLogLevel;
|
||||
|
||||
string beforeArgs = $"-hide_banner -stats -loglevel {loglevel} -y";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(workingDir))
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {workingDir.Wrap()} & {Path.Combine(GetAvDir(), "ffmpeg.exe").Wrap()} {beforeArgs} {args}";
|
||||
else
|
||||
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg {beforeArgs} {args}";
|
||||
|
||||
if (logMode != LogMode.Hidden) Logger.Log("Running FFmpeg...", false);
|
||||
Logger.Log($"ffmpeg {beforeArgs} {args}", true, false, "ffmpeg");
|
||||
|
||||
ffmpeg.StartInfo.Arguments += " 2>&1";
|
||||
ffmpeg.Start();
|
||||
ffmpeg.PriorityClass = ProcessPriorityClass.BelowNormal;
|
||||
string output = ffmpeg.StandardOutput.ReadToEnd();
|
||||
ffmpeg.WaitForExit();
|
||||
Logger.Log($"Synchronous ffmpeg output:\n{output}", true, false, "ffmpeg");
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string GetFfmpegDefaultArgs(string loglevel = "warning")
|
||||
{
|
||||
return $"-hide_banner -stats -loglevel {loglevel} -y";
|
||||
|
|
|
@ -19,6 +19,14 @@ namespace Flowframes.Media
|
|||
return;
|
||||
}
|
||||
|
||||
Data.Enums.Output.Format format = I.currentSettings.outSettings.Format;
|
||||
|
||||
if (format == Data.Enums.Output.Format.Gif || format == Data.Enums.Output.Format.Images)
|
||||
{
|
||||
Logger.Log("Warning: Output format does not support audio.");
|
||||
return;
|
||||
}
|
||||
|
||||
string containerExt = Path.GetExtension(interpVideo);
|
||||
string tempPath = Path.Combine(tempFolder, $"vid{containerExt}");
|
||||
string outPath = Path.Combine(tempFolder, $"muxed{containerExt}");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.MiscUtils;
|
||||
using Flowframes.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -38,23 +39,28 @@ namespace Flowframes.Media
|
|||
{
|
||||
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} ";
|
||||
args += $"{pre} {await 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)
|
||||
public static async Task<string> GetFfmpegExportArgsIn(Fraction fps, float itsScale)
|
||||
{
|
||||
var args = new List<string>();
|
||||
|
||||
fps = fps / new Fraction(itsScale);
|
||||
return $"-r {fps}";
|
||||
args.Add($"-r {fps}");
|
||||
|
||||
return string.Join(" ", args);
|
||||
}
|
||||
|
||||
public static string GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false)
|
||||
public static async Task<string> GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false)
|
||||
{
|
||||
List<string> filters = new List<string>();
|
||||
string extraArgs = Config.Get(Config.Key.ffEncArgs);
|
||||
var beforeArgs = new List<string>();
|
||||
var filters = new List<string>();
|
||||
var extraArgs = new List<string> { Config.Get(Config.Key.ffEncArgs) };
|
||||
|
||||
if (resampleFps.GetFloat() >= 0.1f)
|
||||
filters.Add($"fps=fps={resampleFps}");
|
||||
|
@ -63,25 +69,36 @@ namespace Flowframes.Media
|
|||
{
|
||||
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()}";
|
||||
extraArgs.Add($"-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}";
|
||||
extraArgs.Add($"-aspect {extraData.displayRatio}");
|
||||
|
||||
if (!isChunk && settings.Format == Enums.Output.Format.Mp4)
|
||||
extraArgs += $" -movflags +faststart";
|
||||
if (!isChunk && settings.Format == Enums.Output.Format.Mp4 || settings.Format == Enums.Output.Format.Mov)
|
||||
extraArgs.Add($"-movflags +faststart");
|
||||
|
||||
if(settings.Format == Enums.Output.Format.Gif)
|
||||
if (settings.Format == Enums.Output.Format.Gif)
|
||||
{
|
||||
string dither = Config.Get(Config.Key.gifDitherType).Split(' ').First();
|
||||
string palettePath = Path.Combine(Paths.GetSessionDataPath(), "palette.png");
|
||||
string paletteFilter = $"[1:v]paletteuse=dither={dither}";
|
||||
|
||||
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);
|
||||
await FfmpegExtract.GeneratePalette(Interpolate.currentMediaFile.ImportPath, palettePath, colors);
|
||||
|
||||
if (File.Exists(palettePath))
|
||||
{
|
||||
beforeArgs.Add($"-i {palettePath.Wrap()}");
|
||||
filters.Add(paletteFilter);
|
||||
}
|
||||
}
|
||||
|
||||
filters.Add(GetPadFilter());
|
||||
return filters.Count > 0 ? $"-vf {string.Join(",", filters)}" : "" + $" {extraArgs}";
|
||||
|
||||
return filters.Count > 0 ?
|
||||
$"{string.Join(" ", beforeArgs)} -filter_complex [0:v]{string.Join("[vf],[vf]", filters.Where(f => !string.IsNullOrWhiteSpace(f)))}[vf] -map [vf] {string.Join(" ", extraArgs)}" :
|
||||
$"{string.Join(" ", beforeArgs)} {string.Join(" ", extraArgs)}";
|
||||
}
|
||||
|
||||
public static string GetConcatFileExt(string concatFilePath)
|
||||
|
|
|
@ -99,7 +99,6 @@ namespace Flowframes.Media
|
|||
if (!alpha && compatible)
|
||||
{
|
||||
await CopyImages(inPath, outPath, showLog);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -311,5 +310,11 @@ namespace Flowframes.Media
|
|||
string args = $"{sseof} -i {inputFile.Wrap()} -update 1 {pixFmt} {sizeStr} {trim} {outputPath.Wrap()}";
|
||||
await RunFfmpeg(args, LogMode.Hidden);
|
||||
}
|
||||
|
||||
public static async Task GeneratePalette (string inputFile, string outputPath, int colors = 256)
|
||||
{
|
||||
string args = $"-i {inputFile.Wrap()} -vf palettegen={colors} {outputPath.Wrap()}";
|
||||
await Task.Run(() => AvProcess.RunFfmpegSync(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace Flowframes.MiscUtils
|
|||
PixelFormats = new List<PixFmt>() { PixFmt.Yuv422P10Le, PixFmt.Yuv444P10Le, PixFmt.Yuva444P10Le },
|
||||
PixelFormatDefault = PixFmt.Yuv422P10Le,
|
||||
QualityLevels = ParseUtils.GetEnumStrings<Quality.ProResProfile>(),
|
||||
QualityDefault = (int)Quality.ProResProfile.Hq,
|
||||
QualityDefault = (int)Quality.ProResProfile.Standard,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -125,12 +125,13 @@ namespace Flowframes.MiscUtils
|
|||
{
|
||||
Codec = Codec.Gif,
|
||||
Name = "gif",
|
||||
PixelFormats = new List<PixFmt>() { PixFmt.Rgb8 },
|
||||
PixelFormatDefault = PixFmt.Rgb8,
|
||||
PixelFormats = new List<PixFmt>() { PixFmt.Pal8 },
|
||||
PixelFormatDefault = PixFmt.Pal8,
|
||||
QualityLevels = ParseUtils.GetEnumStrings<Quality.GifColors>(),
|
||||
QualityDefault = (int)Quality.GifColors.High128,
|
||||
OverideExtension = "gif",
|
||||
MaxFramerate = 50,
|
||||
Modulo = 1,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue