gl/mixer: support GstGLDisplay changes
authorMatthew Waters <matthew@centricular.com>
Tue, 4 Feb 2020 02:58:06 +0000 (13:58 +1100)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 3 Mar 2020 02:11:52 +0000 (02:11 +0000)
ext/gl/gstglbasemixer.c
ext/gl/gstglbasemixer.h
ext/gl/gstglmixer.c
ext/gl/gstglvideomixer.c

index 0284c8d..fc61e84 100644 (file)
@@ -41,11 +41,19 @@ static void gst_gl_base_mixer_set_context (GstElement * element,
 static GstStateChangeReturn gst_gl_base_mixer_change_state (GstElement *
     element, GstStateChange transition);
 
+static void gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data);
+static void gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data);
+
 struct _GstGLBaseMixerPrivate
 {
   gboolean negotiated;
 
   GstGLContext *other_context;
+
+  gboolean gl_started;
+  gboolean gl_result;
+
+  GRecMutex context_lock;
 };
 
 #define gst_gl_base_mixer_parent_class parent_class
@@ -95,21 +103,37 @@ gst_gl_base_mixer_pad_set_property (GObject * object, guint prop_id,
 static gboolean
 _find_local_gl_context (GstGLBaseMixer * mix)
 {
-  if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SRC,
-          &mix->context))
-    return TRUE;
-  if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SINK,
-          &mix->context))
-    return TRUE;
+  GstGLContext *context = mix->context;
+
+  if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SRC, &context)) {
+    if (context->display == mix->display) {
+      mix->context = context;
+      return TRUE;
+    }
+    if (context != mix->context)
+      gst_clear_object (&context);
+  }
+  if (gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SINK, &context)) {
+    if (context->display == mix->display) {
+      mix->context = context;
+      return TRUE;
+    }
+    if (context != mix->context)
+      gst_clear_object (&context);
+  }
   return FALSE;
 }
 
 static gboolean
-_get_gl_context (GstGLBaseMixer * mix)
+_get_gl_context_unlocked (GstGLBaseMixer * mix)
 {
   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
+  gboolean new_context = FALSE;
   GError *error = NULL;
 
+  if (!mix->context)
+    new_context = TRUE;
+
   if (!gst_gl_ensure_element_data (mix, &mix->display,
           &mix->priv->other_context))
     return FALSE;
@@ -139,10 +163,20 @@ _get_gl_context (GstGLBaseMixer * mix)
   }
   GST_OBJECT_UNLOCK (mix->display);
 
-  {
-    GstGLAPI current_gl_api = gst_gl_context_get_gl_api (mix->context);
-    if ((current_gl_api & mix_class->supported_gl_api) == 0)
-      goto unsupported_gl_api;
+  if (new_context || !mix->priv->gl_started) {
+    if (mix->priv->gl_started)
+      gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
+
+    {
+      if ((gst_gl_context_get_gl_api (mix->
+                  context) & mix_class->supported_gl_api) == 0)
+        goto unsupported_gl_api;
+    }
+
+    gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_start, mix);
+
+    if (!mix->priv->gl_started)
+      goto error;
   }
 
   return TRUE;
@@ -168,6 +202,22 @@ context_error:
     g_clear_error (&error);
     return FALSE;
   }
+error:
+  {
+    GST_ELEMENT_ERROR (mix, LIBRARY, INIT,
+        ("Subclass failed to initialize."), (NULL));
+    return FALSE;
+  }
+}
+
+static gboolean
+_get_gl_context (GstGLBaseMixer * mix)
+{
+  gboolean ret;
+  g_rec_mutex_lock (&mix->priv->context_lock);
+  ret = _get_gl_context_unlocked (mix);
+  g_rec_mutex_unlock (&mix->priv->context_lock);
+  return ret;
 }
 
 static gboolean
@@ -193,9 +243,13 @@ gst_gl_base_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_CONTEXT:
     {
-      if (gst_gl_handle_context_query ((GstElement *) mix, query,
-              mix->display, mix->context, mix->priv->other_context))
-        return TRUE;
+      gboolean ret;
+      g_rec_mutex_lock (&mix->priv->context_lock);
+      ret = gst_gl_handle_context_query ((GstElement *) mix, query,
+          mix->display, mix->context, mix->priv->other_context);
+      g_rec_mutex_unlock (&mix->priv->context_lock);
+      if (ret)
+        return ret;
       break;
     }
     default:
