General alpha support with VS, fix SAR/DAR problems in combination with rotation

This commit is contained in:
n00mkrad
2025-11-09 16:55:58 +01:00
parent 95295c3b0f
commit 5a18f896b5
8 changed files with 165 additions and 87 deletions

View File

@@ -6,7 +6,8 @@ using Flowframes.MiscUtils;
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Enc = Flowframes.Data.Enums.Encoding.Encoder;
namespace Flowframes
{
@@ -85,7 +86,7 @@ namespace Flowframes
public InterpSettings() { }
public void InitArgs ()
public void InitArgs()
{
outFps = inFps == null ? new Fraction() : inFps * (double)interpFactor;
outFpsResampled = new Fraction(Config.Get(Config.Key.maxFps));
@@ -99,7 +100,7 @@ namespace Flowframes
RefreshExtensions(ai: ai);
}
private void SetPaths (string inputPath)
private void SetPaths(string inputPath)
{
if (!File.Exists(inputPath) && !Directory.Exists(inputPath))
return;
@@ -112,18 +113,28 @@ namespace Flowframes
inputIsFrames = IoUtils.IsPathDirectory(inPath);
}
public void RefreshAlpha ()
public void RefreshAlpha(bool vs = false)
{
try
{
bool alphaModel = model.SupportsAlpha;
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool gifOutput = outSettings.Encoder == Enums.Encoding.Encoder.Gif;
bool proResAlpha = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool outputSupportsAlpha = pngOutput || gifOutput || proResAlpha;
string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).Lower() : Path.GetExtension(inPath).Lower();
alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov"));
Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - input ext: {ext} => alpha = {alpha}", true);
if (Interpolate.currentMediaFile != null && !Interpolate.currentMediaFile.MayHaveAlpha)
{
Logger.Log($"RefreshAlpha: Input video does not have alpha channel.", true);
return;
}
string noAlphaReason = CantUseAlphaReason(vs);
alpha = string.IsNullOrEmpty(noAlphaReason);
Logger.Log($"RefreshAlpha: Alpha {(alpha ? "enabled" : $"disabled: {noAlphaReason}")}", true);
if (!alpha)
{
Logger.Log($"Input may have alpha (transparency) which won't be interpolated - {noAlphaReason}");
}
else
{
Logger.Log($"Input has alpha (transparency), interpolation will take longer. Alpha support can be toggled in the Settings.");
}
}
catch (Exception e)
{
@@ -132,11 +143,30 @@ namespace Flowframes
}
}
private string CantUseAlphaReason(bool vs)
{
if (!Config.GetBool(Config.Key.enableAlpha))
return "Alpha interpolation is disabled.";
if (!vs && !model.SupportsAlpha)
return "Alpha requires VS backend or a compatible model.";
var supportedEncoders = new List<Enc> { Enc.Png, Enc.Gif, Enc.Webp, Enc.Exr, Enc.Tiff, Enc.ProResKs, Enc.VpxVp9, Enc.Ffv1, Enc.Huffyuv };
if(!supportedEncoders.Contains(outSettings.Encoder))
return $"Selected encoder ({Strings.Encoder.Get($"{outSettings.Encoder}", returnKeyInsteadOfEmptyString: true)}) does not support alpha.";
if(!OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat))
return $"Selected pixel format ({Strings.PixelFormat.Get($"{outSettings.PixelFormat}", returnKeyInsteadOfEmptyString: true)}) does not support alpha.";
return "";
}
public enum FrameType { Import, Interp, Both };
public void RefreshExtensions(FrameType type = FrameType.Both, AiInfo ai = null)
{
if(ai == null)
if (ai == null)
{
if (Interpolate.currentSettings == null)
return;
@@ -144,9 +174,9 @@ namespace Flowframes
ai = Interpolate.currentSettings.ai;
}
bool pngOutput = outSettings.Encoder == Enums.Encoding.Encoder.Png;
bool pngOutput = outSettings.Encoder == Enc.Png;
bool aviHqChroma = outSettings.Format == Enums.Output.Format.Avi && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool proresHqChroma = outSettings.Encoder == Enums.Encoding.Encoder.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool proresHqChroma = outSettings.Encoder == Enc.ProResKs && OutputUtils.AlphaFormats.Contains(outSettings.PixelFormat);
bool forceHqChroma = pngOutput || aviHqChroma || proresHqChroma;
bool tiffSupport = !ai.NameInternal.Upper().EndsWith("NCNN"); // NCNN binaries can't load TIFF (unlike OpenCV, ffmpeg etc)
string losslessExt = tiffSupport ? ".tiff" : ".png";
@@ -164,7 +194,7 @@ namespace Flowframes
Logger.Log($"RefreshExtensions - Using '{framesExt}' for imported frames, using '{interpExt}' for interpolated frames", true);
}
public string Serialize ()
public string Serialize()
{
string s = $"INPATH|{inPath}\n";
s += $"OUTPATH|{outPath}\n";

View File

@@ -1,5 +1,4 @@
using Flowframes.Data.Streams;
using Flowframes.Forms;
using Flowframes.IO;
using Flowframes.Main;
using Flowframes.Media;
@@ -44,6 +43,8 @@ namespace Flowframes.Data
public List<float> InputTimestampDurations = new List<float>();
public List<float> OutputTimestamps = new List<float>();
public List<int> OutputFrameIndexes = null;
public VidExtraData VideoExtraData = null;
public bool MayHaveAlpha = false;
public int FileCount = 1;
public int FrameCount { get { return VideoStreams.Count > 0 ? VideoStreams[0].FrameCount : 0; } }
@@ -114,6 +115,7 @@ namespace Flowframes.Data
SubtitleStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Subtitle).Select(x => (SubtitleStream)x).ToList();
DataStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Data).Select(x => (DataStream)x).ToList();
AttachmentStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Attachment).Select(x => (AttachmentStream)x).ToList();
MayHaveAlpha = VideoStreams.Any(vs => vs.CanHaveAlpha);
Logger.Log($"Loaded and sorted streams for {Name}", true);
}
catch (Exception e)

