gtkglsink: fix crash when widget is resized after element destruction
[platform/upstream/gst-plugins-good.git] / ext / gtk / gstgtkglsink.c
index f278873..1102d47 100644 (file)
  */
 
 /**
- * SECTION:gstgtkglsink
- *
+ * SECTION:element-gtkglsink
+ * @title: gtkglsink
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 
+#include <gst/gl/gstglfuncs.h>
+
 #include "gstgtkglsink.h"
+#include "gtkgstglwidget.h"
 
 GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
 #define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
 
-static void gst_gtk_gl_sink_finalize (GObject * object);
-static void gst_gtk_gl_sink_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * param_spec);
-static void gst_gtk_gl_sink_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * param_spec);
-
+static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
 static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
-
 static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
-
-static GstStateChangeReturn
-gst_gtk_gl_sink_change_state (GstElement * element, GstStateChange transition);
-
-static void gst_gtk_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
-    GstClockTime * start, GstClockTime * end);
-static gboolean gst_gtk_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
-static GstFlowReturn gst_gtk_gl_sink_show_frame (GstVideoSink * bsink,
-    GstBuffer * buf);
 static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
     GstQuery * query);
+static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
+    GstCaps * filter);
+
+static void gst_gtk_gl_sink_finalize (GObject * object);
 
 static GstStaticPadTemplate gst_gtk_gl_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
-        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA")));
-
-enum
-{
-  ARG_0,
-  PROP_WIDGET
-};
-
-enum
-{
-  SIGNAL_0,
-  LAST_SIGNAL
-};
+        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
+        GST_VIDEO_CAPS_MAKE_WITH_FEATURES
+        (GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
+            GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
 
 #define gst_gtk_gl_sink_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
-    GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
-        "gtkglsink", 0, "Gtk Video Sink"));
+    GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
+        "gtkglsink", 0, "Gtk GL Video Sink"));
 
 static void
 gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
@@ -83,104 +66,35 @@ gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
   GObjectClass *gobject_class;
   GstElementClass *gstelement_class;
   GstBaseSinkClass *gstbasesink_class;
-  GstVideoSinkClass *gstvideosink_class;
+  GstGtkBaseSinkClass *gstgtkbasesink_class;
 
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
   gstbasesink_class = (GstBaseSinkClass *) klass;
-  gstvideosink_class = (GstVideoSinkClass *) klass;
-
-  gobject_class->set_property = gst_gtk_gl_sink_set_property;
-  gobject_class->get_property = gst_gtk_gl_sink_get_property;
-
-  gst_element_class_set_metadata (gstelement_class, "Gtk Video Sink",
-      "Sink/Video", "A video sink the renders to a GtkWidget",
-      "Matthew Waters <matthew@centricular.com>");
-
-  g_object_class_install_property (gobject_class, PROP_WIDGET,
-      g_param_spec_object ("widget", "Gtk Widget",
-          "The GtkWidget to place in the widget heirachy",
-          GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_gtk_gl_sink_template));
+  gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
 
   gobject_class->finalize = gst_gtk_gl_sink_finalize;
 
-  gstelement_class->change_state = gst_gtk_gl_sink_change_state;
   gstbasesink_class->query = gst_gtk_gl_sink_query;
-  gstbasesink_class->set_caps = gst_gtk_gl_sink_set_caps;
-  gstbasesink_class->get_times = gst_gtk_gl_sink_get_times;
   gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
+  gstbasesink_class->start = gst_gtk_gl_sink_start;
   gstbasesink_class->stop = gst_gtk_gl_sink_stop;
+  gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
 
-  gstvideosink_class->show_frame = gst_gtk_gl_sink_show_frame;
-}
-
-static void
-gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
-{
-}
-
-static void
-gst_gtk_gl_sink_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
-{
-  switch (prop_id) {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_gtk_gl_sink_finalize (GObject * object)
-{
-  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);;
-
-  g_object_unref (gtk_sink->widget);
+  gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
+  gstgtkbasesink_class->window_title = "Gtk+ GL renderer";
 
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static GtkGstGLWidget *
-gst_gtk_gl_sink_get_widget (GstGtkGLSink * gtk_sink)
-{
-  if (gtk_sink->widget != NULL)
-    return gtk_sink->widget;
-
-  /* Ensure GTK is initialized, this has no side effect if it was already
-   * initialized. Also, we do that lazylli, so the application can be first */
-  if (!gtk_init_check (NULL, NULL)) {
-    GST_ERROR_OBJECT (gtk_sink, "Could not ensure GTK initialization.");
-    return NULL;
-  }
-
-  gtk_sink->widget = (GtkGstGLWidget *) gtk_gst_gl_widget_new ();
-
-  /* Take the floating ref, otherwise the destruction of the container will
-   * make this widget disapear possibly before we are done. */
-  gst_object_ref_sink (gtk_sink->widget);
+  gst_element_class_set_metadata (gstelement_class, "Gtk GL Video Sink",
+      "Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
+      "Matthew Waters <matthew@centricular.com>");
 