@@ -232,6 +286,7 @@ gst_gl_base_mixer_src_activate_mode (GstAggregator * aggregator,
 static gboolean gst_gl_base_mixer_stop (GstAggregator * agg);
 static gboolean gst_gl_base_mixer_start (GstAggregator * agg);
 
+static void gst_gl_base_mixer_finalize (GObject * object);
 static void gst_gl_base_mixer_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_gl_base_mixer_get_property (GObject * object, guint prop_id,
@@ -240,6 +295,9 @@ static void gst_gl_base_mixer_get_property (GObject * object, guint prop_id,
 static gboolean gst_gl_base_mixer_decide_allocation (GstAggregator * agg,
     GstQuery * query);
 
+static gboolean gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src);
+static void gst_gl_base_mixer_default_gl_stop (GstGLBaseMixer * src);
+
 static void
 gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass)
 {
@@ -252,6 +310,7 @@ gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass)
   gobject_class = (GObjectClass *) klass;
   element_class = GST_ELEMENT_CLASS (klass);
 
+  gobject_class->finalize = gst_gl_base_mixer_finalize;
   gobject_class->get_property = gst_gl_base_mixer_get_property;
   gobject_class->set_property = gst_gl_base_mixer_set_property;
 
@@ -267,6 +326,9 @@ gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass)
   agg_class->decide_allocation = gst_gl_base_mixer_decide_allocation;
   agg_class->propose_allocation = gst_gl_base_mixer_propose_allocation;
 
+  klass->gl_start = gst_gl_base_mixer_default_gl_start;
+  klass->gl_stop = gst_gl_base_mixer_default_gl_stop;
+
   g_object_class_install_property (gobject_class, PROP_CONTEXT,
       g_param_spec_object ("context",
           "OpenGL context",
@@ -283,6 +345,58 @@ static void
 gst_gl_base_mixer_init (GstGLBaseMixer * mix)
 {
   mix->priv = gst_gl_base_mixer_get_instance_private (mix);
+
+  g_rec_mutex_init (&mix->priv->context_lock);
+}
+
+static void
+gst_gl_base_mixer_finalize (GObject * object)
+{
+  GstGLBaseMixer *mix = GST_GL_BASE_MIXER (object);
+
+  g_rec_mutex_clear (&mix->priv->context_lock);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src)
+{
+  return TRUE;
+}
+
+static void
+gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data)
+{
+  GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
+  GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
+
+  GST_INFO_OBJECT (src, "starting");
+  gst_gl_insert_debug_marker (src->context,
+      "starting element %s", GST_OBJECT_NAME (src));
+
+  src->priv->gl_started = src_class->gl_start (src);
+}
+
+static void
+gst_gl_base_mixer_default_gl_stop (GstGLBaseMixer * src)
+{
+}
+
+static void
+gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data)
+{
+  GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
+  GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
+
+  GST_INFO_OBJECT (src, "stopping");
+  gst_gl_insert_debug_marker (src->context,
+      "stopping element %s", GST_OBJECT_NAME (src));
+
+  if (src->priv->gl_started)
+    src_class->gl_stop (src);
+
+  src->priv->gl_started = FALSE;
 }
 
 static void
@@ -290,12 +404,26 @@ gst_gl_base_mixer_set_context (GstElement * element, GstContext * context)
 {
   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element);
   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
+  GstGLDisplay *old_display, *new_display;
 
+  g_rec_mutex_lock (&mix->priv->context_lock);
+  old_display = mix->display ? gst_object_ref (mix->display) : NULL;
   gst_gl_handle_set_context (element, context, &mix->display,
       &mix->priv->other_context);
-
   if (mix->display)
     gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
+  new_display = mix->display ? gst_object_ref (mix->display) : NULL;
+
+  if (old_display && new_display) {
+    if (old_display != new_display) {
+      gst_clear_object (&mix->context);
+      _get_gl_context_unlocked (mix);
+      gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (mix));
+    }
+  }
+  gst_clear_object (&old_display);
+  gst_clear_object (&new_display);
+  g_rec_mutex_unlock (&mix->priv->context_lock);
 
   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
 }
