[vlc-devel] [RFC] VLC 3.0.23: preserve resume progress across interrupted laptop sessions
Kevin Du
kevin7du at gmail.com
Sun Sep 20 15:12:38 UTC 2026
Hello VLC developers,
I use VLC 3.0.23 Vetinari with "Continue playback?" set to "Always".
After putting my laptop to sleep and later reopening it elsewhere,
playback does not consistently resume from where I stopped. I would
like VLC to preserve my position automatically without creating
bookmarks or remembering to quit VLC before sleep.
Could you consider improving resume persistence and the meaning of "Always"?
An AI coding assistant helped inspect the official 3.0.23 source and
prepare the attached RFC patch. The source has a potential persistence
gap: InputManager::delInput() captures the playback position, while
RecentsMRL::setTime() only changes the in-memory list. This does not
establish the root cause of my sleep problem. The exact OS version,
media location, whether VLC remains open, and whether the process
restarts during the incident still need confirmation.
The prototype:
- Checkpoints progress about every ten seconds while active position
events arrive.
- Saves on pause and normal input teardown, flushing changed history
through QSettings::sync().
- Honors disabled recent history and skips unchanged values.
- Lets "Always" retain any positive unfinished position in a local
file with a known duration, including outside the current 5%-95% range
and short clips. Existing heuristics remain for the other modes.
Validation is limited to source review, git diff --check, and git
apply --check against the original 3.0.23 files. It has NOT been
compiled, installed, or tested in a running VLC instance. Please treat
it as a discussion prototype, not a verified or merge-ready fix. It
targets 3.0.23's Qt implementation; master uses different
player/media-library code and would need a separate implementation or
port.
Suggested validation includes pause/sleep/wake, hibernate, normal
exit/reopen, abrupt termination after a checkpoint, 2%/98% positions,
short clips, natural end-of-media, Ask/Always/Never, privacy/history
clearing, seeks while paused, repeat/playlists, input errors, multiple
instances, and storage latency. An explicit suspend hook or
asynchronous settings persistence may be preferable after review.
I understand code contributions go through VideoLAN GitLab merge
requests. I am sending this enhancement request and prototype to the
developer discussion list first to ask whether the behavior and
approach are appropriate. Please let me know if there is an existing
issue or a preferred next step.
Thank you,
Kevin
-------------- next part --------------
diff --git a/modules/gui/qt/input_manager.cpp b/modules/gui/qt/input_manager.cpp
index 4a3f689..bcf18f5 100644
--- a/modules/gui/qt/input_manager.cpp
+++ b/modules/gui/qt/input_manager.cpp
@@ -140,6 +140,7 @@ void InputManager::setInput( input_thread_t *_p_input )
playlist_Unlock( THEPL );
free( uri );
}
+ resumeSaveTimer.start();
}
else
{
@@ -158,19 +159,8 @@ void InputManager::delInput()
if( !p_input ) return;
msg_Dbg( p_intf, "IM: Deleting the input" );
- /* Save time / position */
- char *uri = input_item_GetURI( p_item );
- if( uri != NULL ) {
- float f_pos = var_GetFloat( p_input , "position" );
- int64_t i_time = -1;
-
- if( f_pos >= 0.05f && f_pos <= 0.95f
- && var_GetInteger( p_input, "length" ) >= 60 * CLOCK_FREQ )
- i_time = var_GetInteger( p_input, "time");
-
- RecentsMRL::getInstance( p_intf )->setTime( qfu(uri), i_time );
- free(uri);
- }
+ SavePlaybackPosition();
+ resumeSaveTimer.invalidate();
delCallbacks();
i_old_playing_status = END_S;
@@ -433,6 +423,40 @@ void InputManager::UpdatePosition()
int64_t i_time = var_GetInteger( p_input , "time");
float f_pos = var_GetFloat( p_input , "position" );
emit positionUpdated( f_pos, i_time, i_length / CLOCK_FREQ );
+ const int state = var_GetInteger( p_input, "state" );
+ if( resumeSaveTimer.isValid() && resumeSaveTimer.elapsed() >= 10000
+ && ( state == PLAYING_S || state == PAUSE_S ) )
+ {
+ SavePlaybackPosition();
+ resumeSaveTimer.restart();
+ }
+}
+
+void InputManager::SavePlaybackPosition()
+{
+ if( !p_input || !p_item || p_item->i_type != ITEM_TYPE_FILE )
+ return;
+
+ char *uri = input_item_GetURI( p_item );
+ if( uri == NULL )
+ return;
+
+ const float pos = var_GetFloat( p_input, "position" );
+ const int64_t length = var_GetInteger( p_input, "length" );
+ const int64_t time = var_GetInteger( p_input, "time" );
+ const bool always = var_InheritInteger( p_intf, "qt-continue" ) == 2;
+ int64_t savedTime = -1;
+
+ /* Keep the prompt heuristics for Ask, but honor Always for any
+ * unfinished file with a known duration. Completed files restart. */
+ if( always ? ( length > 0 && time > 0 && time < length
+ && pos > 0.f && pos < 1.f )
+ : ( pos >= 0.05f && pos <= 0.95f
+ && length >= 60 * CLOCK_FREQ ) )
+ savedTime = time;
+
+ RecentsMRL::getInstance( p_intf )->setTime( qfu(uri), savedTime );
+ free( uri );
}
void InputManager::UpdateNavigation()
@@ -484,6 +508,11 @@ void InputManager::UpdateStatus()
if( i_old_playing_status != state )
{
i_old_playing_status = state;
+ if( state == PAUSE_S && resumeSaveTimer.isValid() )
+ {
+ SavePlaybackPosition();
+ resumeSaveTimer.restart();
+ }
emit playingStatusChanged( state );
}
}
diff --git a/modules/gui/qt/input_manager.hpp b/modules/gui/qt/input_manager.hpp
index 6170e71..fade020 100644
--- a/modules/gui/qt/input_manager.hpp
+++ b/modules/gui/qt/input_manager.hpp
@@ -37,6 +37,7 @@
#include <QObject>
#include <QEvent>
+#include <QElapsedTimer>
class QSignalMapper;
enum { NORMAL, /* loop: 0, repeat: 0 */
@@ -157,6 +158,7 @@ private:
float f_cache;
bool b_video;
vlc_tick_t timeA, timeB;
+ QElapsedTimer resumeSaveTimer;
void customEvent( QEvent * ) override;
@@ -168,6 +170,7 @@ private:
void UpdateStatus();
void UpdateNavigation();
void UpdatePosition();
+ void SavePlaybackPosition();
void UpdateTeletext();
void UpdateArt();
void UpdateInfo();
diff --git a/modules/gui/qt/recents.cpp b/modules/gui/qt/recents.cpp
index 6e98e96..d67ea02 100644
--- a/modules/gui/qt/recents.cpp
+++ b/modules/gui/qt/recents.cpp
@@ -29,6 +29,7 @@
#include "util/qt_dirs.hpp"
#include <QStringList>
+#include <QSettings>
#include <QRegularExpression>
#include <QSignalMapper>
@@ -175,9 +176,21 @@ int RecentsMRL::time( const QString &mrl )
void RecentsMRL::setTime( const QString &mrl, const int64_t time )
{
+ if( !isActive )
+ return;
+
int i_index = recents.indexOf( mrl );
if( i_index != -1 )
- times[i_index] = QString::number( time / 1000 );
+ {
+ const QString value = QString::number( time / 1000 );
+ if( times.value( i_index ) == value )
+ return;
+
+ times[i_index] = value;
+ save();
+ /* Do not depend on normal shutdown to flush the checkpoint. */
+ getSettings()->sync();
+ }
}
int Open::openMRL( intf_thread_t *p_intf,
More information about the vlc-devel
mailing list