-  return gtk_sink->widget;
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &gst_gtk_gl_sink_template);
 }
 
 static void
-gst_gtk_gl_sink_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec)
+gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
 {
-  GstGtkGLSink *gtk_sink;
-
-  gtk_sink = GST_GTK_GL_SINK (object);
-
-  switch (prop_id) {
-    case PROP_WIDGET:
-      g_value_set_object (value, gst_gtk_gl_sink_get_widget (gtk_sink));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
 }
 
 static gboolean
@@ -192,41 +106,10 @@ gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_CONTEXT:
     {
-      const gchar *context_type;
-      GstContext *context, *old_context;
-      gboolean ret;
-
-      ret = gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
-          &gtk_sink->display, &gtk_sink->gtk_context);
-
-      if (gtk_sink->display)
-        gst_gl_display_filter_gl_api (gtk_sink->display, GST_GL_API_OPENGL3);
-
-      gst_query_parse_context_type (query, &context_type);
-
-      if (g_strcmp0 (context_type, "gst.gl.local_context") == 0) {
-        GstStructure *s;
-
-        gst_query_parse_context (query, &old_context);
-
-        if (old_context)
-          context = gst_context_copy (old_context);
-        else
-          context = gst_context_new ("gst.gl.local_context", FALSE);
-
-        s = gst_context_writable_structure (context);
-        gst_structure_set (s, "context", GST_GL_TYPE_CONTEXT, gtk_sink->context,
-            NULL);
-        gst_query_set_context (query, context);
-        gst_context_unref (context);
-
-        ret = gtk_sink->context != NULL;
-      }
-      GST_LOG_OBJECT (gtk_sink, "context query of type %s %i", context_type,
-          ret);
-
-      if (ret)
-        return ret;
+      if (gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
+              gtk_sink->display, gtk_sink->context, gtk_sink->gtk_context))
+        return TRUE;
+      break;
     }
     default:
       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
@@ -236,149 +119,126 @@ gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
   return res;
 }
 
-static gboolean
-gst_gtk_gl_sink_stop (GstBaseSink * bsink)
+static void
+_size_changed_cb (GtkWidget * widget, GdkRectangle * rectangle,
+    GstGtkGLSink * gtk_sink)
 {
-  return TRUE;
+  gint scale_factor, width, height;
+  gboolean reconfigure;
+
+  scale_factor = gtk_widget_get_scale_factor (widget);
+  width = scale_factor * gtk_widget_get_allocated_width (widget);
+  height = scale_factor * gtk_widget_get_allocated_height (widget);
+
+  GST_OBJECT_LOCK (gtk_sink);
+  reconfigure =
+      (width != gtk_sink->display_width || height != gtk_sink->display_height);
+  gtk_sink->display_width = width;
+  gtk_sink->display_height = height;
+  GST_OBJECT_UNLOCK (gtk_sink);
+
+  if (reconfigure) {
+    GST_DEBUG_OBJECT (gtk_sink, "Sending reconfigure event on sinkpad.");
+    gst_pad_push_event (GST_BASE_SINK (gtk_sink)->sinkpad,
+        gst_event_new_reconfigure ());
+  }
 }
 