@@ -307,11 +435,15 @@ gst_gl_base_mixer_activate (GstGLBaseMixer * mix, gboolean active)
   gboolean result = TRUE;
 
   if (active) {
+    g_rec_mutex_lock (&mix->priv->context_lock);
     if (!gst_gl_ensure_element_data (mix, &mix->display,
-            &mix->priv->other_context))
+            &mix->priv->other_context)) {
+      g_rec_mutex_lock (&mix->priv->context_lock);
       return FALSE;
+    }
 
     gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
+    g_rec_mutex_unlock (&mix->priv->context_lock);
   }
 
   return result;
@@ -346,9 +478,13 @@ gst_gl_base_mixer_src_query (GstAggregator * agg, GstQuery * query)
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_CONTEXT:
     {
-      if (gst_gl_handle_context_query ((GstElement *) mix, query,
-              mix->display, mix->context, mix->priv->other_context))
-        return TRUE;
+      gboolean ret;
+      g_rec_mutex_lock (&mix->priv->context_lock);
+      ret = gst_gl_handle_context_query ((GstElement *) mix, query,
+          mix->display, mix->context, mix->priv->other_context);
+      g_rec_mutex_unlock (&mix->priv->context_lock);
+      if (ret)
+        return ret;
       break;
     }
     default:
@@ -407,10 +543,11 @@ gst_gl_base_mixer_stop (GstAggregator * agg)
 {
   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
 
-  if (mix->context) {
-    gst_object_unref (mix->context);
-    mix->context = NULL;
-  }
+  g_rec_mutex_lock (&mix->priv->context_lock);
+  if (mix->priv->gl_started)
+    gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
+  gst_clear_object (&mix->context);
+  g_rec_mutex_unlock (&mix->priv->context_lock);
 
   return TRUE;
 }
@@ -449,10 +586,9 @@ gst_gl_base_mixer_change_state (GstElement * element, GstStateChange transition)
         mix->priv->other_context = NULL;
       }
 
-      if (mix->display) {
-        gst_object_unref (mix->display);
-        mix->display = NULL;
-      }
+      g_rec_mutex_lock (&mix->priv->context_lock);
+      gst_clear_object (&mix->display);
+      g_rec_mutex_unlock (&mix->priv->context_lock);
       break;
     default:
       break;
@@ -460,3 +596,24 @@ gst_gl_base_mixer_change_state (GstElement * element, GstStateChange transition)
 
   return ret;
 }
+
+/**
+ * gst_gl_base_mixer_get_gl_context:
+ * @mix: a #GstGLBaseMixer
+ *
+ * Returns: (transfer full) (nullable): the #GstGLContext found by @mix
+ *
+ * Since: 1.18
+ */
+GstGLContext *
+gst_gl_base_mixer_get_gl_context (GstGLBaseMixer * mix)
+{
+  GstGLContext *ret;
+
+  g_return_val_if_fail (GST_IS_GL_BASE_MIXER (mix), NULL);
+
+  g_rec_mutex_lock (&mix->priv->context_lock);
+  ret = mix->context ? gst_object_ref (mix->context) : NULL;
+  g_rec_mutex_unlock (&mix->priv->context_lock);
+  return ret;
+}
index e9684ab..c823479 100644 (file)
@@ -89,10 +89,15 @@ struct _GstGLBaseMixerClass
   GstVideoAggregatorClass parent_class;
   GstGLAPI supported_gl_api;
 
+  gboolean      (*gl_start)     (GstGLBaseMixer * mix);
+  void          (*gl_stop)      (GstGLBaseMixer * mix);
+
   gpointer _padding[GST_PADDING];
 };
 
 GType gst_gl_base_mixer_get_type(void);
 
+GstGLContext *      gst_gl_base_mixer_get_gl_context        (GstGLBaseMixer * mix);
+
 G_END_DECLS
 #endif /* __GST_GL_BASE_MIXER_H__ */
