From c3f72729298e1c38bd6432f4f7d4a769dcdc2b7b Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 24 Sep 2015 10:51:31 +0200 Subject: [PATCH] gtk: Marshall state changes in the main thread Gtk is not MT safe thus we need to make sure that everything is done in the main thread when working with it. https://bugzilla.gnome.org/show_bug.cgi?id=755251 --- ext/gtk/gstgtkbasesink.c | 97 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/ext/gtk/gstgtkbasesink.c b/ext/gtk/gstgtkbasesink.c index 6321bdaae3..41bb7f23ca 100644 --- a/ext/gtk/gstgtkbasesink.c +++ b/ext/gtk/gstgtkbasesink.c @@ -77,6 +77,54 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink, "gtkbasesink", 0, "Gtk Video Sink base class")); +struct invoke_context +{ + GThreadFunc func; + gpointer data; + GMutex lock; + GCond cond; + gboolean fired; + + gpointer res; +}; + +static gboolean +_invoke_func (struct invoke_context *info) +{ + g_mutex_lock (&info->lock); + info->res = info->func (info->data); + info->fired = TRUE; + g_cond_signal (&info->cond); + g_mutex_unlock (&info->lock); + + return G_SOURCE_REMOVE; +} + +static gpointer +_invoke_on_main (GThreadFunc func, gpointer data) +{ + GMainContext *main_context = g_main_context_default (); + struct invoke_context info; + + g_mutex_init (&info.lock); + g_cond_init (&info.cond); + info.fired = FALSE; + info.func = func; + info.data = data; + + g_main_context_invoke (main_context, (GSourceFunc) _invoke_func, &info); + + g_mutex_lock (&info.lock); + while (!info.fired) + g_cond_wait (&info.cond, &info.lock); + g_mutex_unlock (&info.lock); + + g_mutex_clear (&info.lock); + g_cond_clear (&info.cond); + + return info.res; +} + static void gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass) { @@ -207,8 +255,21 @@ gst_gtk_base_sink_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_WIDGET: - g_value_set_object (value, gst_gtk_base_sink_get_widget (gtk_sink)); + { + GObject *widget = NULL; + + GST_OBJECT_LOCK (gtk_sink); + if (gtk_sink->widget != NULL) + widget = G_OBJECT (gtk_sink->widget); + GST_OBJECT_UNLOCK (gtk_sink); + + if (!widget) + widget = _invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget, + gtk_sink); + + g_value_set_object (value, widget); break; + } case PROP_FORCE_ASPECT_RATIO: g_value_set_boolean (value, gtk_sink->force_aspect_ratio); break; @@ -273,7 +334,7 @@ gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface) } static gboolean -gst_gtk_base_sink_start (GstBaseSink * bsink) +gst_gtk_base_sink_start_on_main (GstBaseSink * bsink) { GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink); GstGtkBaseSinkClass *klass = GST_GTK_BASE_SINK_GET_CLASS (bsink); @@ -303,7 +364,14 @@ gst_gtk_base_sink_start (GstBaseSink * bsink) } static gboolean -gst_gtk_base_sink_stop (GstBaseSink * bsink) +gst_gtk_base_sink_start (GstBaseSink * bsink) +{ + return ! !_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_start_on_main, + bsink); +} + +static gboolean +gst_gtk_base_sink_stop_on_main (GstBaseSink * bsink) { GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink); @@ -317,11 +385,17 @@ gst_gtk_base_sink_stop (GstBaseSink * bsink) } static gboolean -_show_window_cb (GstGtkBaseSink * gtk_sink) +gst_gtk_base_sink_stop (GstBaseSink * bsink) { - gtk_widget_show_all (gtk_sink->window); + return ! !_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_stop_on_main, + bsink); +} - return FALSE; +static void +gst_gtk_widget_show_all_and_unref (GtkWidget * widget) +{ + gtk_widget_show_all (widget); + g_object_unref (widget); } static GstStateChangeReturn @@ -340,11 +414,20 @@ gst_gtk_base_sink_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + { + GtkWindow *window = NULL; + GST_OBJECT_LOCK (gtk_sink); if (gtk_sink->window) - g_idle_add ((GSourceFunc) _show_window_cb, gtk_sink); + window = g_object_ref (gtk_sink->window); GST_OBJECT_UNLOCK (gtk_sink); + + if (window) + _invoke_on_main ((GThreadFunc) gst_gtk_widget_show_all_and_unref, + window); + break; + } case GST_STATE_CHANGE_PAUSED_TO_READY: GST_OBJECT_LOCK (gtk_sink); if (gtk_sink->widget)