-static GstStateChangeReturn
-gst_gtk_gl_sink_change_state (GstElement * element, GstStateChange transition)
+static void
+destroy_cb (GtkWidget * widget, GstGtkGLSink * gtk_sink)
 {
-  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (element);
-  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-
-  GST_DEBUG ("changing state: %s => %s",
-      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
-      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      if (gst_gtk_gl_sink_get_widget (gtk_sink) == NULL)
-        return GST_STATE_CHANGE_FAILURE;
-
-      /* After this point, gtk_sink->widget will always be set */
+  if (gtk_sink->size_allocate_sig_handler) {
+    g_signal_handler_disconnect (widget, gtk_sink->size_allocate_sig_handler);
+    gtk_sink->size_allocate_sig_handler = 0;
+  }
 
-      if (!gtk_widget_get_parent (GTK_WIDGET (gtk_sink->widget))) {
-        GST_ERROR_OBJECT (gtk_sink,
-            "gtkglsink widget need to be parented to work.");
-        return GST_STATE_CHANGE_FAILURE;
-      }
+  if (gtk_sink->widget_destroy_sig_handler) {
+    g_signal_handler_disconnect (widget, gtk_sink->widget_destroy_sig_handler);
+    gtk_sink->widget_destroy_sig_handler = 0;
+  }
+}
 
-      if (!gtk_gst_gl_widget_init_winsys (gtk_sink->widget))
-        return GST_STATE_CHANGE_FAILURE;
+static gboolean
+gst_gtk_gl_sink_start (GstBaseSink * bsink)
+{
+  GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
+  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
+  GtkGstGLWidget *gst_widget;
 
-      gtk_sink->display = gtk_gst_gl_widget_get_display (gtk_sink->widget);
-      gtk_sink->context = gtk_gst_gl_widget_get_context (gtk_sink->widget);
-      gtk_sink->gtk_context =
-          gtk_gst_gl_widget_get_gtk_context (gtk_sink->widget);
+  if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
+    return FALSE;
 
-      if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context)
-        return GST_STATE_CHANGE_FAILURE;
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      break;
-    default:
-      break;
-  }
+  /* After this point, gtk_sink->widget will always be set */
+  gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
 
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-  if (ret == GST_STATE_CHANGE_FAILURE)
-    return ret;
+  /* Track the allocation size */
+  gtk_sink->size_allocate_sig_handler =
+      g_signal_connect (gst_widget, "size-allocate",
+      G_CALLBACK (_size_changed_cb), gtk_sink);
 
-  switch (transition) {
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      gtk_gst_gl_widget_set_buffer (gtk_sink->widget, NULL);
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      if (gtk_sink->display) {
-        gst_object_unref (gtk_sink->display);
-        gtk_sink->display = NULL;
-      }
-
-      if (gtk_sink->context) {
-        gst_object_unref (gtk_sink->context);
-        gtk_sink->context = NULL;
-      }
-
-      if (gtk_sink->gtk_context) {
-        gst_object_unref (gtk_sink->gtk_context);
-        gtk_sink->gtk_context = NULL;
-      }
-      break;
-    default:
-      break;
-  }
+  gtk_sink->widget_destroy_sig_handler =
+      g_signal_connect (gst_widget, "destroy", G_CALLBACK (destroy_cb),
+      gtk_sink);
 
-  return ret;
-}
+  _size_changed_cb (GTK_WIDGET (gst_widget), NULL, gtk_sink);
 
-static void
-gst_gtk_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
-    GstClockTime * start, GstClockTime * end)
-{
-  GstGtkGLSink *gtk_sink;
-
-  gtk_sink = GST_GTK_GL_SINK (bsink);
-
-  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
-    *start = GST_BUFFER_TIMESTAMP (buf);
-    if (GST_BUFFER_DURATION_IS_VALID (buf))
-      *end = *start + GST_BUFFER_DURATION (buf);
-    else {
-      if (GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info) > 0) {
-        *end = *start +
-            gst_util_uint64_scale_int (GST_SECOND,
-            GST_VIDEO_INFO_FPS_D (&gtk_sink->v_info),
-            GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info));
-      }
-    }
+  if (!gtk_gst_gl_widget_init_winsys (gst_widget)) {
+    GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
+            "Failed to initialize OpenGL with Gtk"), (NULL));
+    return FALSE;
   }
-}
 
-gboolean
-gst_gtk_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
-{
-  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
+  gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
+  gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
+  gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
 
-  GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
-
-  if (!gst_video_info_from_caps (&gtk_sink->v_info, caps))
+  if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context) {
+    GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
+            "Failed to retrieve OpenGL context from Gtk"), (NULL));
     return FALSE;
+  }
 
-  if (!gtk_gst_gl_widget_set_caps (gtk_sink->widget, caps))
-    return FALSE;
+  gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
+      gtk_sink->display);
 
   return TRUE;
 }
 