index 682622d..d3046f0 100644 (file)
@@ -392,6 +392,9 @@ static void gst_gl_mixer_get_property (GObject * object, guint prop_id,
 static gboolean gst_gl_mixer_decide_allocation (GstAggregator * agg,
     GstQuery * query);
 
+static gboolean gst_gl_mixer_gl_start (GstGLBaseMixer * mix);
+static void gst_gl_mixer_gl_stop (GstGLBaseMixer * mix);
+
 static void gst_gl_mixer_finalize (GObject * object);
 
 static void
@@ -402,6 +405,7 @@ gst_gl_mixer_class_init (GstGLMixerClass * klass)
   GstVideoAggregatorClass *videoaggregator_class =
       (GstVideoAggregatorClass *) klass;
   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
+  GstGLBaseMixerClass *base_class = (GstGLBaseMixerClass *) klass;
 
   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "OpenGL mixer");
 
@@ -426,6 +430,8 @@ gst_gl_mixer_class_init (GstGLMixerClass * klass)
   videoaggregator_class->aggregate_frames = gst_gl_mixer_aggregate_frames;
   videoaggregator_class->find_best_format = _find_best_format;
 
+  base_class->gl_start = gst_gl_mixer_gl_start;
+  base_class->gl_stop = gst_gl_mixer_gl_stop;
 
   /* Register the pad class */
   g_type_class_ref (GST_TYPE_GL_MIXER_PAD);
@@ -528,30 +534,17 @@ _mixer_create_fbo (GstGLContext * context, GstGLMixer * mix)
 }
 
 static gboolean
-gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
+gst_gl_mixer_gl_start (GstGLBaseMixer * base_mix)
 {
-  GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (agg);
   GstGLMixer *mix = GST_GL_MIXER (base_mix);
   GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
-  GstGLContext *context;
-  GstBufferPool *pool = NULL;
-  GstStructure *config;
-  GstCaps *caps;
-  guint min, max, size;
-  gboolean update_pool;
-
-  if (!GST_AGGREGATOR_CLASS (gst_gl_mixer_parent_class)->decide_allocation (agg,
-          query))
-    return FALSE;
-
-  context = base_mix->context;
 
   g_mutex_lock (&mix->priv->gl_resource_lock);
   mix->priv->gl_resource_ready = FALSE;
   if (mix->fbo)
     gst_object_unref (mix->fbo);
 
-  gst_gl_context_thread_add (context,
+  gst_gl_context_thread_add (base_mix->context,
       (GstGLContextThreadFunc) _mixer_create_fbo, mix);
   if (!mix->fbo) {
     g_cond_signal (&mix->priv->gl_resource_cond);
@@ -559,15 +552,62 @@ gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
     goto context_error;
   }
 
-  gst_query_parse_allocation (query, &caps, NULL);
-
   if (mixer_class->set_caps)
-    mixer_class->set_caps (mix, caps);
+    mixer_class->set_caps (mix, mix->out_caps);
 
   mix->priv->gl_resource_ready = TRUE;
   g_cond_signal (&mix->priv->gl_resource_cond);
   g_mutex_unlock (&mix->priv->gl_resource_lock);
 
+  return GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (base_mix);
+
+context_error:
+  {
+    GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("Context error"), (NULL));
+    return FALSE;
+  }
+}
+
+static void
+gst_gl_mixer_gl_stop (GstGLBaseMixer * base_mix)
+{
+  GstGLMixer *mix = GST_GL_MIXER (base_mix);
+  GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
+
+  if (mixer_class->reset)
+    mixer_class->reset (mix);
+
+  if (mix->fbo) {
+    gst_object_unref (mix->fbo);
+    mix->fbo = NULL;
+  }
+
+  GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (base_mix);
+}
+
+static gboolean
+gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
+{
+  GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (agg);
+  GstGLContext *context;
+  GstBufferPool *pool = NULL;
+  GstStructure *config;
+  GstCaps *caps;
+  guint min, max, size;
+  gboolean update_pool;
+
+  if (!GST_AGGREGATOR_CLASS (gst_gl_mixer_parent_class)->decide_allocation (agg,
+          query))
+    return FALSE;
+
+  context = gst_gl_base_mixer_get_gl_context (base_mix);
+  if (!context) {
+    GST_WARNING_OBJECT (agg, "No OpenGL context");
+    return FALSE;
+  }
+
+  gst_query_parse_allocation (query, &caps, NULL);
+
   if (gst_query_get_n_allocation_pools (query) > 0) {
     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
 
@@ -598,13 +638,9 @@ gst_gl_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
 
   gst_object_unref (pool);
 
-  return TRUE;
+  gst_clear_object (&context);
 
-context_error:
-  {
-    GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("Context error"), (NULL));
-    return FALSE;
-  }
+  return TRUE;
 }
 
 gboolean
