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