From 14078f0466a0255e7bb10148657af91130d5ee30 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 4 Aug 2026 01:04:07 +0000 Subject: [PATCH] Close try/catch gap around appsink pull_sample() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standalone gst-launch testing confirmed appsink-as-playbin-video-sink works fine outside the extension (reaches PLAYING, negotiates caps, runs to completion), narrowing the "no frames received" symptom down to something inside our own new-sample handling in the shell process. _onNewSample()'s try/catch didn't cover the sink.pull_sample() call itself — if that specific call throws when invoked as a GJS signal callback, the exception bypassed our logging entirely, falling back to GJS's generic uncaught-exception path (which, like the openPreferences case, may not mention "benthicbloom" and gets missed by a filtered grep). Now the whole handler is covered, logs when the signal fires for the first time, logs a null pull_sample() or failed buffer.map() explicitly, and the watchdog reports the new-sample fire count so we can tell "signal never fired" from "signal fired but processing failed" on the next test. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8 --- lib/liveWallpaper.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/liveWallpaper.js b/lib/liveWallpaper.js index 566192f..fe69289 100644 --- a/lib/liveWallpaper.js +++ b/lib/liveWallpaper.js @@ -50,6 +50,7 @@ export class LiveWallpaperManager { this._upowerSignalId = 0; this._frameCount = 0; + this._newSampleCallCount = 0; this._noFrameWatchdogId = 0; } @@ -162,13 +163,16 @@ export class LiveWallpaperManager { this._logger.debug(`playbin.set_state(PLAYING) returned ${stateChangeResult}`); this._frameCount = 0; + this._newSampleCallCount = 0; this._noFrameWatchdogId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 4, () => { this._noFrameWatchdogId = 0; if (this._active && this._frameCount === 0) { this._logger.warn( - 'Live wallpaper: no frames received 4s after starting playback. ' + - 'The pipeline likely stalled during caps negotiation or preroll without ' + - 'raising an error — check for GStreamer WARNING messages logged above.'); + `Live wallpaper: no frames received 4s after starting playback ` + + `(new-sample signal fired ${this._newSampleCallCount} times). ` + + 'If that count is 0, the signal itself never fired (check for a stalled ' + + 'pipeline above); if it is nonzero, pull_sample()/buffer processing is failing ' + + '— check for a "Failed to process live wallpaper frame" error above.'); } return GLib.SOURCE_REMOVE; }); @@ -185,19 +189,27 @@ export class LiveWallpaperManager { _onNewSample(sink) { const Gst = this._Gst; - const sample = sink.pull_sample(); - if (!sample) - return Gst.FlowReturn.OK; + this._newSampleCallCount = (this._newSampleCallCount ?? 0) + 1; + if (this._newSampleCallCount === 1) + this._logger.debug('Live wallpaper: new-sample signal fired for the first time'); try { + const sample = sink.pull_sample(); + if (!sample) { + this._logger.debug('Live wallpaper: pull_sample() returned no sample'); + return Gst.FlowReturn.OK; + } + const buffer = sample.get_buffer(); const structure = sample.get_caps().get_structure(0); const [, width] = structure.get_int('width'); const [, height] = structure.get_int('height'); const [ok, mapInfo] = buffer.map(Gst.MapFlags.READ); - if (!ok) + if (!ok) { + this._logger.debug('Live wallpaper: buffer.map() failed'); return Gst.FlowReturn.OK; + } try { this._updateFrame(mapInfo.data, width, height); @@ -210,7 +222,7 @@ export class LiveWallpaperManager { buffer.unmap(mapInfo); } } catch (e) { - this._logger.error(e, 'Failed to process live wallpaper frame'); + this._logger.error(e, 'Failed to process live wallpaper frame (new-sample handler)'); } return Gst.FlowReturn.OK;