mirror of
https://github.com/n00mkrad/flowframes.git
synced 2026-07-12 13:31:56 +02:00
Support for preserving VFR timings and video rotation, better duration readout
This commit is contained in:
@@ -8,7 +8,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Stream = Flowframes.Data.Streams.Stream;
|
||||
|
||||
@@ -40,6 +39,8 @@ namespace Flowframes.Data
|
||||
public long CreationTime;
|
||||
public bool Initialized = false;
|
||||
public bool SequenceInitialized = false;
|
||||
public bool IsVfr = false;
|
||||
public List<float> InputTimestamps = new List<float>();
|
||||
|
||||
public int FileCount = 1;
|
||||
public int FrameCount { get { return VideoStreams.Count > 0 ? VideoStreams[0].FrameCount : 0; } }
|
||||
@@ -103,7 +104,7 @@ namespace Flowframes.Data
|
||||
await InitializeSequence();
|
||||
|
||||
await LoadFormatInfo(ImportPath);
|
||||
AllStreams = await FfmpegUtils.GetStreams(ImportPath, progressBar, StreamCount, InputRate, countFrames);
|
||||
AllStreams = await FfmpegUtils.GetStreams(ImportPath, progressBar, StreamCount, InputRate, countFrames, this);
|
||||
VideoStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Video).Select(x => (VideoStream)x).ToList();
|
||||
AudioStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Audio).Select(x => (AudioStream)x).ToList();
|
||||
SubtitleStreams = AllStreams.Where(x => x.Type == Stream.StreamType.Subtitle).Select(x => (SubtitleStream)x).ToList();
|
||||
@@ -123,11 +124,41 @@ namespace Flowframes.Data
|
||||
{
|
||||
Title = await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "TAG:title");
|
||||
Language = await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "TAG:language");
|
||||
DurationMs = (await FfmpegCommands.GetDurationMs(path));
|
||||
DurationMs = await FfmpegCommands.GetDurationMs(path, this);
|
||||
FfmpegCommands.CheckVfr(path, this);
|
||||
StreamCount = await FfmpegUtils.GetStreamCount(path);
|
||||
TotalKbits = (await GetVideoInfo.GetFfprobeInfoAsync(path, GetVideoInfo.FfprobeMode.ShowFormat, "bit_rate")).GetInt() / 1000;
|
||||
}
|
||||
|
||||
public List<float> GetResampledTimestamps(List<float> timestamps, double factor)
|
||||
{
|
||||
int originalCount = timestamps.Count;
|
||||
int newCount = (int)Math.Round(originalCount * factor);
|
||||
List<float> resampledTimestamps = new List<float>();
|
||||
|
||||
for (int i = 0; i < newCount; i++)
|
||||
{
|
||||
double x = i / factor;
|
||||
|
||||
if (x >= originalCount - 1)
|
||||
{
|
||||
resampledTimestamps.Add(timestamps[originalCount - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = (int)Math.Floor(x);
|
||||
double fraction = x - index;
|
||||
float startTime = timestamps[index];
|
||||
float endTime = timestamps[index + 1];
|
||||
|
||||
float interpolatedTime = (float)(startTime + (endTime - startTime) * fraction);
|
||||
resampledTimestamps.Add(interpolatedTime);
|
||||
}
|
||||
}
|
||||
|
||||
return resampledTimestamps;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
if (IsDirectory)
|
||||
|
||||
@@ -25,9 +25,14 @@ namespace Flowframes.Data
|
||||
|
||||
public ModelInfo() { }
|
||||
|
||||
public string GetUiString()
|
||||
public string GetUiString(bool addRecommendedStr = false)
|
||||
{
|
||||
return $"{Name} - {Desc}{(SupportsAlpha ? " (Supports Transparency)" : "")}{(FixedFactors.Count() > 0 ? $" ({GetFactorsString()})" : "")}{(IsDefault ? " (Recommended)" : "")}";
|
||||
string s = $"{Name} - {Desc}{(SupportsAlpha ? " - Transparency Support" : "")}{(FixedFactors.Count() > 0 ? $" ({GetFactorsString()})" : "")}";
|
||||
|
||||
if(addRecommendedStr && IsDefault)
|
||||
s += " (Recommended)";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public string GetFactorsString ()
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace Flowframes.Data
|
||||
// Aspect Ratio
|
||||
public string displayRatio = "";
|
||||
|
||||
// Rotation
|
||||
public int Rotation = 0;
|
||||
|
||||
private readonly string[] validColorSpaces = new string[] { "bt709", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100",
|
||||
"log316", "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", "smpte2084", "smpte428", "arib-std-b67" };
|
||||
|
||||
@@ -23,37 +26,44 @@ namespace Flowframes.Data
|
||||
public VidExtraData(string ffprobeOutput)
|
||||
{
|
||||
string[] lines = ffprobeOutput.SplitIntoLines();
|
||||
|
||||
if (!Config.GetBool(Config.Key.keepColorSpace, true))
|
||||
return;
|
||||
bool keepColorSpace = Config.GetBool(Config.Key.keepColorSpace, true);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.Contains("color_range"))
|
||||
if(line.StartsWith("rotation="))
|
||||
{
|
||||
colorRange = line.Split('=').LastOrDefault();
|
||||
Rotation = line.Split('=').LastOrDefault().GetInt();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.Contains("color_space"))
|
||||
if (keepColorSpace)
|
||||
{
|
||||
colorSpace = line.Split('=').LastOrDefault();
|
||||
continue;
|
||||
if (line.StartsWith("color_range"))
|
||||
{
|
||||
colorRange = line.Split('=').LastOrDefault().Lower();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("color_space"))
|
||||
{
|
||||
colorSpace = line.Split('=').LastOrDefault().Lower();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("color_transfer"))
|
||||
{
|
||||
colorTransfer = line.Split('=').LastOrDefault().Lower();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.StartsWith("color_primaries"))
|
||||
{
|
||||
colorPrimaries = line.Split('=').LastOrDefault().Lower();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.Contains("color_transfer"))
|
||||
{
|
||||
colorTransfer = line.Split('=').LastOrDefault();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.Contains("color_primaries"))
|
||||
{
|
||||
colorPrimaries = line.Split('=').LastOrDefault();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.Contains("display_aspect_ratio") && Config.GetBool(Config.Key.keepAspectRatio, true))
|
||||
if (line.StartsWith("display_aspect_ratio") && Config.GetBool(Config.Key.keepAspectRatio, true))
|
||||
{
|
||||
displayRatio = line.Split('=').LastOrDefault();
|
||||
continue;
|
||||
@@ -76,7 +86,7 @@ namespace Flowframes.Data
|
||||
}
|
||||
else
|
||||
{
|
||||
colorTransfer = colorTransfer.Replace("bt470bg", "gamma28").Replace("bt470m", "gamma28"); // https://forum.videohelp.com/threads/394596-Color-Matrix
|
||||
colorTransfer = colorTransfer.Replace("bt470bg", "gamma28").Replace("bt470m", "gamma28"); // https://forum.videohelp.com/threads/394596-Color-Matrix
|
||||
}
|
||||
|
||||
if (!validColorSpaces.Contains(colorPrimaries.Trim()))
|
||||
@@ -86,24 +96,7 @@ namespace Flowframes.Data
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAnyValues ()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(colorSpace))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(colorRange))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(colorTransfer))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(colorPrimaries))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAllValues()
|
||||
public bool HasAllColorValues()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(colorSpace))
|
||||
return false;
|
||||
|
||||
@@ -17,13 +17,17 @@ namespace Flowframes
|
||||
{
|
||||
public static class ExtensionMethods
|
||||
{
|
||||
public static string TrimNumbers(this string s, bool allowDotComma = false)
|
||||
/// <summary> Remove anything from a string that is not a number, optionally allowing scientific notation (<paramref name="allowScientific"/>) </summary>
|
||||
public static string TrimNumbers(this string s, bool allowDotComma = false, bool allowScientific = false)
|
||||
{
|
||||
if (!allowDotComma)
|
||||
s = Regex.Replace(s, "[^0-9]", "");
|
||||
else
|
||||
s = Regex.Replace(s, "[^.,0-9]", "");
|
||||
return s.Trim();
|
||||
if (s == null)
|
||||
return s;
|
||||
|
||||
// string og = s;
|
||||
string regex = $@"[^{(allowDotComma ? ".," : "")}0-9\-{(allowScientific ? "e" : "")}]";
|
||||
s = Regex.Replace(s, regex, "").Trim();
|
||||
// Logger.Log($"Trimmed {og} -> {s} - Pattern: {regex}", true);
|
||||
return s;
|
||||
}
|
||||
|
||||
public static int GetInt(this TextBox textbox)
|
||||
@@ -43,7 +47,8 @@ namespace Flowframes
|
||||
|
||||
try
|
||||
{
|
||||
return int.Parse(str.TrimNumbers());
|
||||
str = str.TrimNumbers(allowDotComma: false);
|
||||
return int.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -92,11 +97,11 @@ namespace Flowframes
|
||||
|
||||
public static float GetFloat(this string str)
|
||||
{
|
||||
if (str == null || str.Length < 1)
|
||||
if (str.Length < 1 || str == null)
|
||||
return 0f;
|
||||
|
||||
string num = str.TrimNumbers(true).Replace(",", ".");
|
||||
float.TryParse(num, out float value);
|
||||
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out float value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Flowframes.Main
|
||||
|
||||
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(s.outFps, s.outItsScale);
|
||||
string extraArgsIn = await FfmpegEncode.GetFfmpegExportArgsIn(s.outFps, s.outItsScale, extraData.Rotation);
|
||||
string extraArgsOut = await FfmpegEncode.GetFfmpegExportArgsOut(fpsLimit ? maxFps : new Fraction(), extraData, s.outSettings);
|
||||
|
||||
if(s.outSettings.Encoder == Enums.Encoding.Encoder.Exr)
|
||||
@@ -94,7 +94,6 @@ namespace Flowframes.Main
|
||||
extraArgsIn += " -color_trc bt709 -color_primaries bt709 -colorspace bt709";
|
||||
}
|
||||
|
||||
|
||||
if (ffplay)
|
||||
{
|
||||
bool useNutPipe = true; // TODO: Make this bool a config flag
|
||||
@@ -393,5 +392,32 @@ namespace Flowframes.Main
|
||||
Logger.Log("MergeAudio() Exception: " + e.Message, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MuxTimestamps (string vidPath)
|
||||
{
|
||||
Logger.Log($"Muxing timestamps for '{vidPath}'", hidden: true);
|
||||
var resampledTs = I.currentMediaFile.GetResampledTimestamps(I.currentMediaFile.InputTimestamps, I.currentSettings.interpFactor);
|
||||
var tsFileLines = new List<string>() { "# timecode format v2" };
|
||||
|
||||
for (int i = 0; i < (resampledTs.Count - 1); i++)
|
||||
{
|
||||
tsFileLines.Add((resampledTs[i] * 1000f).ToString("0.000000"));
|
||||
}
|
||||
|
||||
string tsFile = Path.Combine(Paths.GetSessionDataPath(), "ts.out.txt");
|
||||
File.WriteAllLines(tsFile, tsFileLines);
|
||||
string outPath = Path.ChangeExtension(vidPath, ".tmp.mkv");
|
||||
string args = $"mkvmerge --output {outPath.Wrap()} --timestamps \"0:{tsFile}\" {vidPath.Wrap()}";
|
||||
var outputMux = NUtilsTemp.OsUtils.RunCommand($"cd /D {Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir).Wrap()} && {args}");
|
||||
Logger.Log(outputMux, hidden: true);
|
||||
|
||||
// Check if file exists and is not too small (min. 80% of input file)
|
||||
if (File.Exists(outPath) && ((double)new FileInfo(outPath).Length / (double)new FileInfo(vidPath).Length) > 0.8d)
|
||||
{
|
||||
Logger.Log($"Deleting '{vidPath}' and moving '{outPath}' to '{vidPath}'", hidden: true);
|
||||
File.Delete(vidPath);
|
||||
File.Move(outPath, vidPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using static Flowframes.AvProcess;
|
||||
using Utils = Flowframes.Media.FfmpegUtils;
|
||||
using I = Flowframes.Interpolate;
|
||||
using Flowframes.Main;
|
||||
|
||||
namespace Flowframes.Media
|
||||
{
|
||||
@@ -59,6 +60,11 @@ namespace Flowframes.Media
|
||||
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" : "";
|
||||
|
||||
if (I.currentMediaFile.IsVfr)
|
||||
{
|
||||
Export.MuxTimestamps(tempPath);
|
||||
}
|
||||
|
||||
if (QuickSettingsTab.trimEnabled)
|
||||
{
|
||||
string otherStreamsName = $"otherStreams{containerExt}";
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static Flowframes.AvProcess;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Flowframes
|
||||
{
|
||||
@@ -18,8 +19,8 @@ namespace Flowframes
|
||||
public static string pngCompr = "-compression_level 3";
|
||||
|
||||
public enum MpDecSensitivity { Normal = 4, High = 20, VeryHigh = 32, Extreme = 40 }
|
||||
|
||||
public static string GetMpdecimate (int sensitivity = 4, bool wrap = true)
|
||||
|
||||
public static string GetMpdecimate(int sensitivity = 4, bool wrap = true)
|
||||
{
|
||||
string mpd = $"mpdecimate=hi=64*1024:lo=64*{sensitivity}:frac=1.0";
|
||||
return wrap ? mpd.Wrap() : mpd;
|
||||
@@ -33,7 +34,7 @@ namespace Flowframes
|
||||
return wrap ? mpd.Wrap() : mpd;
|
||||
}
|
||||
|
||||
public static int GetModulo ()
|
||||
public static int GetModulo()
|
||||
{
|
||||
if (Interpolate.currentSettings.ai.NameInternal == Implementations.flavrCuda.NameInternal)
|
||||
return 8;
|
||||
@@ -41,7 +42,7 @@ namespace Flowframes
|
||||
return Interpolate.currentSettings.outSettings.Encoder.GetInfo().Modulo;
|
||||
}
|
||||
|
||||
public static string GetPadFilter ()
|
||||
public static string GetPadFilter()
|
||||
{
|
||||
int mod = GetModulo();
|
||||
|
||||
@@ -55,7 +56,7 @@ namespace Flowframes
|
||||
{
|
||||
Logger.Log($"ConcatVideos('{Path.GetFileName(concatFile)}', '{outPath}', {looptimes})", true, false, "ffmpeg");
|
||||
|
||||
if(showLog)
|
||||
if (showLog)
|
||||
Logger.Log($"Merging videos...", false, Logger.GetLastLine().Contains("frame"));
|
||||
|
||||
IoUtils.RenameExistingFileOrDir(outPath);
|
||||
@@ -91,32 +92,35 @@ namespace Flowframes
|
||||
DeleteSource(inputFile);
|
||||
}
|
||||
|
||||
public static async Task<long> GetDurationMs(string inputFile, bool demuxInsteadOfPacketTs = false)
|
||||
public static async Task<long> GetDurationMs(string inputFile, MediaFile mediaFile, bool demuxInsteadOfPacketTs = false, bool allowDurationFromMetadata = true)
|
||||
{
|
||||
Logger.Log($"GetDuration({inputFile}) - Reading duration by checking metadata.", true, false, "ffmpeg");
|
||||
string argsMeta = $"ffprobe -v quiet -show_streams -select_streams v:0 -show_entries stream=duration {inputFile.Wrap()}";
|
||||
var outputLinesMeta = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsMeta}").SplitIntoLines();
|
||||
|
||||
foreach (string line in outputLinesMeta.Where(l => l.MatchesWildcard("*?=?*")))
|
||||
if (allowDurationFromMetadata)
|
||||
{
|
||||
var split = line.Split('=');
|
||||
string key = split[0];
|
||||
string value = split[1];
|
||||
Logger.Log($"GetDuration({inputFile}) - Reading duration by checking metadata.", true, false, "ffmpeg");
|
||||
string argsMeta = $"ffprobe -v quiet -show_streams -select_streams v:0 -show_entries stream=duration {inputFile.Wrap()}";
|
||||
var outputLinesMeta = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsMeta}").SplitIntoLines();
|
||||
|
||||
if(value.Contains("N/A"))
|
||||
continue;
|
||||
foreach (string line in outputLinesMeta.Where(l => l.MatchesWildcard("*?=?*")))
|
||||
{
|
||||
var split = line.Split('=');
|
||||
string key = split[0];
|
||||
string value = split[1];
|
||||
|
||||
if (key == "duration")
|
||||
{
|
||||
return (long)TimeSpan.FromSeconds(value.GetFloat()).TotalMilliseconds;
|
||||
}
|
||||
else if (key == "TAG:DURATION")
|
||||
{
|
||||
string[] tsSplit = value.Split(':');
|
||||
int hours = tsSplit[0].GetInt();
|
||||
int minutes = tsSplit[1].GetInt();
|
||||
float seconds = tsSplit[2].GetFloat();
|
||||
return (long)TimeSpan.FromHours(hours).Add(TimeSpan.FromMinutes(minutes)).Add(TimeSpan.FromSeconds(seconds)).TotalMilliseconds;
|
||||
if (value.Contains("N/A"))
|
||||
continue;
|
||||
|
||||
if (key == "duration")
|
||||
{
|
||||
return (long)TimeSpan.FromSeconds(value.GetFloat()).TotalMilliseconds;
|
||||
}
|
||||
else if (key == "TAG:DURATION")
|
||||
{
|
||||
string[] tsSplit = value.Split(':');
|
||||
int hours = tsSplit[0].GetInt();
|
||||
int minutes = tsSplit[1].GetInt();
|
||||
float seconds = tsSplit[2].GetFloat();
|
||||
return (long)TimeSpan.FromHours(hours).Add(TimeSpan.FromMinutes(minutes)).Add(TimeSpan.FromSeconds(seconds)).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,17 +139,81 @@ namespace Flowframes
|
||||
else
|
||||
{
|
||||
Logger.Log($"GetDuration({inputFile}) - Reading duration using packet timestamps.", true, false, "ffmpeg");
|
||||
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=dts_time -of csv=p=0 {inputFile.Wrap()}";
|
||||
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts_time -of csv=p=0 {inputFile.Wrap()}";
|
||||
var outputLinesPackets = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsPackets}").SplitIntoLines().Where(l => l.IsNotEmpty()).ToList();
|
||||
|
||||
if (outputLinesPackets == null || outputLinesPackets.Count == 0)
|
||||
return 0;
|
||||
|
||||
CheckVfr(inputFile, mediaFile);
|
||||
string lastTimestamp = outputLinesPackets.Last().Split('=').Last();
|
||||
return (long)TimeSpan.FromSeconds(lastTimestamp.GetFloat()).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckVfr (string inputFile, MediaFile mediaFile, List<string> outputLinesPackets = null)
|
||||
{
|
||||
if (mediaFile.InputTimestamps.Any())
|
||||
return;
|
||||
|
||||
Logger.Log($"Checking frame timing...", true, false, "ffmpeg");
|
||||
|
||||
if(outputLinesPackets == null)
|
||||
{
|
||||
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts_time -read_intervals \"%+120\" -of csv=p=0 {inputFile.Wrap()}";
|
||||
outputLinesPackets = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsPackets}").SplitIntoLines().Where(l => l.IsNotEmpty()).ToList();
|
||||
}
|
||||
|
||||
var timestamps = new List<float>();
|
||||
var timestampDurations = new List<float>();
|
||||
var timestampDurationsRes = new List<float>();
|
||||
|
||||
foreach (string line in outputLinesPackets)
|
||||
{
|
||||
timestamps.Add(line.GetFloat());
|
||||
}
|
||||
|
||||
timestamps = timestamps.OrderBy(x => x).ToList();
|
||||
|
||||
for (int i = 1; i < (timestamps.Count - 1); i++)
|
||||
{
|
||||
float diff = Math.Abs(timestamps[i] - timestamps[i - 1]);
|
||||
timestampDurations.Add(diff);
|
||||
|
||||
// if (diff > 0)
|
||||
// {
|
||||
// Console.WriteLine($"Duration of {timestamps.Count}: {diff * 1000f} ms ({1f / diff} FPS)");
|
||||
// }
|
||||
}
|
||||
//
|
||||
// var tsResampleTest = Interpolate.currentMediaFile.ResampleTimestamps(timestamps, 2.5f);
|
||||
//
|
||||
// for (int i = 1; i < (tsResampleTest.Count - 1); i++)
|
||||
// {
|
||||
// float diff = Math.Abs(tsResampleTest[i] - tsResampleTest[i - 1]);
|
||||
// timestampDurationsRes.Add(diff);
|
||||
//
|
||||
// if (diff > 0)
|
||||
// {
|
||||
// Console.WriteLine($"Resampled duration of {tsResampleTest.Count}: {diff * 1000f} ms ({1f / diff} FPS)");
|
||||
// }
|
||||
// }
|
||||
|
||||
mediaFile.InputTimestamps = new List<float>(timestamps);
|
||||
|
||||
float maxDeviationMs = (timestampDurations.Max() - timestampDurations.Min()) * 1000f;
|
||||
float maxDeviationPercent = ((timestampDurations.Max() / timestampDurations.Min()) * 100f) - 100;
|
||||
// float maxDeviationMsResampled = (timestampDurationsRes.Max() - timestampDurationsRes.Min()) * 1000f;
|
||||
Logger.Log($"Min ts duration: {timestampDurations.Min() * 1000f} ms - Max ts duration: {timestampDurations.Max() * 1000f} ms - Biggest deviation: {maxDeviationMs.ToString("0.##")} ms", hidden: true);
|
||||
// Logger.Log($"Resampled - Min ts duration: {timestampDurationsRes.Min() * 1000f} ms - Max ts duration: {timestampDurationsRes.Max() * 1000f} ms - Biggest deviation: {maxDeviationMsResampled.ToString("0.##")} ms", hidden: true);
|
||||
|
||||
if (maxDeviationPercent > 20f)
|
||||
{
|
||||
Logger.Log($"Max timestamp deviation is {maxDeviationPercent.ToString("0.##")}% or {maxDeviationMs} ms - Assuming VFR input!", hidden: true);
|
||||
mediaFile.IsVfr = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Fraction> GetFramerate(string inputFile, bool preferFfmpeg = false)
|
||||
{
|
||||
Logger.Log($"GetFramerate(inputFile = '{inputFile}', preferFfmpeg = {preferFfmpeg})", true, false, "ffmpeg");
|
||||
@@ -180,7 +248,7 @@ namespace Flowframes
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ffmpegEx)
|
||||
catch (Exception ffmpegEx)
|
||||
{
|
||||
Logger.Log("GetFramerate ffmpeg Error: " + ffmpegEx.Message, true, false);
|
||||
}
|
||||
@@ -207,7 +275,7 @@ namespace Flowframes
|
||||
string args = $" -v panic -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 {inputFile.Wrap()}";
|
||||
string[] outputLines = GetFfprobeOutput(args).SplitIntoLines();
|
||||
|
||||
foreach(string line in outputLines)
|
||||
foreach (string line in outputLines)
|
||||
{
|
||||
if (!line.Contains("x") || line.Trim().Length < 3)
|
||||
continue;
|
||||
|
||||
@@ -43,12 +43,17 @@ namespace Flowframes.Media
|
||||
IoUtils.TryDeleteIfExists(linksDir);
|
||||
}
|
||||
|
||||
public static async Task<string> GetFfmpegExportArgsIn(Fraction fps, float itsScale)
|
||||
public static async Task<string> GetFfmpegExportArgsIn(Fraction fps, float itsScale, int rotation = 0)
|
||||
{
|
||||
var args = new List<string>();
|
||||
|
||||
fps = fps / new Fraction(itsScale);
|
||||
args.Add($"-r {fps}");
|
||||
|
||||
if(rotation != 0)
|
||||
{
|
||||
args.Add($"-display_rotation {rotation}");
|
||||
}
|
||||
|
||||
return string.Join(" ", args);
|
||||
}
|
||||
@@ -62,7 +67,7 @@ namespace Flowframes.Media
|
||||
if (resampleFps.Float >= 0.1f)
|
||||
filters.Add($"fps={resampleFps}");
|
||||
|
||||
if (Config.GetBool(Config.Key.keepColorSpace) && extraData.HasAllValues())
|
||||
if (Config.GetBool(Config.Key.keepColorSpace) && extraData.HasAllColorValues())
|
||||
{
|
||||
Logger.Log($"Using color data: Space {extraData.colorSpace}; Primaries {extraData.colorPrimaries}; Transfer {extraData.colorTransfer}; Range {extraData.colorRange}", true, false, "ffmpeg");
|
||||
extraArgs.Add($"-colorspace {extraData.colorSpace} -color_primaries {extraData.colorPrimaries} -color_trc {extraData.colorTransfer} -color_range:v {extraData.colorRange.Wrap()}");
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Flowframes.Media
|
||||
return output.SplitIntoLines().Where(x => x.MatchesWildcard("*Stream #0:*: *: *")).Count();
|
||||
}
|
||||
|
||||
public static async Task<List<Stream>> GetStreams(string path, bool progressBar, int streamCount, Fraction? defaultFps, bool countFrames)
|
||||
public static async Task<List<Stream>> GetStreams(string path, bool progressBar, int streamCount, Fraction? defaultFps, bool countFrames, MediaFile mediaFile = null)
|
||||
{
|
||||
List<Stream> streamList = new List<Stream>();
|
||||
|
||||
@@ -71,10 +71,8 @@ namespace Flowframes.Media
|
||||
Size sar = SizeFromString(await GetFfprobeInfoAsync(path, showStreams, "sample_aspect_ratio", idx));
|
||||
Size dar = SizeFromString(await GetFfprobeInfoAsync(path, showStreams, "display_aspect_ratio", idx));
|
||||
int frameCount = countFrames ? await GetFrameCountCached.GetFrameCountAsync(path) : 0;
|
||||
FpsInfo fps = await GetFps(path, streamStr, idx, (Fraction)defaultFps, frameCount);
|
||||
VideoStream vStream = new VideoStream(lang, title, codec, codecLong, pixFmt, kbits, res, sar, dar, fps, frameCount);
|
||||
vStream.Index = idx;
|
||||
vStream.IsDefault = def;
|
||||
FpsInfo fps = await GetFps(path, streamStr, idx, (Fraction)defaultFps, frameCount, allowFpsOverride: !mediaFile.IsVfr);
|
||||
VideoStream vStream = new VideoStream(lang, title, codec, codecLong, pixFmt, kbits, res, sar, dar, fps, frameCount) { Index = idx, IsDefault = def };
|
||||
Logger.Log($"Added video stream: {vStream}", true);
|
||||
streamList.Add(vStream);
|
||||
continue;
|
||||
@@ -166,7 +164,7 @@ namespace Flowframes.Media
|
||||
return streamList;
|
||||
}
|
||||
|
||||
private static async Task<FpsInfo> GetFps(string path, string streamStr, int streamIdx, Fraction defaultFps, int frameCount)
|
||||
private static async Task<FpsInfo> GetFps(string path, string streamStr, int streamIdx, Fraction defaultFps, int frameCount, bool allowFpsOverride = false)
|
||||
{
|
||||
if (path.IsConcatFile())
|
||||
return new FpsInfo(defaultFps);
|
||||
@@ -194,7 +192,7 @@ namespace Flowframes.Media
|
||||
|
||||
float fpsEstTolerance = GetFpsEstimationTolerance(durationMs);
|
||||
|
||||
if (Math.Abs(fps.Float - calculatedFps) > fpsEstTolerance)
|
||||
if (allowFpsOverride && Math.Abs(fps.Float - calculatedFps) > fpsEstTolerance)
|
||||
{
|
||||
Logger.Log($"Detected FPS {fps} is not within tolerance (+-{fpsEstTolerance}) of calculated FPS ({calculatedFps}), using estimated FPS.", true);
|
||||
info.Fps = new Fraction(calculatedFps); // Change true FPS to the estimated FPS if the estimate does not match the specified FPS
|
||||
@@ -206,7 +204,7 @@ namespace Flowframes.Media
|
||||
return new FpsInfo(await IoUtils.GetVideoFramerate(path));
|
||||
}
|
||||
|
||||
private static float GetFpsEstimationTolerance (long videoDurationMs)
|
||||
private static float GetFpsEstimationTolerance(long videoDurationMs)
|
||||
{
|
||||
if (videoDurationMs < 300) return 5.0f;
|
||||
if (videoDurationMs < 1000) return 2.5f;
|
||||
|
||||
@@ -374,7 +374,7 @@ namespace Flowframes.Os
|
||||
IoUtils.CreateDir(outPath);
|
||||
Process rifeNcnnVs = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
|
||||
string avDir = Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
|
||||
string pipedTargetArgs = $"{Path.Combine(avDir, "ffmpeg").Wrap()} -y {await Export.GetPipedFfmpegCmd(rt)}";
|
||||
string pipedTargetArgs = $"{Path.Combine(avDir, "ffmpeg").Wrap()} -loglevel warning -stats -y {await Export.GetPipedFfmpegCmd(rt)}";
|
||||
string pkgDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
|
||||
int gpuId = Config.Get(Config.Key.ncnnGpus).Split(',')[0].GetInt();
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Flowframes.Os
|
||||
long srcTrimStartFrame = trim ? (long)(Math.Round(FormatUtils.TimestampToMs(QuickSettingsTab.trimStart) / 1000f * s.InterpSettings.inFps.Float)) : 0;
|
||||
long srcTrimEndFrame = trim && QuickSettingsTab.doTrimEnd ? (long)(Math.Round(FormatUtils.TimestampToMs(QuickSettingsTab.trimEnd) / 1000f * s.InterpSettings.inFps.Float)) - 1 : frameCount - 1;
|
||||
|
||||
if(trim)
|
||||
if (trim)
|
||||
frameCount = srcTrimEndFrame - srcTrimStartFrame;
|
||||
|
||||
int endDupeCount = s.Factor.RoundToInt() - 1;
|
||||
@@ -78,7 +78,7 @@ namespace Flowframes.Os
|
||||
}
|
||||
|
||||
if (trim)
|
||||
l.Add($"clip = clip.std.Trim({srcTrimStartFrame}, {srcTrimEndFrame})");
|
||||
l.Add($"clip = clip.std.Trim({srcTrimStartFrame}, {srcTrimEndFrame}) # Trim");
|
||||
|
||||
l.Add($"");
|
||||
|
||||
@@ -100,7 +100,9 @@ namespace Flowframes.Os
|
||||
outFps = Interpolate.currentMediaFile.VideoStreams.First().FpsInfo.SpecifiedFps * s.Factor;
|
||||
}
|
||||
|
||||
l.Add($"clip = core.rife.RIFE(clip, fps_num={outFps.Numerator}, fps_den={outFps.Denominator}, model_path={mdlPath}, gpu_id={s.GpuId}, gpu_thread={s.GpuThreads}, tta={s.Tta}, uhd={s.Uhd}, sc={sc})"); // Interpolate
|
||||
Fraction factorFrac = new Fraction(s.Factor);
|
||||
string interpStr = Interpolate.currentMediaFile.IsVfr ? $"factor_num={factorFrac.Numerator}, factor_den={factorFrac.Denominator}" : $"fps_num={outFps.Numerator}, fps_den={outFps.Denominator}";
|
||||
l.Add($"clip = core.rife.RIFE(clip, {interpStr}, model_path={mdlPath}, gpu_id={s.GpuId}, gpu_thread={s.GpuThreads}, tta={s.Tta}, uhd={s.Uhd}, sc={sc})"); // Interpolate
|
||||
|
||||
if (s.Dedupe && !s.Realtime)
|
||||
l.Add(GetRedupeLines(s));
|
||||
@@ -113,21 +115,23 @@ namespace Flowframes.Os
|
||||
{
|
||||
if (s.Loop)
|
||||
{
|
||||
l.Add($"clip = clip.std.Trim(0, {targetFrameCountMatchDuration - 1})"); // -1 because we use index, not count
|
||||
l.Add($"clip = clip.std.Trim(0, {targetFrameCountMatchDuration - 1}) # Trim, loop enabled"); // -1 because we use index, not count
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!s.MatchDuration)
|
||||
l.Add($"clip = clip.std.Trim(0, {targetFrameCountTrue - 1})"); // -1 because we use index, not count
|
||||
l.Add($"clip = clip.std.Trim(0, {targetFrameCountTrue - 1}) # Trim, loop disabled, duration matching disabled"); // -1 because we use index, not count
|
||||
}
|
||||
}
|
||||
|
||||
if(s.Realtime && s.Loop)
|
||||
if (s.Realtime && s.Loop)
|
||||
l.AddRange(new List<string> { $"clip = clip.std.Loop(0)", "" }); // Can't loop piped video so we loop it before piping it to ffplay
|
||||
|
||||
if(s.Realtime && s.Osd)
|
||||
if (s.Realtime && s.Osd)
|
||||
l.Add(GetOsdLines());
|
||||
|
||||
l.Add(Debugger.IsAttached ? "clip = core.text.Text(clip, str(len(clip)), alignment=4)" : "");
|
||||
l.Add(Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=1)" : "");
|
||||
l.Add($"clip.set_output()"); // Set output
|
||||
l.Add("");
|
||||
|
||||
@@ -206,7 +210,7 @@ namespace Flowframes.Os
|
||||
s += " for i in frameList:\n";
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1) # Dedupe trim\n";
|
||||
s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=4)\n" : "";
|
||||
s += "\n";
|
||||
|
||||
@@ -226,7 +230,7 @@ namespace Flowframes.Os
|
||||
s += " if(i < clip.num_frames):\n";
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1) # Redupe trim\n";
|
||||
s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=1)\n" : "";
|
||||
s += "\n";
|
||||
|
||||
@@ -275,9 +279,9 @@ namespace Flowframes.Os
|
||||
{
|
||||
int seekStep = 10;
|
||||
|
||||
if(videoLengthSeconds > 2 * 60) seekStep = 20;
|
||||
if(videoLengthSeconds > 5 * 60) seekStep = 30;
|
||||
if(videoLengthSeconds > 15 * 60) seekStep = 60;
|
||||
if (videoLengthSeconds > 2 * 60) seekStep = 20;
|
||||
if (videoLengthSeconds > 5 * 60) seekStep = 30;
|
||||
if (videoLengthSeconds > 15 * 60) seekStep = 60;
|
||||
|
||||
return seekStep;
|
||||
}
|
||||
|
||||
1179
patrons.csv
1179
patrons.csv
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user