From c8e12ead08fd8ee8593b033c1f1a624f94a47ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 12 May 2020 16:14:00 +0200 Subject: [PATCH] screen-cast-src: Notify about the stream being closed after dispatch We're iterating inside the PipeWire loop when detecting PipeWire errors, and shouldn't destroy the PipeWire objects mid-iteration. Avoid this by first disabling the stream src (effectively stopping the recording), then notifying about it being closed in an idle callback. The notification eventually makes the rest of the screen cast code clean up the objects, including the src and the associated PipeWire objects, but will do so outside the PipeWire loop iteration. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1251 https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 --- src/backends/meta-screen-cast-stream-src.c | 31 +++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 544ed27a3..e3a85c101 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -68,6 +68,7 @@ typedef struct _MetaPipeWireSource { GSource base; + MetaScreenCastStreamSrc *src; struct pw_loop *pipewire_loop; } MetaPipeWireSource; @@ -81,6 +82,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate struct spa_hook pipewire_core_listener; gboolean is_enabled; + gboolean emit_closed_after_dispatch; struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; @@ -540,12 +542,6 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) priv->is_enabled = FALSE; } -static void -meta_screen_cast_stream_src_notify_closed (MetaScreenCastStreamSrc *src) -{ - g_signal_emit (src, signals[CLOSED], 0); -} - static void on_stream_state_changed (void *data, enum pw_stream_state old, @@ -560,7 +556,9 @@ on_stream_state_changed (void *data, { case PW_STREAM_STATE_ERROR: g_warning ("pipewire stream error: %s", error_message); - meta_screen_cast_stream_src_notify_closed (src); + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + priv->emit_closed_after_dispatch = TRUE; break; case PW_STREAM_STATE_PAUSED: if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) @@ -828,11 +826,17 @@ on_core_error (void *data, const char *message) { MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); g_warning ("pipewire remote error: id:%u %s", id, message); if (id == PW_ID_CORE && res == -EPIPE) - meta_screen_cast_stream_src_notify_closed (src); + { + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + priv->emit_closed_after_dispatch = TRUE; + } } static gboolean @@ -849,12 +853,18 @@ pipewire_loop_source_dispatch (GSource *source, gpointer user_data) { MetaPipeWireSource *pipewire_source = (MetaPipeWireSource *) source; + MetaScreenCastStreamSrc *src = pipewire_source->src; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); int result; result = pw_loop_iterate (pipewire_source->pipewire_loop, 0); if (result < 0) g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result)); + if (priv->emit_closed_after_dispatch) + g_signal_emit (src, signals[CLOSED], 0); + return TRUE; } @@ -876,13 +886,14 @@ static GSourceFuncs pipewire_source_funcs = }; static MetaPipeWireSource * -create_pipewire_source (void) +create_pipewire_source (MetaScreenCastStreamSrc *src) { MetaPipeWireSource *pipewire_source; pipewire_source = (MetaPipeWireSource *) g_source_new (&pipewire_source_funcs, sizeof (MetaPipeWireSource)); + pipewire_source->src = src; pipewire_source->pipewire_loop = pw_loop_new (NULL); if (!pipewire_source->pipewire_loop) { @@ -914,7 +925,7 @@ meta_screen_cast_stream_src_initable_init (GInitable *initable, MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); - priv->pipewire_source = create_pipewire_source (); + priv->pipewire_source = create_pipewire_source (src); if (!priv->pipewire_source) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,