• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Generic video mixer plugin
2  *
3  * GStreamer
4  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <gst/gst.h>
27 #include <gst/video/video.h>
28 
29 #include "gstglbasemixer.h"
30 
31 #define GST_CAT_DEFAULT gst_gl_base_mixer_debug
32 GST_DEBUG_CATEGORY (gst_gl_base_mixer_debug);
33 
34 static void gst_gl_base_mixer_pad_get_property (GObject * object, guint prop_id,
35     GValue * value, GParamSpec * pspec);
36 static void gst_gl_base_mixer_pad_set_property (GObject * object, guint prop_id,
37     const GValue * value, GParamSpec * pspec);
38 
39 static void gst_gl_base_mixer_set_context (GstElement * element,
40     GstContext * context);
41 static GstStateChangeReturn gst_gl_base_mixer_change_state (GstElement *
42     element, GstStateChange transition);
43 
44 static void gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data);
45 static void gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data);
46 
47 struct _GstGLBaseMixerPrivate
48 {
49   gboolean negotiated;
50 
51   GstGLContext *other_context;
52 
53   gboolean gl_started;
54   gboolean gl_result;
55 
56   GRecMutex context_lock;
57 };
58 
59 #define gst_gl_base_mixer_parent_class parent_class
60 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstGLBaseMixer, gst_gl_base_mixer,
61     GST_TYPE_VIDEO_AGGREGATOR);
62 
63 G_DEFINE_TYPE (GstGLBaseMixerPad, gst_gl_base_mixer_pad,
64     GST_TYPE_VIDEO_AGGREGATOR_PAD);
65 
66 static void
gst_gl_base_mixer_pad_class_init(GstGLBaseMixerPadClass * klass)67 gst_gl_base_mixer_pad_class_init (GstGLBaseMixerPadClass * klass)
68 {
69   GObjectClass *gobject_class = (GObjectClass *) klass;
70   GstVideoAggregatorPadClass *vaggpad_class =
71       (GstVideoAggregatorPadClass *) klass;
72 
73   gobject_class->set_property = gst_gl_base_mixer_pad_set_property;
74   gobject_class->get_property = gst_gl_base_mixer_pad_get_property;
75 
76   vaggpad_class->prepare_frame = NULL;
77   vaggpad_class->clean_frame = NULL;
78 
79   gst_type_mark_as_plugin_api (GST_TYPE_GL_BASE_MIXER_PAD, 0);
80 }
81 
82 static void
gst_gl_base_mixer_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)83 gst_gl_base_mixer_pad_get_property (GObject * object, guint prop_id,
84     GValue * value, GParamSpec * pspec)
85 {
86   switch (prop_id) {
87     default:
88       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
89       break;
90   }
91 }
92 
93 static void
gst_gl_base_mixer_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)94 gst_gl_base_mixer_pad_set_property (GObject * object, guint prop_id,
95     const GValue * value, GParamSpec * pspec)
96 {
97   switch (prop_id) {
98     default:
99       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100       break;
101   }
102 }
103 
104 static gboolean
_find_local_gl_context_unlocked(GstGLBaseMixer * mix)105 _find_local_gl_context_unlocked (GstGLBaseMixer * mix)
106 {
107   GstGLContext *context, *prev_context;
108   gboolean ret;
109 
110   if (mix->context && mix->context->display == mix->display)
111     return TRUE;
112 
113   context = prev_context = mix->context;
114   g_rec_mutex_unlock (&mix->priv->context_lock);
115   /* we need to drop the lock to query as another element may also be
116    * performing a context query on us which would also attempt to take the
117    * context_lock. Our query could block on the same lock in the other element.
118    */
119   ret =
120       gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SRC, &context);
121   g_rec_mutex_lock (&mix->priv->context_lock);
122   if (ret) {
123     if (mix->context != prev_context) {
124       /* we need to recheck everything since we dropped the lock and the
125        * context has changed */
126       if (mix->context && mix->context->display == mix->display) {
127         if (context != mix->context)
128           gst_clear_object (&context);
129         return TRUE;
130       }
131     }
132 
133     if (context->display == mix->display) {
134       mix->context = context;
135       return TRUE;
136     }
137     if (context != mix->context)
138       gst_clear_object (&context);
139   }
140 
141   context = prev_context = mix->context;
142   g_rec_mutex_unlock (&mix->priv->context_lock);
143   /* we need to drop the lock to query as another element may also be
144    * performing a context query on us which would also attempt to take the
145    * context_lock. Our query could block on the same lock in the other element.
146    */
147   ret =
148       gst_gl_query_local_gl_context (GST_ELEMENT (mix), GST_PAD_SINK, &context);
149   g_rec_mutex_lock (&mix->priv->context_lock);
150   if (ret) {
151     if (mix->context != prev_context) {
152       /* we need to recheck everything now that we dropped the lock */
153       if (mix->context && mix->context->display == mix->display) {
154         if (context != mix->context)
155           gst_clear_object (&context);
156         return TRUE;
157       }
158     }
159 
160     if (context->display == mix->display) {
161       mix->context = context;
162       return TRUE;
163     }
164     if (context != mix->context)
165       gst_clear_object (&context);
166   }
167 
168   return FALSE;
169 }
170 
171 static gboolean
_get_gl_context_unlocked(GstGLBaseMixer * mix)172 _get_gl_context_unlocked (GstGLBaseMixer * mix)
173 {
174   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
175   gboolean new_context = FALSE;
176   GError *error = NULL;
177 
178   if (!mix->context)
179     new_context = TRUE;
180 
181   if (!gst_gl_ensure_element_data (mix, &mix->display,
182           &mix->priv->other_context))
183     return FALSE;
184 
185   gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
186 
187   _find_local_gl_context_unlocked (mix);
188 
189   GST_OBJECT_LOCK (mix->display);
190   if (!mix->context) {
191     do {
192       if (mix->context) {
193         gst_object_unref (mix->context);
194         mix->context = NULL;
195       }
196       /* just get a GL context.  we don't care */
197       mix->context =
198           gst_gl_display_get_gl_context_for_thread (mix->display, NULL);
199       if (!mix->context) {
200         if (!gst_gl_display_create_context (mix->display,
201                 mix->priv->other_context, &mix->context, &error)) {
202           GST_OBJECT_UNLOCK (mix->display);
203           goto context_error;
204         }
205       }
206     } while (!gst_gl_display_add_context (mix->display, mix->context));
207   }
208   GST_OBJECT_UNLOCK (mix->display);
209 
210   if (new_context || !mix->priv->gl_started) {
211     if (mix->priv->gl_started)
212       gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
213 
214     {
215       if ((gst_gl_context_get_gl_api (mix->
216                   context) & mix_class->supported_gl_api) == 0)
217         goto unsupported_gl_api;
218     }
219 
220     gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_start, mix);
221 
222     if (!mix->priv->gl_started)
223       goto error;
224   }
225 
226   return TRUE;
227 
228 unsupported_gl_api:
229   {
230     GstGLAPI gl_api = gst_gl_context_get_gl_api (mix->context);
231     gchar *gl_api_str = gst_gl_api_to_string (gl_api);
232     gchar *supported_gl_api_str =
233         gst_gl_api_to_string (mix_class->supported_gl_api);
234     GST_ELEMENT_ERROR (mix, RESOURCE, BUSY,
235         ("GL API's not compatible context: %s supported: %s", gl_api_str,
236             supported_gl_api_str), (NULL));
237 
238     g_free (supported_gl_api_str);
239     g_free (gl_api_str);
240     return FALSE;
241   }
242 context_error:
243   {
244     GST_ELEMENT_ERROR (mix, RESOURCE, NOT_FOUND, ("%s", error->message),
245         (NULL));
246     g_clear_error (&error);
247     return FALSE;
248   }
249 error:
250   {
251     GST_ELEMENT_ERROR (mix, LIBRARY, INIT,
252         ("Subclass failed to initialize."), (NULL));
253     return FALSE;
254   }
255 }
256 
257 static gboolean
_get_gl_context(GstGLBaseMixer * mix)258 _get_gl_context (GstGLBaseMixer * mix)
259 {
260   gboolean ret;
261   g_rec_mutex_lock (&mix->priv->context_lock);
262   ret = _get_gl_context_unlocked (mix);
263   g_rec_mutex_unlock (&mix->priv->context_lock);
264   return ret;
265 }
266 
267 static gboolean
gst_gl_base_mixer_propose_allocation(GstAggregator * agg,GstAggregatorPad * aggpad,GstQuery * decide_query,GstQuery * query)268 gst_gl_base_mixer_propose_allocation (GstAggregator * agg,
269     GstAggregatorPad * aggpad, GstQuery * decide_query, GstQuery * query)
270 {
271   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
272 
273   if (!_get_gl_context (mix))
274     return FALSE;
275 
276   return TRUE;
277 }
278 
279 static gboolean
gst_gl_base_mixer_sink_query(GstAggregator * agg,GstAggregatorPad * bpad,GstQuery * query)280 gst_gl_base_mixer_sink_query (GstAggregator * agg, GstAggregatorPad * bpad,
281     GstQuery * query)
282 {
283   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
284 
285   GST_TRACE ("QUERY %" GST_PTR_FORMAT, query);
286 
287   switch (GST_QUERY_TYPE (query)) {
288     case GST_QUERY_CONTEXT:
289     {
290       GstGLDisplay *display = NULL;
291       GstGLContext *other = NULL, *local = NULL;
292       gboolean ret;
293 
294       g_rec_mutex_lock (&mix->priv->context_lock);
295       if (mix->display)
296         display = gst_object_ref (mix->display);
297       if (mix->context)
298         local = gst_object_ref (mix->context);
299       if (mix->priv->other_context)
300         other = gst_object_ref (mix->priv->other_context);
301       g_rec_mutex_unlock (&mix->priv->context_lock);
302 
303       ret = gst_gl_handle_context_query ((GstElement *) mix, query,
304           display, local, other);
305 
306       gst_clear_object (&display);
307       gst_clear_object (&other);
308       gst_clear_object (&local);
309 
310       if (ret)
311         return ret;
312       break;
313     }
314     default:
315       break;
316   }
317 
318   return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query);;
319 }
320 
321 static void
gst_gl_base_mixer_pad_init(GstGLBaseMixerPad * mixerpad)322 gst_gl_base_mixer_pad_init (GstGLBaseMixerPad * mixerpad)
323 {
324 }
325 
326 /* GLBaseMixer signals and args */
327 enum
328 {
329   /* FILL ME */
330   LAST_SIGNAL
331 };
332 
333 enum
334 {
335   PROP_0,
336   PROP_CONTEXT
337 };
338 
339 static gboolean gst_gl_base_mixer_src_query (GstAggregator * agg,
340     GstQuery * query);
341 
342 static gboolean
343 gst_gl_base_mixer_src_activate_mode (GstAggregator * aggregator,
344     GstPadMode mode, gboolean active);
345 static gboolean gst_gl_base_mixer_stop (GstAggregator * agg);
346 static gboolean gst_gl_base_mixer_start (GstAggregator * agg);
347 
348 static void gst_gl_base_mixer_finalize (GObject * object);
349 static void gst_gl_base_mixer_set_property (GObject * object, guint prop_id,
350     const GValue * value, GParamSpec * pspec);
351 static void gst_gl_base_mixer_get_property (GObject * object, guint prop_id,
352     GValue * value, GParamSpec * pspec);
353 
354 static gboolean gst_gl_base_mixer_decide_allocation (GstAggregator * agg,
355     GstQuery * query);
356 
357 static gboolean gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src);
358 static void gst_gl_base_mixer_default_gl_stop (GstGLBaseMixer * src);
359 
360 static void
gst_gl_base_mixer_class_init(GstGLBaseMixerClass * klass)361 gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass)
362 {
363   GObjectClass *gobject_class;
364   GstElementClass *element_class;
365   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
366 
367   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "opengl mixer");
368 
369   gobject_class = (GObjectClass *) klass;
370   element_class = GST_ELEMENT_CLASS (klass);
371 
372   gobject_class->finalize = gst_gl_base_mixer_finalize;
373   gobject_class->get_property = gst_gl_base_mixer_get_property;
374   gobject_class->set_property = gst_gl_base_mixer_set_property;
375 
376   element_class->set_context =
377       GST_DEBUG_FUNCPTR (gst_gl_base_mixer_set_context);
378   element_class->change_state = gst_gl_base_mixer_change_state;
379 
380   agg_class->sink_query = gst_gl_base_mixer_sink_query;
381   agg_class->src_query = gst_gl_base_mixer_src_query;
382   agg_class->src_activate = gst_gl_base_mixer_src_activate_mode;
383   agg_class->stop = gst_gl_base_mixer_stop;
384   agg_class->start = gst_gl_base_mixer_start;
385   agg_class->decide_allocation = gst_gl_base_mixer_decide_allocation;
386   agg_class->propose_allocation = gst_gl_base_mixer_propose_allocation;
387 
388   klass->gl_start = gst_gl_base_mixer_default_gl_start;
389   klass->gl_stop = gst_gl_base_mixer_default_gl_stop;
390 
391   g_object_class_install_property (gobject_class, PROP_CONTEXT,
392       g_param_spec_object ("context",
393           "OpenGL context",
394           "Get OpenGL context",
395           GST_TYPE_GL_CONTEXT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
396 
397   /* Register the pad class */
398   g_type_class_ref (GST_TYPE_GL_BASE_MIXER_PAD);
399 
400   klass->supported_gl_api = GST_GL_API_ANY;
401 
402   gst_type_mark_as_plugin_api (GST_TYPE_GL_BASE_MIXER, 0);
403 }
404 
405 static void
gst_gl_base_mixer_init(GstGLBaseMixer * mix)406 gst_gl_base_mixer_init (GstGLBaseMixer * mix)
407 {
408   mix->priv = gst_gl_base_mixer_get_instance_private (mix);
409 
410   g_rec_mutex_init (&mix->priv->context_lock);
411 }
412 
413 static void
gst_gl_base_mixer_finalize(GObject * object)414 gst_gl_base_mixer_finalize (GObject * object)
415 {
416   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (object);
417 
418   g_rec_mutex_clear (&mix->priv->context_lock);
419 
420   G_OBJECT_CLASS (parent_class)->finalize (object);
421 }
422 
423 static gboolean
gst_gl_base_mixer_default_gl_start(GstGLBaseMixer * src)424 gst_gl_base_mixer_default_gl_start (GstGLBaseMixer * src)
425 {
426   return TRUE;
427 }
428 
429 static void
gst_gl_base_mixer_gl_start(GstGLContext * context,gpointer data)430 gst_gl_base_mixer_gl_start (GstGLContext * context, gpointer data)
431 {
432   GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
433   GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
434 
435   GST_INFO_OBJECT (src, "starting");
436   gst_gl_insert_debug_marker (src->context,
437       "starting element %s", GST_OBJECT_NAME (src));
438 
439   src->priv->gl_started = src_class->gl_start (src);
440 }
441 
442 static void
gst_gl_base_mixer_default_gl_stop(GstGLBaseMixer * src)443 gst_gl_base_mixer_default_gl_stop (GstGLBaseMixer * src)
444 {
445 }
446 
447 static void
gst_gl_base_mixer_gl_stop(GstGLContext * context,gpointer data)448 gst_gl_base_mixer_gl_stop (GstGLContext * context, gpointer data)
449 {
450   GstGLBaseMixer *src = GST_GL_BASE_MIXER (data);
451   GstGLBaseMixerClass *src_class = GST_GL_BASE_MIXER_GET_CLASS (src);
452 
453   GST_INFO_OBJECT (src, "stopping");
454   gst_gl_insert_debug_marker (src->context,
455       "stopping element %s", GST_OBJECT_NAME (src));
456 
457   if (src->priv->gl_started)
458     src_class->gl_stop (src);
459 
460   src->priv->gl_started = FALSE;
461 }
462 
463 static void
gst_gl_base_mixer_set_context(GstElement * element,GstContext * context)464 gst_gl_base_mixer_set_context (GstElement * element, GstContext * context)
465 {
466   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element);
467   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
468   GstGLDisplay *old_display, *new_display;
469 
470   g_rec_mutex_lock (&mix->priv->context_lock);
471   old_display = mix->display ? gst_object_ref (mix->display) : NULL;
472   gst_gl_handle_set_context (element, context, &mix->display,
473       &mix->priv->other_context);
474   if (mix->display)
475     gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
476   new_display = mix->display ? gst_object_ref (mix->display) : NULL;
477 
478   if (old_display && new_display) {
479     if (old_display != new_display) {
480       gst_clear_object (&mix->context);
481       _get_gl_context_unlocked (mix);
482       gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (mix));
483     }
484   }
485   gst_clear_object (&old_display);
486   gst_clear_object (&new_display);
487   g_rec_mutex_unlock (&mix->priv->context_lock);
488 
489   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
490 }
491 
492 static gboolean
gst_gl_base_mixer_activate(GstGLBaseMixer * mix,gboolean active)493 gst_gl_base_mixer_activate (GstGLBaseMixer * mix, gboolean active)
494 {
495   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
496   gboolean result = TRUE;
497 
498   if (active) {
499     g_rec_mutex_lock (&mix->priv->context_lock);
500     if (!gst_gl_ensure_element_data (mix, &mix->display,
501             &mix->priv->other_context)) {
502       g_rec_mutex_unlock (&mix->priv->context_lock);
503       return FALSE;
504     }
505 
506     gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
507     g_rec_mutex_unlock (&mix->priv->context_lock);
508   }
509 
510   return result;
511 }
512 
513 static gboolean
gst_gl_base_mixer_src_activate_mode(GstAggregator * aggregator,GstPadMode mode,gboolean active)514 gst_gl_base_mixer_src_activate_mode (GstAggregator * aggregator,
515     GstPadMode mode, gboolean active)
516 {
517   GstGLBaseMixer *mix;
518   gboolean result = FALSE;
519 
520   mix = GST_GL_BASE_MIXER (aggregator);
521 
522   switch (mode) {
523     case GST_PAD_MODE_PUSH:
524     case GST_PAD_MODE_PULL:
525       result = gst_gl_base_mixer_activate (mix, active);
526       break;
527     default:
528       result = TRUE;
529       break;
530   }
531   return result;
532 }
533 
534 static gboolean
gst_gl_base_mixer_src_query(GstAggregator * agg,GstQuery * query)535 gst_gl_base_mixer_src_query (GstAggregator * agg, GstQuery * query)
536 {
537   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
538 
539   switch (GST_QUERY_TYPE (query)) {
540     case GST_QUERY_CONTEXT:
541     {
542       GstGLDisplay *display = NULL;
543       GstGLContext *other = NULL, *local = NULL;
544       gboolean ret;
545 
546       g_rec_mutex_lock (&mix->priv->context_lock);
547       if (mix->display)
548         display = gst_object_ref (mix->display);
549       if (mix->context)
550         local = gst_object_ref (mix->context);
551       if (mix->priv->other_context)
552         other = gst_object_ref (mix->priv->other_context);
553       g_rec_mutex_unlock (&mix->priv->context_lock);
554 
555       ret = gst_gl_handle_context_query ((GstElement *) mix, query,
556           display, local, other);
557 
558       gst_clear_object (&display);
559       gst_clear_object (&other);
560       gst_clear_object (&local);
561 
562       if (ret)
563         return ret;
564       break;
565     }
566     default:
567       break;
568   }
569 
570   return GST_AGGREGATOR_CLASS (parent_class)->src_query (agg, query);
571 }
572 
573 static gboolean
gst_gl_base_mixer_decide_allocation(GstAggregator * agg,GstQuery * query)574 gst_gl_base_mixer_decide_allocation (GstAggregator * agg, GstQuery * query)
575 {
576   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
577 
578   if (!_get_gl_context (mix))
579     return FALSE;
580 
581   return TRUE;
582 }
583 
584 static void
gst_gl_base_mixer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)585 gst_gl_base_mixer_get_property (GObject * object,
586     guint prop_id, GValue * value, GParamSpec * pspec)
587 {
588   GstGLBaseMixer *mixer = GST_GL_BASE_MIXER (object);
589 
590   switch (prop_id) {
591     case PROP_CONTEXT:
592       g_value_set_object (value, mixer->context);
593       break;
594     default:
595       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
596       break;
597   }
598 }
599 
600 static void
gst_gl_base_mixer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)601 gst_gl_base_mixer_set_property (GObject * object,
602     guint prop_id, const GValue * value, GParamSpec * pspec)
603 {
604   switch (prop_id) {
605     default:
606       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
607       break;
608   }
609 }
610 
611 static gboolean
gst_gl_base_mixer_start(GstAggregator * agg)612 gst_gl_base_mixer_start (GstAggregator * agg)
613 {
614   return GST_AGGREGATOR_CLASS (parent_class)->start (agg);;
615 }
616 
617 static gboolean
gst_gl_base_mixer_stop(GstAggregator * agg)618 gst_gl_base_mixer_stop (GstAggregator * agg)
619 {
620   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (agg);
621 
622   g_rec_mutex_lock (&mix->priv->context_lock);
623   if (mix->priv->gl_started)
624     gst_gl_context_thread_add (mix->context, gst_gl_base_mixer_gl_stop, mix);
625   gst_clear_object (&mix->context);
626   g_rec_mutex_unlock (&mix->priv->context_lock);
627 
628   return TRUE;
629 }
630 
631 static GstStateChangeReturn
gst_gl_base_mixer_change_state(GstElement * element,GstStateChange transition)632 gst_gl_base_mixer_change_state (GstElement * element, GstStateChange transition)
633 {
634   GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element);
635   GstGLBaseMixerClass *mix_class = GST_GL_BASE_MIXER_GET_CLASS (mix);
636   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
637 
638   GST_DEBUG_OBJECT (mix, "changing state: %s => %s",
639       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
640       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
641 
642   switch (transition) {
643     case GST_STATE_CHANGE_NULL_TO_READY:
644       if (!gst_gl_ensure_element_data (element, &mix->display,
645               &mix->priv->other_context))
646         return GST_STATE_CHANGE_FAILURE;
647 
648       gst_gl_display_filter_gl_api (mix->display, mix_class->supported_gl_api);
649       break;
650     default:
651       break;
652   }
653 
654   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
655   if (ret == GST_STATE_CHANGE_FAILURE)
656     return ret;
657 
658   switch (transition) {
659     case GST_STATE_CHANGE_READY_TO_NULL:
660       if (mix->priv->other_context) {
661         gst_object_unref (mix->priv->other_context);
662         mix->priv->other_context = NULL;
663       }
664 
665       g_rec_mutex_lock (&mix->priv->context_lock);
666       gst_clear_object (&mix->display);
667       g_rec_mutex_unlock (&mix->priv->context_lock);
668       break;
669     default:
670       break;
671   }
672 
673   return ret;
674 }
675 
676 /**
677  * gst_gl_base_mixer_get_gl_context:
678  * @mix: a #GstGLBaseMixer
679  *
680  * Returns: (transfer full) (nullable): the #GstGLContext found by @mix
681  *
682  * Since: 1.18
683  */
684 GstGLContext *
gst_gl_base_mixer_get_gl_context(GstGLBaseMixer * mix)685 gst_gl_base_mixer_get_gl_context (GstGLBaseMixer * mix)
686 {
687   GstGLContext *ret;
688 
689   g_return_val_if_fail (GST_IS_GL_BASE_MIXER (mix), NULL);
690 
691   g_rec_mutex_lock (&mix->priv->context_lock);
692   ret = mix->context ? gst_object_ref (mix->context) : NULL;
693   g_rec_mutex_unlock (&mix->priv->context_lock);
694   return ret;
695 }
696