From 288c8631f0e710afd6946b27b50bee11d28268f7 Mon Sep 17 00:00:00 2001 From: McSchneidar <262792195+McSchneidar@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:41:39 +0200 Subject: [PATCH] Loop videos seamlessly at the last decoded frame (#939) Looped videos wrapped at the container duration, which is often longer than the last frame the decoder delivers. The picture froze for the difference before restarting. Track the actual end of the video stream and wrap there instead. --- Vocaluxe/Lib/Video/Acinerella/CDecoder.cs | 7 ++++--- Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs index 19003688..982e6d80 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs @@ -124,12 +124,13 @@ namespace Vocaluxe.Lib.Video.Acinerella if (Loop) { time = _LoopTime + _LoopTimer.ElapsedMilliseconds / 1000f; - if (time >= Length) + var loopLength = _Thread.LoopLength; + if (time >= loopLength) { do { - time -= Length; - } while (time >= Length); + time -= loopLength; + } while (time >= loopLength); _LoopTime = time; _LoopTimer.Restart(); diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs index 0b5e4213..e6c31ba6 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs @@ -62,6 +62,7 @@ namespace Vocaluxe.Lib.Video.Acinerella private bool _DropSeekEnabled = true; // Used to fallback to frame skipping if seek is failing once on this file public float Length { get; private set; } + public float LoopLength { get; private set; } public bool Loop { get; set; } public CDecoderThread() @@ -81,6 +82,7 @@ namespace Vocaluxe.Lib.Video.Acinerella var instance = (SACInstance)Marshal.PtrToStructure(_Instance, typeof(SACInstance)); Length = instance.Info.Duration / 1000f; + LoopLength = Length; var ok = instance.Opened && Length > 0.001f; _DropSeekEnabled = true; if (ok) @@ -300,7 +302,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } //If we don't the Length might be inaccurate (e.g. last frame ends at 19.98 but Length=20) - if (time >= Length - 2 * _FrameDuration) + if (time >= LoopLength - 2 * _FrameDuration) { return; } @@ -432,6 +434,11 @@ namespace Vocaluxe.Lib.Video.Acinerella { if (Loop) { + if (!dropFrame && _LastDecodedTime > 0) + { + LoopLength = Math.Min(Length, _LastDecodedTime + _FrameDuration); + } + RequestTime = _LoopedRequestTime; _Skip(); }