View File

@@ -1,10 +1,11 @@
using Flowframes.IO;
using System;
using System.Drawing;
using System.Linq;
namespace Flowframes.Data
{
class VidExtraData
public class VidExtraData
{
// Color
public string ColSpace = "";
@@ -12,7 +13,8 @@ namespace Flowframes.Data
public string ColTransfer = "";
public string ColPrimaries = "";
// Aspect Ratio
// Sample/Display Aspect Ratios
public string Sar = "";
public string Dar = "";
// Rotation
@@ -24,49 +26,96 @@ namespace Flowframes.Data
public bool HasAnyColorValues => ColSpace.IsNotEmpty() || ColRange.IsNotEmpty() || ColTransfer.IsNotEmpty() || ColPrimaries.IsNotEmpty();
public string ColorsStr => $"Color Primaries {(ColPrimaries.IsEmpty() ? "unset" : ColPrimaries)}, Space {(ColSpace.IsEmpty() ? "unset" : ColSpace)}, Transfer {(ColTransfer.IsEmpty() ? "unset" : ColTransfer)}, Range {(ColRange.IsEmpty() ? "unset" : ColRange)}";
public VidExtraData () { }
public VidExtraData() { }
public VidExtraData(string ffprobeOutput)
public VidExtraData(string ffprobeOutput, bool allowColorData = true)
{
string[] lines = ffprobeOutput.SplitIntoLines();
bool keepColorSpace = Config.GetBool(Config.Key.keepColorSpace, true);
bool keepColorSpace = allowColorData && Config.GetBool(Config.Key.keepColorSpace, true);
string GetValue (string key)
{
return lines.FirstOrDefault(l => l.StartsWith(key + "="))?.Split('=').LastOrDefault();
}
string GetValue(string key, string key2 = "") => lines.FirstOrDefault(l => l.StartsWith(key + "=") || key2.IsNotEmpty() && l.StartsWith(key2 + "="))?.Split('=').Last() ?? "";
Rotation = GetValue("display_rotation")?.GetInt() ?? 0;
Dar = GetValue("display_aspect_ratio") ?? "";
int w = GetValue("width", "coded_width").GetInt();
int h = GetValue("height", "coded_height").GetInt();
Rotation = GetValue("display_rotation", "rotation").GetInt();
Sar = GetValue("sample_aspect_ratio");
Dar = GetValue("display_aspect_ratio");
// Dar = ValidateDar(w, h, Dar, Rotation);
if (keepColorSpace)
{
ColPrimaries = GetValue("color_primaries")?.Lower().Replace("unknown", "") ?? "";
ColRange = GetValue("color_range")?.Lower().Replace("unknown", "") ?? "";
ColSpace = GetValue("color_space")?.Lower().Replace("unknown", "") ?? "";
ColTransfer = GetValue("color_transfer")?.Lower().Replace("unknown", "") ?? "";
ColPrimaries = GetValue("color_primaries").Lower().Replace("unknown", "");
ColRange = GetValue("color_range").Lower().Replace("unknown", "");
ColSpace = GetValue("color_space").Lower().Replace("unknown", "");
ColTransfer = GetValue("color_transfer").Lower().Replace("unknown", "");
ColTransfer = ColTransfer.Replace("bt470bg", "gamma28").Replace("bt470m", "gamma28"); // https://forum.videohelp.com/threads/394596-Color-Matrix
if (ColSpace.IsNotEmpty() && !_validColorSpaces.Contains(ColSpace.Trim()))
{
Logger.Log($"Warning: Color Space '{ColSpace.Trim()}'.", true, false, "ffmpeg");
ColSpace = "";
}
if (ColTransfer.IsNotEmpty() && !_validColorSpaces.Contains(ColTransfer.Trim()))
{
Logger.Log($"Warning: Color Transfer '{ColTransfer.Trim()}' not valid.", true, false, "ffmpeg");
ColTransfer = "";
}
if (ColPrimaries.IsNotEmpty() && !_validColorSpaces.Contains(ColPrimaries.Trim()))
{
Logger.Log($"Warning: Color Primaries '{ColPrimaries.Trim()}' not valid.", true, false, "ffmpeg");
ColPrimaries = "";
}
}
Logger.Log($"{ColorsStr}; Display Aspect Ratio {Dar.Wrap()}, Rotation {Rotation}", true, false, "ffmpeg");
Logger.Log($"{ColorsStr}; SAR {Sar.Wrap()}, DAR {Dar.Wrap()}, Rot. {Rotation}", true, false, "ffmpeg");
}
if (!_validColorSpaces.Contains(ColSpace.Trim()))
{
Logger.Log($"Warning: Ignoring invalid color space '{ColSpace.Trim()}'.", true, false, "ffmpeg");
ColSpace = "";
}
public static string ValidateDar(int resW, int resH, string dar, int rotation)
{
if (dar.IsEmpty() || !dar.Contains(':'))
return "";
string[] parts = dar.Split(':');
if (parts.Length != 2)
return "";
var s = ValidateAspectRatio(new Size(resW, resH), new Size(parts[0].GetInt(), parts[1].GetInt()), rotation, out bool wasRotated);
if (wasRotated)
Logger.Log($"Fixed invalid DAR: {dar} -> {s.Width}:{s.Height}", true, false);
return $"{s.Width}:{s.Height}";
}
if (!_validColorSpaces.Contains(ColTransfer.Trim()))
{
Logger.Log($"Warning: Color Transfer '{ColTransfer.Trim()}' not valid.", true, false, "ffmpeg");
ColTransfer = "";
}
/// <summary>
/// Checks if the aspect ratio is valid after applying the provided rotation (degrees) to the resolution.
/// </summary>
public static Size ValidateAspectRatio(Size resolution, Size aspect, int rotationDeg, out bool wasRotated)
{
wasRotated = false;
if (!_validColorSpaces.Contains(ColPrimaries.Trim()))
{
Logger.Log($"Warning: Color Primaries '{ColPrimaries.Trim()}' not valid.", true, false, "ffmpeg");
ColPrimaries = "";
}
if (resolution.Width <= 0 || resolution.Height <= 0 || aspect.Width <= 0 || aspect.Height <= 0 || rotationDeg % 90 != 0)
return aspect; // Invalid dimensions; return as-is
// Normalize rotation to 0/90/180/270
int rot = ((rotationDeg % 360) + 360) % 360; // Map negatives to positive rotation
// Square resolution or square aspect: treat as compatible
if (resolution.Width == resolution.Height || aspect.Width == aspect.Height)
return aspect;
// No need to rotate aspect ratio for 0 or 180 rotation, as orientation stays the same
if (rot == 0 || rot == 180)
return aspect;
// At this point we know rotation is 90 or 270, which flips orientation, so we flip video dimensions for comparison
resolution = new Size(resolution.Height, resolution.Width);
bool rotatedResIsPortrait = resolution.Height > resolution.Width;
bool aspectRatioIsPortrait = aspect.Height > aspect.Width;
if (rotatedResIsPortrait == aspectRatioIsPortrait)
return aspect;
wasRotated = true;
return new Size(aspect.Height, aspect.Width); // Swap DAR to match rotated orientation
}
}
}

