From 6f0b3b39a34ade6f15827fcd1eab1737a1db88da Mon Sep 17 00:00:00 2001 From: N00MKRAD <61149547+n00mkrad@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:15:46 +0200 Subject: [PATCH] Avoid slow duration check if duration metadata for video stream is avail. --- CodeLegacy/Media/FfmpegCommands.cs | 67 ++++++++++++++++++++++++------ CodeLegacy/Media/FfmpegUtils.cs | 2 +- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/CodeLegacy/Media/FfmpegCommands.cs b/CodeLegacy/Media/FfmpegCommands.cs index 8ee7092..5255013 100644 --- a/CodeLegacy/Media/FfmpegCommands.cs +++ b/CodeLegacy/Media/FfmpegCommands.cs @@ -91,17 +91,59 @@ namespace Flowframes DeleteSource(inputFile); } - public static async Task GetDurationMs(string inputFile) + public static async Task GetDurationMs(string inputFile, bool demuxInsteadOfPacketTs = false) { - Logger.Log($"GetDuration({inputFile}) - Reading duration by demuxing.", true, false, "ffmpeg"); - string args = $"ffmpeg -loglevel panic -stats -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null NUL"; - var outputLines = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {args}").SplitIntoLines().Where(l => l.IsNotEmpty() && l.MatchesWildcard("*time=* *")); + 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 (outputLines == null || outputLines.Count() == 0) - return 0; + foreach (string line in outputLinesMeta.Where(l => l.MatchesWildcard("*?=?*"))) + { + var split = line.Split('='); + string key = split[0]; + string value = split[1]; - string output = outputLines.Last().Split("time=")[1].Split(" ")[0]; - return (long)TimeSpan.ParseExact(output, @"hh\:mm\:ss\.ff", null).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; + } + } + + if (demuxInsteadOfPacketTs) + { + Logger.Log($"GetDuration({inputFile}) - Reading duration by demuxing.", true, false, "ffmpeg"); + string argsDemux = $"ffmpeg -loglevel panic -stats -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null NUL"; + var outputLinesDemux = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsDemux}").SplitIntoLines().Where(l => l.IsNotEmpty() && l.MatchesWildcard("*time=* *")); + + if (outputLinesDemux == null || outputLinesDemux.Count() == 0) + return 0; + + string output = outputLinesDemux.Last().Split("time=")[1].Split(" ")[0]; + return (long)TimeSpan.ParseExact(output, @"hh\:mm\:ss\.ff", null).TotalMilliseconds; + } + 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()}"; + var outputLinesPackets = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsPackets}").SplitIntoLines().Where(l => l.IsNotEmpty()).ToList(); + + if (outputLinesPackets == null || outputLinesPackets.Count == 0) + return 0; + + string lastTimestamp = outputLinesPackets.Last().Split('=').Last(); + return (long)TimeSpan.FromSeconds(lastTimestamp.GetFloat()).TotalMilliseconds; + } } public static async Task GetFramerate(string inputFile, bool preferFfmpeg = false) @@ -208,14 +250,13 @@ namespace Flowframes public static async Task ReadFrameCountFfprobePacketCount(string filePath) { - FfprobeSettings settings = new FfprobeSettings() { Args = $"-select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {filePath.Wrap()}", LoggingMode = LogMode.Hidden, LogLevel = "error" }; - string output = await RunFfprobe(settings); - string[] lines = output.SplitIntoLines().Where(x => !string.IsNullOrWhiteSpace(x)).ToArray(); + string args = $"ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {filePath.Wrap()}"; + var outputLines = (await Task.Run(() => NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {args}"))).SplitIntoLines().Where(l => l.IsNotEmpty()); - if (lines == null || lines.Length < 1) + if (outputLines == null || !outputLines.Any()) return 0; - return lines.Last().GetInt(); + return outputLines.Last().GetInt(); } public static async Task ReadFrameCountFfprobe(string filePath) diff --git a/CodeLegacy/Media/FfmpegUtils.cs b/CodeLegacy/Media/FfmpegUtils.cs index a04bbde..a9630a6 100644 --- a/CodeLegacy/Media/FfmpegUtils.cs +++ b/CodeLegacy/Media/FfmpegUtils.cs @@ -178,7 +178,7 @@ namespace Flowframes.Media var ffprobeFps = new Fraction(await GetFfprobeInfoAsync(path, showStreams, "r_frame_rate", streamIdx)); var ffprobeFpsAvg = new Fraction(await GetFfprobeInfoAsync(path, showStreams, "avg_frame_rate", streamIdx)); long durationMs = Interpolate.currentMediaFile.DurationMs; - float calculatedFps = (float)Math.Round(frameCount / (durationMs / 1000f), 5); + float calculatedFps = (float)Math.Round(frameCount / (durationMs / 1000f), 2); Fraction fps = ffprobeFps.Float > 0f ? ffprobeFps : new Fraction(ffmpegFps); Fraction avgFps = ffprobeFpsAvg.Float > 0f ? ffprobeFpsAvg : new Fraction(ffmpegTbr);