@@ -615,7 +651,6 @@ gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
   GstVideoFrame out_frame;
   GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (mix);
   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (mix);
-  GstGLMixerPrivate *priv = mix->priv;
 
   GST_TRACE ("Processing buffers");
 
@@ -626,12 +661,12 @@ gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
 
   out_tex = (GstGLMemory *) out_frame.map[0].memory;
 
-  g_mutex_lock (&priv->gl_resource_lock);
-  if (!priv->gl_resource_ready)
-    g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock);
+  g_mutex_lock (&mix->priv->gl_resource_lock);
+  if (!mix->priv->gl_resource_ready)
+    g_cond_wait (&mix->priv->gl_resource_cond, &mix->priv->gl_resource_lock);
 
-  if (!priv->gl_resource_ready) {
-    g_mutex_unlock (&priv->gl_resource_lock);
+  if (!mix->priv->gl_resource_ready) {
+    g_mutex_unlock (&mix->priv->gl_resource_lock);
     GST_ERROR_OBJECT (mix,
         "fbo used to render can't be created, do not run process_textures");
     res = FALSE;
@@ -640,7 +675,7 @@ gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf)
 
   mix_class->process_textures (mix, out_tex);
 
-  g_mutex_unlock (&priv->gl_resource_lock);
+  g_mutex_unlock (&mix->priv->gl_resource_lock);
 
 out:
   gst_video_frame_unmap (&out_frame);
@@ -659,12 +694,18 @@ gst_gl_mixer_process_buffers (GstGLMixer * mix, GstBuffer * outbuf)
 static GstFlowReturn
 gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
 {
+  GstGLBaseMixer *base_mix = GST_GL_BASE_MIXER (vagg);
   gboolean res = FALSE;
   GstGLMixer *mix = GST_GL_MIXER (vagg);
   GstGLMixerClass *mix_class = GST_GL_MIXER_GET_CLASS (vagg);
-  GstGLContext *context = GST_GL_BASE_MIXER (mix)->context;
+  GstGLContext *context = gst_gl_base_mixer_get_gl_context (base_mix);
   GstGLSyncMeta *sync_meta;
 
+  if (!context) {
+    GST_DEBUG_OBJECT (vagg, "No OpenGL context, try again later");
+    return GST_AGGREGATOR_FLOW_NEED_DATA;
+  }
+
   if (mix_class->process_buffers)
     res = gst_gl_mixer_process_buffers (mix, outbuf);
   else if (mix_class->process_textures)
@@ -674,6 +715,8 @@ gst_gl_mixer_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf)
   if (sync_meta)
     gst_gl_sync_meta_set_sync_point (sync_meta, context);
 
+  gst_clear_object (&context);
+
   return res ? GST_FLOW_OK : GST_FLOW_ERROR;
 }
 
@@ -709,15 +752,6 @@ static gboolean
 gst_gl_mixer_stop (GstAggregator * agg)
 {
   GstGLMixer *mix = GST_GL_MIXER (agg);
-  GstGLMixerClass *mixer_class = GST_GL_MIXER_GET_CLASS (mix);
-
-  if (mixer_class->reset)
-    mixer_class->reset (mix);
-
-  if (mix->fbo) {
-    gst_object_unref (mix->fbo);
-    mix->fbo = NULL;
-  }
 
   gst_gl_mixer_reset (mix);
 
index 0002c85..1c3a468 100644 (file)
@@ -472,7 +472,8 @@ static GstCaps *_update_caps (GstVideoAggregator * vagg, GstCaps * caps);
 static GstCaps *_fixate_caps (GstAggregator * agg, GstCaps * caps);
 static gboolean gst_gl_video_mixer_propose_allocation (GstAggregator *
     agg, GstAggregatorPad * agg_pad, GstQuery * decide_query, GstQuery * query);
-static void gst_gl_video_mixer_reset (GstGLMixer * mixer);
+static gboolean gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix);
+static void gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix);
 static gboolean gst_gl_video_mixer_set_caps (GstGLMixer * mixer,
     GstCaps * outcaps);
 