View File

@@ -83,20 +83,20 @@ namespace Flowframes.Main
string encArgs = FfmpegUtils.GetEncArgs(outSettings, outRes, s.outFps.Float, true).First();
bool fpsLimit = MaxFpsFrac.Float > 0f && s.outFps.Float > MaxFpsFrac.Float;
bool gifInput = I.currentMediaFile.Format.Upper() == "GIF"; // If input is GIF, we don't need to check the color space etc
VidExtraData extraData = gifInput ? new VidExtraData() : await FfmpegCommands.GetVidExtraInfo(s.inPath);
string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(I.currentMediaFile.IsVfr ? s.outFpsResampled : s.outFps, s.outItsScale, extraData.Rotation);
I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(s.inPath, allowColorData: !gifInput);
string extraArgsIn = FfmpegEncode.GetFfmpegExportArgsIn(I.currentMediaFile.IsVfr ? s.outFpsResampled : s.outFps, s.outItsScale, I.currentMediaFile.VideoExtraData.Rotation);
string extraArgsOut;
string alphaPassFile = Path.Combine(s.tempFolder, "alpha.mkv");
Fraction fps = fpsLimit ? MaxFpsFrac : new Fraction();
if (alpha == AlphaMode.AlphaOut)
{
extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fps, new VidExtraData(), alphaOutSettings);
extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fps, new VidExtraData(), alphaOutSettings, allowPad: false);
return $"{extraArgsIn} -i - {extraArgsOut} {encArgs} {alphaPassFile.Wrap()}";
}
else
{
extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fps, extraData, s.outSettings, alphaPassFile: alpha == AlphaMode.AlphaIn ? alphaPassFile : "");
extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fps, I.currentMediaFile.VideoExtraData, s.outSettings, alphaPassFile: alpha == AlphaMode.AlphaIn ? alphaPassFile : "");
}
// For EXR, force bt709 input flags. Not sure if this really does anything
@@ -211,6 +211,9 @@ namespace Flowframes.Main
return;
}
if (I.currentMediaFile.VideoExtraData == null)
I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
if (settings.Format == Enums.Output.Format.Gif)
{
int paletteColors = OutputUtils.GetGifColors(ParseUtils.GetEnum<Enums.Encoding.Quality.GifColors>(settings.Quality, true, Strings.VideoQuality));
@@ -218,8 +221,7 @@ namespace Flowframes.Main
}
else
{
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
await FfmpegEncode.FramesToVideo(framesFile, outPath, settings, fps, resampleFps, I.currentSettings.outItsScale, extraData);
await FfmpegEncode.FramesToVideo(framesFile, outPath, settings, fps, resampleFps, I.currentSettings.outItsScale, I.currentMediaFile.VideoExtraData);
await MuxOutputVideo(I.currentSettings.inPath, outPath);
await Loop(outPath, GetLoopTimes());
}
@@ -306,7 +308,8 @@ namespace Flowframes.Main
await Blend.BlendSceneChanges(concatFile, false);
bool fpsLimit = MaxFpsFrac.Float != 0 && I.currentSettings.outFps.Float > MaxFpsFrac.Float;
VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
if (I.currentMediaFile.VideoExtraData == null)
I.currentMediaFile.VideoExtraData = await FfmpegCommands.GetVidExtraInfo(I.currentSettings.inPath);
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0;
@@ -339,14 +342,14 @@ namespace Flowframes.Main
else
{
if (!dontEncodeFullFpsVid)
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, new Fraction(), I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, new Fraction(), I.currentSettings.outItsScale, I.currentMediaFile.VideoExtraData, AvProcess.LogMode.Hidden, true); // Encode
if (fpsLimit)
{
string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix;
outPath = Path.Combine(newParentDir, filename);
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, MaxFpsFrac, I.currentSettings.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
await FfmpegEncode.FramesToVideo(concatFile, outPath, settings, I.currentSettings.outFps, MaxFpsFrac, I.currentSettings.outItsScale, I.currentMediaFile.VideoExtraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps
}
}
@@ -360,7 +363,7 @@ namespace Flowframes.Main
if (looptimes < 1 || !Config.GetBool(Config.Key.enableLoop))
return;
Logger.Log($"Looping {looptimes} {(looptimes == 1 ? "time" : "times")} to reach target length of {Config.GetInt(Config.Key.minOutVidLength)}s...");
Logger.Log($"Looping {looptimes} {looptimes}x to reach target length of {Config.GetInt(Config.Key.minOutVidLength)}s...");
await FfmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt(Config.Key.loopMode) == 0);
}

