Build the live wallpaper sink bin explicitly instead of via string parsing
User testing pinned the bug precisely: our try/catch is airtight now,
and the diagnostics showed "new-sample signal fired 0 times" — the
appsink genuinely never receives a single sample inside gnome-shell's
process, while an equivalent pipeline (videoconvert ! videoscale !
RGBA caps ! appsink) played correctly standalone via gst-launch-1.0.
Replaces Gst.parse_bin_from_description()'s gst-launch mini-language
parsing (with its automatic unlinked-pad ghosting) with explicit
ElementFactory.make() + Bin.add() + element.link() + a manually
created GhostPad. This removes the string-parsing path as a variable
entirely and, importantly, surfaces any link() failure as a thrown
error instead of failing silently, which the old code path had no way
to report even if that patch were the whole cachet of the problem.
Also fixes a related warning ("Trying to dispose element ..., but it
is in PLAYING instead of the NULL state") from _stop() dropping the
playbin reference immediately after requesting the NULL state, before
the (async) transition actually completed — now waits briefly via
get_state() for it to settle first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
This commit is contained in:
+66
-6
@@ -128,6 +128,61 @@ export class LiveWallpaperManager {
|
||||
this._start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds videoconvert ! videoscale ! capsfilter(RGBA) ! appsink using
|
||||
* explicit element creation, linking, and a ghost pad, rather than
|
||||
* Gst.parse_bin_from_description()'s gst-launch mini-language. Both
|
||||
* are meant to be equivalent, but a user's testing showed the
|
||||
* string-parsed version's appsink never fired 'new-sample' even once
|
||||
* inside gnome-shell's process while an equivalent standalone
|
||||
* gst-launch-1.0 pipeline worked fine — this removes the string
|
||||
* parsing (and its automatic ghost-pad detection) as a variable, and
|
||||
* surfaces link() failures explicitly instead of failing silently.
|
||||
*/
|
||||
_buildSinkBin() {
|
||||
const Gst = this._Gst;
|
||||
|
||||
const videoconvert = Gst.ElementFactory.make('videoconvert', 'benthicbloom-convert');
|
||||
const videoscale = Gst.ElementFactory.make('videoscale', 'benthicbloom-scale');
|
||||
const capsfilter = Gst.ElementFactory.make('capsfilter', 'benthicbloom-capsfilter');
|
||||
const appsink = Gst.ElementFactory.make('appsink', 'benthicbloom-appsink');
|
||||
|
||||
if (!videoconvert || !videoscale || !capsfilter || !appsink) {
|
||||
throw new Error(
|
||||
'Failed to create one or more GStreamer elements ' +
|
||||
'(videoconvert/videoscale/capsfilter/appsink) — a required plugin is likely missing');
|
||||
}
|
||||
|
||||
capsfilter.set_property('caps', Gst.Caps.from_string('video/x-raw,format=RGBA'));
|
||||
appsink.set_property('emit-signals', true);
|
||||
appsink.set_property('max-buffers', 2);
|
||||
appsink.set_property('drop', true);
|
||||
appsink.set_property('sync', true);
|
||||
|
||||
const sinkBin = new Gst.Bin({name: 'benthicbloom-sinkbin'});
|
||||
sinkBin.add(videoconvert);
|
||||
sinkBin.add(videoscale);
|
||||
sinkBin.add(capsfilter);
|
||||
sinkBin.add(appsink);
|
||||
|
||||
if (!videoconvert.link(videoscale))
|
||||
throw new Error('Failed to link videoconvert -> videoscale');
|
||||
if (!videoscale.link(capsfilter))
|
||||
throw new Error('Failed to link videoscale -> capsfilter');
|
||||
if (!capsfilter.link(appsink))
|
||||
throw new Error('Failed to link capsfilter -> appsink');
|
||||
|
||||
const sinkPad = videoconvert.get_static_pad('sink');
|
||||
const ghostPad = Gst.GhostPad.new('sink', sinkPad);
|
||||
if (!ghostPad)
|
||||
throw new Error('Failed to create ghost pad for live wallpaper sink bin');
|
||||
ghostPad.set_active(true);
|
||||
sinkBin.add_pad(ghostPad);
|
||||
|
||||
this._appsink = appsink;
|
||||
return sinkBin;
|
||||
}
|
||||
|
||||
_start() {
|
||||
const path = this._settings.get_string(SettingsKey.LIVE_WALLPAPER_PATH);
|
||||
if (!this._available || !path)
|
||||
@@ -143,11 +198,7 @@ export class LiveWallpaperManager {
|
||||
Main.layoutManager._backgroundGroup.add_child(this._actor);
|
||||
this._layoutActor();
|
||||
|
||||
const sinkBin = Gst.parse_bin_from_description(
|
||||
'videoconvert ! videoscale ! video/x-raw,format=RGBA ! ' +
|
||||
'appsink name=benthicbloom_sink emit-signals=true max-buffers=2 drop=true sync=true',
|
||||
true);
|
||||
this._appsink = sinkBin.get_by_name('benthicbloom_sink');
|
||||
const sinkBin = this._buildSinkBin();
|
||||
this._appsink.connect('new-sample', sink => this._onNewSample(sink));
|
||||
|
||||
this._playbin = Gst.ElementFactory.make('playbin', 'benthicbloom-live-wallpaper');
|
||||
@@ -381,13 +432,22 @@ export class LiveWallpaperManager {
|
||||
}
|
||||
|
||||
if (this._playbin) {
|
||||
const Gst = this._Gst;
|
||||
const bus = this._playbin.get_bus();
|
||||
if (this._busWatchId) {
|
||||
bus.disconnect(this._busWatchId);
|
||||
this._busWatchId = 0;
|
||||
}
|
||||
bus.remove_signal_watch();
|
||||
this._playbin.set_state(this._Gst.State.NULL);
|
||||
|
||||
this._playbin.set_state(Gst.State.NULL);
|
||||
// Block briefly for the (normally fast) transition to actually
|
||||
// finish before dropping our reference — otherwise the element
|
||||
// can get disposed mid-transition, which GStreamer logs as
|
||||
// "Trying to dispose element ..., but it is in PLAYING instead
|
||||
// of the NULL state".
|
||||
this._playbin.get_state(200 * Gst.MSECOND);
|
||||
|
||||
this._playbin = null;
|
||||
this._appsink = null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user