@@ -918,10 +919,11 @@ gst_gl_video_mixer_class_init (GstGLVideoMixerClass * klass)
           DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   GST_GL_MIXER_CLASS (klass)->set_caps = gst_gl_video_mixer_set_caps;
-  GST_GL_MIXER_CLASS (klass)->reset = gst_gl_video_mixer_reset;
   GST_GL_MIXER_CLASS (klass)->process_textures =
       gst_gl_video_mixer_process_textures;
 
+  GST_GL_BASE_MIXER_CLASS (klass)->gl_stop = gst_gl_video_mixer_gl_stop;
+  GST_GL_BASE_MIXER_CLASS (klass)->gl_start = gst_gl_video_mixer_gl_start;
 
   vagg_class->update_caps = _update_caps;
 
@@ -1057,7 +1059,6 @@ _update_caps (GstVideoAggregator * vagg, GstCaps * caps)
       GST_OBJECT_UNLOCK (vagg);
       return NULL;
     }
-
   }
 
   GST_OBJECT_UNLOCK (vagg);
@@ -1180,34 +1181,11 @@ _reset_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
   gst_element_foreach_sink_pad (GST_ELEMENT (video_mixer), _reset_pad_gl, NULL);
 }
 
-static void
-gst_gl_video_mixer_reset (GstGLMixer * mixer)
-{
-  GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer);
-  GstGLContext *context = GST_GL_BASE_MIXER (mixer)->context;
-
-  GST_DEBUG_OBJECT (mixer, "context:%p", context);
-
-  if (video_mixer->shader)
-    gst_object_unref (video_mixer->shader);
-  video_mixer->shader = NULL;
-
-  if (video_mixer->checker)
-    gst_object_unref (video_mixer->checker);
-  video_mixer->checker = NULL;
-
-  if (GST_GL_BASE_MIXER (mixer)->context)
-    gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _reset_gl,
-        mixer);
-}
-
 static gboolean
 gst_gl_video_mixer_set_caps (GstGLMixer * mixer, GstCaps * outcaps)
 {
   GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (mixer);
 
-  g_clear_object (&video_mixer->shader);
-
   /* need reconfigure output geometry */
   video_mixer->output_geo_change = TRUE;
 
@@ -1215,23 +1193,44 @@ gst_gl_video_mixer_set_caps (GstGLMixer * mixer, GstCaps * outcaps)
 }
 
 static void
-_video_mixer_process_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
+gst_gl_video_mixer_gl_stop (GstGLBaseMixer * base_mix)
 {
-  GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
+  GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
+
+  gst_clear_object (&video_mixer->shader);
+  gst_clear_object (&video_mixer->checker);
+
+  _reset_gl (base_mix->context, video_mixer);
+
+  GST_GL_BASE_MIXER_CLASS (parent_class)->gl_stop (base_mix);
+}
+
+static gboolean
+gst_gl_video_mixer_gl_start (GstGLBaseMixer * base_mix)
+{
+  GstGLVideoMixer *video_mixer = GST_GL_VIDEO_MIXER (base_mix);
 
   if (!video_mixer->shader) {
     gchar *frag_str = g_strdup_printf ("%s%s",
-        gst_gl_shader_string_get_highest_precision (GST_GL_BASE_MIXER
-            (mixer)->context, GST_GLSL_VERSION_NONE,
+        gst_gl_shader_string_get_highest_precision (base_mix->context,
+            GST_GLSL_VERSION_NONE,
             GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY),
         video_mixer_f_src);
 
-    gst_gl_context_gen_shader (GST_GL_BASE_MIXER (mixer)->context,
+    gst_gl_context_gen_shader (base_mix->context,
         gst_gl_shader_string_vertex_mat4_vertex_transform,
         frag_str, &video_mixer->shader);
     g_free (frag_str);
   }
 
+  return GST_GL_BASE_MIXER_CLASS (parent_class)->gl_start (base_mix);
+}
+
+static void
+_video_mixer_process_gl (GstGLContext * context, GstGLVideoMixer * video_mixer)
+{
+  GstGLMixer *mixer = GST_GL_MIXER (video_mixer);
+
   gst_gl_framebuffer_draw_to_texture (mixer->fbo, video_mixer->out_tex,
       gst_gl_video_mixer_callback, video_mixer);
 }