View File

@@ -56,6 +56,7 @@ namespace Flowframes
await PostProcessFrames(false);
}
currentSettings.RefreshAlpha(currentSettings.ai.Piped);
if (canceled) return;
bool skip = await AutoEncodeResume.PrepareResumedRun();
if (skip || canceled) return;
@@ -120,7 +121,6 @@ namespace Flowframes
public static async Task GetFrames()
{
currentSettings.RefreshAlpha();
currentSettings.RefreshExtensions(InterpSettings.FrameType.Import);
if (Config.GetBool(Config.Key.scnDetect) && !currentSettings.ai.Piped)

View File

@@ -1,12 +1,13 @@
using Flowframes.IO;
using Flowframes.Main;
using Flowframes.Ui;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Flowframes.AvProcess;
using Utils = Flowframes.Media.FfmpegUtils;
using I = Flowframes.Interpolate;
using Flowframes.Main;
using System.Linq;
using Utils = Flowframes.Media.FfmpegUtils;
namespace Flowframes.Media
{
@@ -57,9 +58,15 @@ namespace Flowframes.Media
subArgs = "-sn";
bool isMkv = I.currentSettings.outSettings.Format == Data.Enums.Output.Format.Mkv;
string mkvFix = isMkv ? "-max_interleave_delta 0" : ""; // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/
string metaArg = (isMkv && meta) ? "-map 1:t?" : ""; // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/
string shortestArg = shortest ? "-shortest" : "";
var muxArgs = new List<string>() { "-map 0:v:0", "-map 1:a:?", "-map 1:s:?", "-c copy", audioArgs, subArgs };
muxArgs.AddIf("-max_interleave_delta 0", isMkv); // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/
muxArgs.AddIf("-map 1:t?", isMkv && meta); // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/
muxArgs.AddIf("-shortest", shortest);
muxArgs.AddIf($"-aspect {I.currentMediaFile.VideoExtraData.Dar}", I.currentMediaFile.VideoExtraData.Dar.MatchesWildcard("*:*"));
string muxArgsStr = $"{string.Join(" ", muxArgs)} {outName}";
var inputArgs = new List<string>();
inputArgs.AddIf($"-display_rotation {I.currentMediaFile.VideoExtraData.Rotation}", I.currentMediaFile.VideoExtraData.Rotation != 0);
string inputArgsStr = $"{string.Join(" ", inputArgs)} -i {inName}";
if (I.currentMediaFile.IsVfr && I.currentMediaFile.OutputTimestamps.Any())
{
@@ -74,14 +81,14 @@ namespace Flowframes.Media
string args1 = $"{trim[0]} -i {inputVideo.Wrap()} {trim[1]} -map 0 -map -0:v -map -0:d -c copy {audioArgs} {subArgs} {otherStreamsName}"; // Extract trimmed
await RunFfmpeg(args1, tempFolder, LogMode.Hidden);
string args2 = $"-i {inName} -i {otherStreamsName} -map 0:v:0 -map 1:a:? -map 1:s:? {metaArg} -c copy {audioArgs} {subArgs} {mkvFix} {shortestArg} {outName}"; // Merge interp + trimmed original
string args2 = $"{inputArgsStr} -i {otherStreamsName} {muxArgsStr}"; // Merge interp + trimmed original
await RunFfmpeg(args2, tempFolder, LogMode.Hidden);
IoUtils.TryDeleteIfExists(Path.Combine(tempFolder, otherStreamsName));
}
else // If trimming is disabled we can pull the streams directly from the input file
{
string args = $"-i {inName} -i {inputVideo.Wrap()} -map 0:v:0 -map 1:a:? -map 1:s:? {metaArg} -c copy {audioArgs} {subArgs} {mkvFix} {shortestArg} {outName}";
string args = $"{inputArgsStr} -i {inputVideo.Wrap()} {muxArgsStr}";
await RunFfmpeg(args, tempFolder, LogMode.Hidden);
}

View File

@@ -408,7 +408,7 @@ namespace Flowframes
}
}
public static async Task<VidExtraData> GetVidExtraInfo(string inputFile)
public static async Task<VidExtraData> GetVidExtraInfo(string inputFile, bool allowColorData = true)
{
string ffprobeOutput = await GetVideoInfo.GetFfprobeInfoAsync(inputFile, GetVideoInfo.FfprobeMode.ShowBoth);
VidExtraData data = new VidExtraData(ffprobeOutput);

View File

@@ -37,32 +37,22 @@ namespace Flowframes.Media
{
string pre = i == 0 ? "" : $" && ffmpeg {AvProcess.GetFfmpegDefaultArgs()}";
string post = (i == 0 && encArgs.Length > 1) ? $"-f null -" : outPath.Wrap();
args += $"{pre} {await GetFfmpegExportArgsIn(fps, itsScale)} {inArg} {encArgs[i]} {await GetFfmpegExportArgsOut(resampleFps, extraData, settings, isChunk)} {post} ";
args += $"{pre} {GetFfmpegExportArgsIn(fps, itsScale)} {inArg} {encArgs[i]} {await GetFfmpegExportArgsOut(resampleFps, extraData, settings, isChunk)} {post} ";
}
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, !isChunk);
IoUtils.TryDeleteIfExists(linksDir);
}
public static async Task<string> GetFfmpegExportArgsIn(Fraction fps, float itsScale, int rotation = 0)
public static string GetFfmpegExportArgsIn(Fraction fps, float itsScale, int rotation = 0)
{
var args = new List<string>();
fps = fps / new Fraction(itsScale);
if (fps > 0.1f)
{
args.Add($"-r {fps}");
}
if (rotation != 0)
{
args.Add($"-display_rotation {rotation}");
}
args.AddIf($"-r {fps}", fps > 0.1f);
return string.Join(" ", args);
}
public static async Task<string> GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false, string alphaPassFile = "")
public static async Task<string> GetFfmpegExportArgsOut(Fraction resampleFps, VidExtraData extraData, OutputSettings settings, bool isChunk = false, string alphaPassFile = "", bool allowPad = true)
{
var beforeArgs = new List<string>();
var filters = new List<string>();
@@ -79,10 +69,7 @@ namespace Flowframes.Media
extraArgs.AddIf($"-color_range:v {extraData.ColRange.Wrap()}", extraData.ColRange.IsNotEmpty());
}
if (!string.IsNullOrWhiteSpace(extraData.Dar) && !extraData.Dar.MatchesWildcard("*N/A*"))
extraArgs.Add($"-aspect {extraData.Dar}");
if (!isChunk && settings.Format == Enums.Output.Format.Mp4 || settings.Format == Enums.Output.Format.Mov)
if (!isChunk && (settings.Format == Enums.Output.Format.Mp4 || settings.Format == Enums.Output.Format.Mov))
extraArgs.Add($"-movflags +faststart");
if (resampleFps.Float >= 0.1f)
@@ -124,7 +111,7 @@ namespace Flowframes.Media
filters.Add($"zscale=transfer=linear,format={settings.PixelFormat.ToString().Lower()}".Wrap());
}
filters.Add(GetPadFilter(Interpolate.currentSettings.ScaledResolution.Width, Interpolate.currentSettings.ScaledResolution.Height));
filters.AddIf(GetPadFilter(Interpolate.currentSettings.ScaledResolution.Width, Interpolate.currentSettings.ScaledResolution.Height), allowPad);
filters = filters.Where(f => f.IsNotEmpty()).ToList();
return filters.Count > 0 ?