-static GstFlowReturn
-gst_gtk_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
+static gboolean
+gst_gtk_gl_sink_stop (GstBaseSink * bsink)
 {
-  GstGtkGLSink *gtk_sink;
+  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
 
-  GST_TRACE ("rendering buffer:%p", buf);
+  if (gtk_sink->display) {
+    gst_object_unref (gtk_sink->display);
+    gtk_sink->display = NULL;
+  }
 
-  gtk_sink = GST_GTK_GL_SINK (vsink);
+  if (gtk_sink->context) {
+    gst_object_unref (gtk_sink->context);
+    gtk_sink->context = NULL;
+  }
 
-  gtk_gst_gl_widget_set_buffer (gtk_sink->widget, buf);
+  if (gtk_sink->gtk_context) {
+    gst_object_unref (gtk_sink->gtk_context);
+    gtk_sink->gtk_context = NULL;
+  }
 
-  return GST_FLOW_OK;
+  return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
 }
 
 static gboolean
 gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
 {
   GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
-  GstBufferPool *pool;
+  GstBufferPool *pool = NULL;
   GstStructure *config;
   GstCaps *caps;
+  GstVideoInfo info;
   guint size;
   gboolean need_pool;
+  GstStructure *allocation_meta = NULL;
+  gint display_width, display_height;
 
   if (!gtk_sink->display || !gtk_sink->context)
     return FALSE;
@@ -388,49 +248,49 @@ gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
   if (caps == NULL)
     goto no_caps;
 
-  if ((pool = gtk_sink->pool))
-    gst_object_ref (pool);
-
-  if (pool != NULL) {
-    GstCaps *pcaps;
+  if (!gst_video_info_from_caps (&info, caps))
+    goto invalid_caps;
 
-    /* we had a pool, check caps */
-    GST_DEBUG_OBJECT (gtk_sink, "check existing pool caps");
-    config = gst_buffer_pool_get_config (pool);
-    gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
-
-    if (!gst_caps_is_equal (caps, pcaps)) {
-      GST_DEBUG_OBJECT (gtk_sink, "pool has different caps");
-      /* different caps, we can't use this pool */
-      gst_object_unref (pool);
-      pool = NULL;
-    }
-    gst_structure_free (config);
-  }
-
-  if (pool == NULL && need_pool) {
-    GstVideoInfo info;
-
-    if (!gst_video_info_from_caps (&info, caps))
-      goto invalid_caps;
+  /* the normal size of a frame */
+  size = info.size;
 
+  if (need_pool) {
     GST_DEBUG_OBJECT (gtk_sink, "create new pool");
     pool = gst_gl_buffer_pool_new (gtk_sink->context);
 
-    /* the normal size of a frame */
-    size = info.size;
-
     config = gst_buffer_pool_get_config (pool);
     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
+    gst_buffer_pool_config_add_option (config,
+        GST_BUFFER_POOL_OPTION_GL_SYNC_META);
+
     if (!gst_buffer_pool_set_config (pool, config))
       goto config_failed;
   }
+
   /* we need at least 2 buffer because we hold on to the last one */
-  if (pool) {
-    gst_query_add_allocation_pool (query, pool, size, 2, 0);
+  gst_query_add_allocation_pool (query, pool, size, 2, 0);
+  if (pool)
     gst_object_unref (pool);
+
+  GST_OBJECT_LOCK (gtk_sink);
+  display_width = gtk_sink->display_width;
+  display_height = gtk_sink->display_height;
+  GST_OBJECT_UNLOCK (gtk_sink);
+
+  if (display_width != 0 && display_height != 0) {
+    GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
+        display_width, display_height);
+    allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
+        "width", G_TYPE_UINT, display_width,
+        "height", G_TYPE_UINT, display_height, NULL);
   }
 
+  gst_query_add_allocation_meta (query,
+      GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
+
+  if (allocation_meta)
+    gst_structure_free (allocation_meta);
+
   /* we also support various metadata */
   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
 
@@ -456,3 +316,49 @@ config_failed:
     return FALSE;
   }
 }
+
+static GstCaps *
+gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
+{
+  GstCaps *tmp = NULL;
+  GstCaps *result = NULL;
+
+  tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
+
+  if (filter) {
+    GST_DEBUG_OBJECT (bsink, "intersecting with filter caps %" GST_PTR_FORMAT,
+        filter);
+
+    result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (tmp);
+  } else {
+    result = tmp;
+  }
+
+  result = gst_gl_overlay_compositor_add_caps (result);
+
+  GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
+
+  return result;
+}
+
+static void
+gst_gtk_gl_sink_finalize (GObject * object)
+{
+  GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);
+  GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (object);
+
+  if (gtk_sink->size_allocate_sig_handler) {
+    g_signal_handler_disconnect (base_sink->widget,
+        gtk_sink->size_allocate_sig_handler);
+    gtk_sink->size_allocate_sig_handler = 0;
+  }
+
+  if (gtk_sink->widget_destroy_sig_handler) {
+    g_signal_handler_disconnect (base_sink->widget,
+        gtk_sink->widget_destroy_sig_handler);
+    gtk_sink->widget_destroy_sig_handler = 0;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}