Fixed GIF quantizing and some other export weirdness

This commit is contained in:
n00mkrad 2023-01-20 20:58:32 +01:00
parent 33115cdbc2
commit 9b77f06dbe
9 changed files with 83 additions and 25 deletions

View File

@ -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
{

View File

@ -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" },
};

View File

@ -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");

View File

@ -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()}";
}
}

View File

@ -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";

View File

@ -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}");

View File

@ -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)

View File

@ -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));
}
}
}

View File

@ -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,
};
}