Fix EOS loop freeze and playback rate not sticking on start
Throttle the EOS loop-restart seek so a fast-looping clip can't fire back-to-back blocking flush seeks on gnome-shell's main loop, which was severe enough to freeze the whole desktop. Also apply the configured playback rate immediately on start instead of only from the first loop restart. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+56
-6
@@ -78,6 +78,8 @@ export class LiveWallpaperManager {
|
|||||||
|
|
||||||
this._settingsSignals = [];
|
this._settingsSignals = [];
|
||||||
this._busWatchId = 0;
|
this._busWatchId = 0;
|
||||||
|
this._eosSeekTimeoutId = 0;
|
||||||
|
this._lastEosSeekTimeMs = 0;
|
||||||
this._monitorsChangedId = 0;
|
this._monitorsChangedId = 0;
|
||||||
this._fullscreenChangedId = 0;
|
this._fullscreenChangedId = 0;
|
||||||
this._upowerProxy = null;
|
this._upowerProxy = null;
|
||||||
@@ -277,6 +279,11 @@ export class LiveWallpaperManager {
|
|||||||
this._applyMute();
|
this._applyMute();
|
||||||
const stateChangeResult = this._playbin.set_state(Gst.State.PLAYING);
|
const stateChangeResult = this._playbin.set_state(Gst.State.PLAYING);
|
||||||
this._logger.debug(`playbin.set_state(PLAYING) returned ${stateChangeResult}`);
|
this._logger.debug(`playbin.set_state(PLAYING) returned ${stateChangeResult}`);
|
||||||
|
// set_state(PLAYING) alone always starts at native rate 1.0 —
|
||||||
|
// without this, a configured custom speed only ever took effect
|
||||||
|
// once the first EOS loop-restart seek ran.
|
||||||
|
if (this._settings.get_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE) !== 1.0)
|
||||||
|
this._seekToLoopStart();
|
||||||
|
|
||||||
this._frameCount = 0;
|
this._frameCount = 0;
|
||||||
this._pollCount = 0;
|
this._pollCount = 0;
|
||||||
@@ -399,13 +406,9 @@ export class LiveWallpaperManager {
|
|||||||
_onBusMessage(message) {
|
_onBusMessage(message) {
|
||||||
const Gst = this._Gst;
|
const Gst = this._Gst;
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case Gst.MessageType.EOS: {
|
case Gst.MessageType.EOS:
|
||||||
const rate = this._settings.get_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE);
|
this._loopPlayback();
|
||||||
this._playbin.seek(
|
|
||||||
rate, Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
|
|
||||||
Gst.SeekType.SET, 0, Gst.SeekType.NONE, -1);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Gst.MessageType.ERROR: {
|
case Gst.MessageType.ERROR: {
|
||||||
const [error, debug] = message.parse_error();
|
const [error, debug] = message.parse_error();
|
||||||
this._logger.error(error, `Live wallpaper playback error (${debug ?? 'no debug info'})`);
|
this._logger.error(error, `Live wallpaper playback error (${debug ?? 'no debug info'})`);
|
||||||
@@ -426,6 +429,48 @@ export class LiveWallpaperManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts playback at EOS. `seek()` with GST_SEEK_FLAG_FLUSH blocks
|
||||||
|
* the calling thread until the pipeline flushes — and since this whole
|
||||||
|
* extension runs on gnome-shell's own main loop, that blocks the entire
|
||||||
|
* desktop for as long as it takes. At rate 1.0 that's one blocking call
|
||||||
|
* per full playthrough, tolerable; at higher rates a short clip loops
|
||||||
|
* (and EOS fires) many times a second, turning it into a sustained
|
||||||
|
* back-to-back stall that reads as a hard freeze. Throttling to at most
|
||||||
|
* one restart per EOS_SEEK_MIN_INTERVAL_MS gives the main loop room to
|
||||||
|
* breathe between them.
|
||||||
|
*/
|
||||||
|
_loopPlayback() {
|
||||||
|
const nowMs = GLib.get_monotonic_time() / 1000;
|
||||||
|
const elapsedMs = nowMs - this._lastEosSeekTimeMs;
|
||||||
|
const minIntervalMs = 150;
|
||||||
|
|
||||||
|
if (elapsedMs >= minIntervalMs) {
|
||||||
|
this._seekToLoopStart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._eosSeekTimeoutId)
|
||||||
|
return;
|
||||||
|
this._eosSeekTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, minIntervalMs - elapsedMs, () => {
|
||||||
|
this._eosSeekTimeoutId = 0;
|
||||||
|
this._seekToLoopStart();
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_seekToLoopStart() {
|
||||||
|
if (!this._playbin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._lastEosSeekTimeMs = GLib.get_monotonic_time() / 1000;
|
||||||
|
const Gst = this._Gst;
|
||||||
|
const rate = this._settings.get_double(SettingsKey.LIVE_WALLPAPER_PLAYBACK_RATE);
|
||||||
|
this._playbin.seek(
|
||||||
|
rate, Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
|
||||||
|
Gst.SeekType.SET, 0, Gst.SeekType.NONE, -1);
|
||||||
|
}
|
||||||
|
|
||||||
_stateName(state) {
|
_stateName(state) {
|
||||||
const Gst = this._Gst;
|
const Gst = this._Gst;
|
||||||
return Object.keys(Gst.State).find(name => Gst.State[name] === state) ?? String(state);
|
return Object.keys(Gst.State).find(name => Gst.State[name] === state) ?? String(state);
|
||||||
@@ -610,6 +655,11 @@ export class LiveWallpaperManager {
|
|||||||
this._pollTimeoutId = 0;
|
this._pollTimeoutId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._eosSeekTimeoutId) {
|
||||||
|
GLib.source_remove(this._eosSeekTimeoutId);
|
||||||
|
this._eosSeekTimeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._playbin) {
|
if (this._playbin) {
|
||||||
const Gst = this._Gst;
|
const Gst = this._Gst;
|
||||||
const bus = this._playbin.get_bus();
|
const bus = this._playbin.get_bus();
|
||||||
|
|||||||
Reference in New Issue
Block a user