1 /*
2  * GStreamer
3  * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
4  * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
5  * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
6  * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /**
25  * SECTION:gstgldisplay
26  * @short_description: window system display connection abstraction
27  * @title: GstGLDisplay
28  * @see_also: #GstContext, #GstGLContext, #GstGLWindow
29  *
30  * #GstGLDisplay represents a connection to the underlying windowing system.
31  * Elements are required to make use of #GstContext to share and propogate
32  * a #GstGLDisplay.
33  *
34  * There are a number of environment variables that influence the choice of
35  * platform and window system specific functionality.
36  * - GST_GL_WINDOW influences the window system to use.  Common values are
37  *   'x11', 'wayland', 'win32' or 'cocoa'.
38  * - GST_GL_PLATFORM influences the OpenGL platform to use.  Common values are
39  *   'egl', 'glx', 'wgl' or 'cgl'.
40  * - GST_GL_API influences the OpenGL API requested by the OpenGL platform.
41  *   Common values are 'opengl', 'opengl3' and 'gles2'.
42  *
43  * > Certain window systems require a special function to be called to
44  * > initialize threading support.  As this GStreamer GL library does not preclude
45  * > concurrent access to the windowing system, it is strongly advised that
46  * > applications ensure that threading support has been initialized before any
47  * > other toolkit/library functionality is accessed.  Failure to do so could
48  * > result in sudden application abortion during execution.  The most notably
49  * > example of such a function is X11's XInitThreads\().
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #include "config.h"
54 #endif
55 
56 #include "gl.h"
57 #include "gstgldisplay.h"
58 
59 #if GST_GL_HAVE_WINDOW_COCOA
60 #include <gst/gl/cocoa/gstgldisplay_cocoa.h>
61 #endif
62 #if GST_GL_HAVE_WINDOW_X11
63 #include <gst/gl/x11/gstgldisplay_x11.h>
64 #endif
65 #if GST_GL_HAVE_WINDOW_WAYLAND
66 #include <gst/gl/wayland/gstgldisplay_wayland.h>
67 #endif
68 #if GST_GL_HAVE_PLATFORM_EGL
69 #include <gst/gl/egl/gstgldisplay_egl.h>
70 #include <gst/gl/egl/gsteglimage.h>
71 #include <gst/gl/egl/gstglmemoryegl.h>
72 #endif
73 #if GST_GL_HAVE_WINDOW_VIV_FB
74 #include <gst/gl/viv-fb/gstgldisplay_viv_fb.h>
75 #endif
76 #if GST_GL_HAVE_WINDOW_GBM
77 #include <gst/gl/gbm/gstgldisplay_gbm.h>
78 #endif
79 
80 GST_DEBUG_CATEGORY_STATIC (gst_context);
81 GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
82 #define GST_CAT_DEFAULT gst_gl_display_debug
83 
84 enum
85 {
86   SIGNAL_0,
87   CREATE_CONTEXT,
88   LAST_SIGNAL
89 };
90 
91 static guint gst_gl_display_signals[LAST_SIGNAL] = { 0 };
92 
93 
94 static void gst_gl_display_dispose (GObject * object);
95 static void gst_gl_display_finalize (GObject * object);
96 static guintptr gst_gl_display_default_get_handle (GstGLDisplay * display);
97 static GstGLWindow *gst_gl_display_default_create_window (GstGLDisplay *
98     display);
99 
100 struct _GstGLDisplayPrivate
101 {
102   GstGLAPI gl_api;
103 
104   GList *contexts;
105 
106   GThread *event_thread;
107 
108   GMutex thread_lock;
109   GCond thread_cond;
110 };
111 
112 #define DEBUG_INIT \
113   GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display"); \
114   GST_DEBUG_CATEGORY_GET (gst_context, "GST_CONTEXT");
115 
116 G_DEFINE_TYPE_WITH_CODE (GstGLDisplay, gst_gl_display, GST_TYPE_OBJECT,
117     G_ADD_PRIVATE (GstGLDisplay)
118     DEBUG_INIT);
119 
120 static gboolean
_unlock_main_thread(GstGLDisplay * display)121 _unlock_main_thread (GstGLDisplay * display)
122 {
123   g_mutex_unlock (&display->priv->thread_lock);
124 
125   return G_SOURCE_REMOVE;
126 }
127 
128 static gpointer
_event_thread_main(GstGLDisplay * display)129 _event_thread_main (GstGLDisplay * display)
130 {
131   g_mutex_lock (&display->priv->thread_lock);
132 
133   display->main_context = g_main_context_new ();
134   display->main_loop = g_main_loop_new (display->main_context, FALSE);
135 
136   g_main_context_invoke (display->main_context,
137       (GSourceFunc) _unlock_main_thread, display);
138 
139   g_cond_broadcast (&display->priv->thread_cond);
140 
141   g_main_loop_run (display->main_loop);
142 
143   g_mutex_lock (&display->priv->thread_lock);
144   g_main_loop_unref (display->main_loop);
145   g_main_context_unref (display->main_context);
146 
147   display->main_loop = NULL;
148   display->main_context = NULL;
149 
150   g_cond_broadcast (&display->priv->thread_cond);
151   g_mutex_unlock (&display->priv->thread_lock);
152 
153   return NULL;
154 }
155 
156 static void
gst_gl_display_class_init(GstGLDisplayClass * klass)157 gst_gl_display_class_init (GstGLDisplayClass * klass)
158 {
159   /**
160    * GstGLDisplay::create-context:
161    * @object: the #GstGLDisplay
162    * @context: (transfer none): other context to share resources with.
163    *
164    * Overrides the @GstGLContext creation mechanism.
165    * It can be called in any thread and it is emitted with
166    * display's object lock held.
167    *
168    * Returns: (transfer full): the new context.
169    */
170   gst_gl_display_signals[CREATE_CONTEXT] =
171       g_signal_new ("create-context", G_TYPE_FROM_CLASS (klass),
172       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
173       GST_TYPE_GL_CONTEXT, 1, GST_TYPE_GL_CONTEXT);
174 
175   klass->get_handle = gst_gl_display_default_get_handle;
176   klass->create_window = gst_gl_display_default_create_window;
177 
178   G_OBJECT_CLASS (klass)->finalize = gst_gl_display_finalize;
179   G_OBJECT_CLASS (klass)->dispose = gst_gl_display_dispose;
180 }
181 
182 static void
gst_gl_display_init(GstGLDisplay * display)183 gst_gl_display_init (GstGLDisplay * display)
184 {
185   display->priv = gst_gl_display_get_instance_private (display);
186 
187   display->type = GST_GL_DISPLAY_TYPE_ANY;
188   display->priv->gl_api = GST_GL_API_ANY;
189 
190   g_mutex_init (&display->priv->thread_lock);
191   g_cond_init (&display->priv->thread_cond);
192 
193   display->priv->event_thread = g_thread_new ("gldisplay-event",
194       (GThreadFunc) _event_thread_main, display);
195 
196   g_mutex_lock (&display->priv->thread_lock);
197   while (!display->main_loop)
198     g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
199   g_mutex_unlock (&display->priv->thread_lock);
200 
201   GST_TRACE ("init %p", display);
202 
203   gst_gl_buffer_init_once ();
204   gst_gl_memory_pbo_init_once ();
205   gst_gl_renderbuffer_init_once ();
206 
207 #if GST_GL_HAVE_PLATFORM_EGL
208   gst_gl_memory_egl_init_once ();
209 #endif
210 }
211 
212 static void
gst_gl_display_dispose(GObject * object)213 gst_gl_display_dispose (GObject * object)
214 {
215   GstGLDisplay *display = GST_GL_DISPLAY (object);
216 
217   if (display->main_loop)
218     g_main_loop_quit (display->main_loop);
219 
220   if (display->priv->event_thread) {
221     /* can't use g_thread_join() as we could lose the last ref from a user
222      * function */
223     g_mutex_lock (&display->priv->thread_lock);
224     while (display->main_loop)
225       g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
226     g_mutex_unlock (&display->priv->thread_lock);
227     g_thread_unref (display->priv->event_thread);
228   }
229   display->priv->event_thread = NULL;
230 
231   if (display->event_source) {
232     g_source_destroy (display->event_source);
233     g_source_unref (display->event_source);
234   }
235   display->event_source = NULL;
236 
237   G_OBJECT_CLASS (gst_gl_display_parent_class)->dispose (object);
238 }
239 
240 static void
gst_gl_display_finalize(GObject * object)241 gst_gl_display_finalize (GObject * object)
242 {
243   GstGLDisplay *display = GST_GL_DISPLAY (object);
244   GList *l;
245 
246   GST_TRACE_OBJECT (object, "finalizing");
247 
248   for (l = display->priv->contexts; l; l = l->next) {
249     g_weak_ref_clear ((GWeakRef *) l->data);
250     g_free (l->data);
251   }
252 
253   g_list_free (display->windows);
254   g_list_free (display->priv->contexts);
255 
256   g_cond_clear (&display->priv->thread_cond);
257   g_mutex_clear (&display->priv->thread_lock);
258 
259   G_OBJECT_CLASS (gst_gl_display_parent_class)->finalize (object);
260 }
261 
262 /**
263  * gst_gl_display_new:
264  *
265  * Returns: (transfer full): a new #GstGLDisplay
266  *
267  * Since: 1.4
268  */
269 GstGLDisplay *
gst_gl_display_new(void)270 gst_gl_display_new (void)
271 {
272   GstGLDisplay *display = NULL;
273   const gchar *user_choice, *platform_choice;
274   static volatile gsize _init = 0;
275 
276   if (g_once_init_enter (&_init)) {
277     GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0,
278         "gldisplay element");
279     g_once_init_leave (&_init, 1);
280   }
281 
282   user_choice = g_getenv ("GST_GL_WINDOW");
283   platform_choice = g_getenv ("GST_GL_PLATFORM");
284   GST_INFO ("creating a display, user choice:%s (platform: %s)",
285       GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
286 
287 #if GST_GL_HAVE_WINDOW_COCOA
288   if (!display && (!user_choice || g_strstr_len (user_choice, 5, "cocoa"))) {
289     display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
290     if (!display)
291       return NULL;
292   }
293 #endif
294 #if GST_GL_HAVE_WINDOW_WAYLAND
295   if (!display && (!user_choice || g_strstr_len (user_choice, 7, "wayland")))
296     display = GST_GL_DISPLAY (gst_gl_display_wayland_new (NULL));
297 #endif
298 #if GST_GL_HAVE_WINDOW_X11
299   if (!display && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
300     display = GST_GL_DISPLAY (gst_gl_display_x11_new (NULL));
301 #endif
302 #if GST_GL_HAVE_WINDOW_VIV_FB
303   if (!display && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb"))) {
304     const gchar *disp_idx_str = NULL;
305     gint disp_idx = 0;
306     disp_idx_str = g_getenv ("GST_GL_VIV_FB");
307     if (disp_idx_str) {
308       gint64 v = g_ascii_strtoll (disp_idx_str, NULL, 10);
309       if (v >= G_MININT && v <= G_MAXINT)
310         disp_idx = v;
311     }
312     display = GST_GL_DISPLAY (gst_gl_display_viv_fb_new (disp_idx));
313   }
314 #endif
315 #if GST_GL_HAVE_WINDOW_GBM
316   if (!display && (!user_choice || g_strstr_len (user_choice, 3, "gbm"))) {
317     display = GST_GL_DISPLAY (gst_gl_display_gbm_new ());
318   }
319 #endif
320 #if GST_GL_HAVE_PLATFORM_EGL
321   if (!display && (!platform_choice
322           || g_strstr_len (platform_choice, 3, "egl")))
323     display = GST_GL_DISPLAY (gst_gl_display_egl_new ());
324 #endif
325   if (!display) {
326     GST_INFO ("Could not create platform/winsys display. user specified %s "
327         "(platform: %s), creating dummy",
328         GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
329 
330     display = g_object_new (GST_TYPE_GL_DISPLAY, NULL);
331     gst_object_ref_sink (display);
332   }
333 
334   return display;
335 }
336 
337 /**
338  * gst_gl_display_get_handle:
339  * @display: a #GstGLDisplay
340  *
341  * Returns: the native handle for the display
342  *
343  * Since: 1.4
344  */
345 guintptr
gst_gl_display_get_handle(GstGLDisplay * display)346 gst_gl_display_get_handle (GstGLDisplay * display)
347 {
348   GstGLDisplayClass *klass;
349 
350   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), 0);
351   klass = GST_GL_DISPLAY_GET_CLASS (display);
352   g_return_val_if_fail (klass->get_handle != NULL, 0);
353 
354   return klass->get_handle (display);
355 }
356 
357 static guintptr
gst_gl_display_default_get_handle(GstGLDisplay * display)358 gst_gl_display_default_get_handle (GstGLDisplay * display)
359 {
360   return 0;
361 }
362 
363 /**
364  * gst_gl_display_filter_gl_api:
365  * @display: a #GstGLDisplay
366  * @gl_api: a #GstGLAPI to filter with
367  *
368  * limit the use of OpenGL to the requested @gl_api.  This is intended to allow
369  * application and elements to request a specific set of OpenGL API's based on
370  * what they support.  See gst_gl_context_get_gl_api() for the retreiving the
371  * API supported by a #GstGLContext.
372  */
373 void
gst_gl_display_filter_gl_api(GstGLDisplay * display,GstGLAPI gl_api)374 gst_gl_display_filter_gl_api (GstGLDisplay * display, GstGLAPI gl_api)
375 {
376   gchar *gl_api_s;
377 
378   g_return_if_fail (GST_IS_GL_DISPLAY (display));
379 
380   gl_api_s = gst_gl_api_to_string (gl_api);
381   GST_TRACE_OBJECT (display, "filtering with api %s", gl_api_s);
382   g_free (gl_api_s);
383 
384   GST_OBJECT_LOCK (display);
385   display->priv->gl_api &= gl_api;
386   GST_OBJECT_UNLOCK (display);
387 }
388 
389 GstGLAPI
gst_gl_display_get_gl_api_unlocked(GstGLDisplay * display)390 gst_gl_display_get_gl_api_unlocked (GstGLDisplay * display)
391 {
392   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
393 
394   return display->priv->gl_api;
395 }
396 
397 /**
398  * gst_gl_display_get_gl_api:
399  * @display: a #GstGLDisplay
400  *
401  * see gst_gl_display_filter_gl_api() for what the returned value represents
402  *
403  * Returns: the #GstGLAPI configured for @display
404  */
405 GstGLAPI
gst_gl_display_get_gl_api(GstGLDisplay * display)406 gst_gl_display_get_gl_api (GstGLDisplay * display)
407 {
408   GstGLAPI ret;
409 
410   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
411 
412   GST_OBJECT_LOCK (display);
413   ret = display->priv->gl_api;
414   GST_OBJECT_UNLOCK (display);
415 
416   return ret;
417 }
418 
419 /**
420  * gst_gl_display_get_handle_type:
421  * @display: a #GstGLDisplay
422  *
423  * Returns: the #GstGLDisplayType of @display
424  *
425  * Since: 1.4
426  */
427 GstGLDisplayType
gst_gl_display_get_handle_type(GstGLDisplay * display)428 gst_gl_display_get_handle_type (GstGLDisplay * display)
429 {
430   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_DISPLAY_TYPE_NONE);
431 
432   return display->type;
433 }
434 
435 /**
436  * gst_context_set_gl_display:
437  * @context: a #GstContext
438  * @display: (transfer none): resulting #GstGLDisplay
439  *
440  * Sets @display on @context
441  *
442  * Since: 1.4
443  */
444 void
gst_context_set_gl_display(GstContext * context,GstGLDisplay * display)445 gst_context_set_gl_display (GstContext * context, GstGLDisplay * display)
446 {
447   GstStructure *s;
448 
449   g_return_if_fail (context != NULL);
450 
451   if (display)
452     GST_CAT_LOG (gst_context,
453         "setting GstGLDisplay(%" GST_PTR_FORMAT ") on context(%" GST_PTR_FORMAT
454         ")", display, context);
455 
456   s = gst_context_writable_structure (context);
457   gst_structure_set (s, GST_GL_DISPLAY_CONTEXT_TYPE, GST_TYPE_GL_DISPLAY,
458       display, NULL);
459 }
460 
461 /**
462  * gst_context_get_gl_display:
463  * @context: a #GstContext
464  * @display: (out) (transfer full): resulting #GstGLDisplay
465  *
466  * Returns: Whether @display was in @context
467  *
468  * Since: 1.4
469  */
470 gboolean
gst_context_get_gl_display(GstContext * context,GstGLDisplay ** display)471 gst_context_get_gl_display (GstContext * context, GstGLDisplay ** display)
472 {
473   const GstStructure *s;
474   gboolean ret;
475 
476   g_return_val_if_fail (display != NULL, FALSE);
477   g_return_val_if_fail (context != NULL, FALSE);
478 
479   s = gst_context_get_structure (context);
480   ret = gst_structure_get (s, GST_GL_DISPLAY_CONTEXT_TYPE,
481       GST_TYPE_GL_DISPLAY, display, NULL);
482 
483   GST_CAT_LOG (gst_context, "got GstGLDisplay(%p) from context(%p)", *display,
484       context);
485 
486   return ret;
487 }
488 
489 /**
490  * gst_gl_display_create_context:
491  * @display: a #GstGLDisplay
492  * @other_context: (transfer none): other #GstGLContext to share resources with.
493  * @p_context: (transfer full) (out): resulting #GstGLContext
494  * @error: (allow-none): resulting #GError
495  *
496  * It requires the display's object lock to be held.
497  *
498  * Returns: whether a new context could be created.
499  *
500  * Since: 1.6
501  */
502 gboolean
gst_gl_display_create_context(GstGLDisplay * display,GstGLContext * other_context,GstGLContext ** p_context,GError ** error)503 gst_gl_display_create_context (GstGLDisplay * display,
504     GstGLContext * other_context, GstGLContext ** p_context, GError ** error)
505 {
506   GstGLContext *context = NULL;
507   gboolean ret = FALSE;
508 
509   g_return_val_if_fail (display != NULL, FALSE);
510   g_return_val_if_fail (p_context != NULL, FALSE);
511   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
512 
513   g_signal_emit (display, gst_gl_display_signals[CREATE_CONTEXT], 0,
514       other_context, &context);
515 
516   if (context) {
517     *p_context = context;
518     return TRUE;
519   }
520 
521   context = gst_gl_context_new (display);
522   if (!context) {
523     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
524         "Failed to create GL context");
525     return FALSE;
526   }
527 
528   GST_DEBUG_OBJECT (display,
529       "creating context %" GST_PTR_FORMAT " from other context %"
530       GST_PTR_FORMAT, context, other_context);
531 
532   ret = gst_gl_context_create (context, other_context, error);
533 
534   if (ret)
535     *p_context = context;
536 
537   return ret;
538 }
539 
540 /**
541  * gst_gl_display_create_window:
542  * @display: a #GstGLDisplay
543  *
544  * It requires the display's object lock to be held.
545  *
546  * Returns: (transfer full): a new #GstGLWindow for @display or %NULL.
547  */
548 GstGLWindow *
gst_gl_display_create_window(GstGLDisplay * display)549 gst_gl_display_create_window (GstGLDisplay * display)
550 {
551   GstGLDisplayClass *klass;
552   GstGLWindow *window;
553 
554   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
555   klass = GST_GL_DISPLAY_GET_CLASS (display);
556   g_return_val_if_fail (klass->create_window != NULL, NULL);
557 
558   window = klass->create_window (display);
559 
560   if (window)
561     display->windows = g_list_prepend (display->windows, window);
562 
563   return window;
564 }
565 
566 static GstGLWindow *
gst_gl_display_default_create_window(GstGLDisplay * display)567 gst_gl_display_default_create_window (GstGLDisplay * display)
568 {
569   return gst_gl_window_new (display);
570 }
571 
572 /**
573  * gst_gl_display_remove_window:
574  * @display: a #GstGLDisplay
575  * @window: a #GstGLWindow to remove
576  *
577  * Returns: if @window could be removed from @display
578  *
579  * Since: 1.12
580  */
581 gboolean
gst_gl_display_remove_window(GstGLDisplay * display,GstGLWindow * window)582 gst_gl_display_remove_window (GstGLDisplay * display, GstGLWindow * window)
583 {
584   gboolean ret = FALSE;
585   GList *l;
586 
587   GST_OBJECT_LOCK (display);
588   l = g_list_find (display->windows, window);
589   if (l) {
590     display->windows = g_list_delete_link (display->windows, l);
591     ret = TRUE;
592   }
593   GST_OBJECT_UNLOCK (display);
594 
595   return ret;
596 }
597 
598 /**
599  * gst_gl_display_find_window:
600  * @display: a #GstGLDisplay
601  * @data: (closure): some data to pass to @compare_func
602  * @compare_func: (scope call): a comparison function to run
603  *
604  * Execute @compare_func over the list of windows stored by @display.  The
605  * first argment to @compare_func is the #GstGLWindow being checked and the
606  * second argument is @data.
607  *
608  * Returns: (transfer none): The first #GstGLWindow that causes a match
609  *          from @compare_func
610  *
611  * Since: 1.12
612  */
613 GstGLWindow *
gst_gl_display_find_window(GstGLDisplay * display,gpointer data,GCompareFunc compare_func)614 gst_gl_display_find_window (GstGLDisplay * display, gpointer data,
615     GCompareFunc compare_func)
616 {
617   GstGLWindow *ret = NULL;
618   GList *l;
619 
620   GST_OBJECT_LOCK (display);
621   l = g_list_find_custom (display->windows, data, compare_func);
622   if (l)
623     ret = l->data;
624   GST_OBJECT_UNLOCK (display);
625 
626   return ret;
627 }
628 
629 static GstGLContext *
_get_gl_context_for_thread_unlocked(GstGLDisplay * display,GThread * thread)630 _get_gl_context_for_thread_unlocked (GstGLDisplay * display, GThread * thread)
631 {
632   GstGLContext *context = NULL;
633   GList *prev = NULL, *l = display->priv->contexts;
634 
635   while (l) {
636     GWeakRef *ref = l->data;
637     GThread *context_thread;
638 
639     context = g_weak_ref_get (ref);
640     if (!context) {
641       /* remove dead contexts */
642       g_weak_ref_clear (l->data);
643       g_free (l->data);
644       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
645       l = prev ? prev->next : display->priv->contexts;
646       continue;
647     }
648 
649     if (thread == NULL) {
650       GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
651           "NULL thread", context);
652       return context;
653     }
654 
655     context_thread = gst_gl_context_get_thread (context);
656     if (thread != context_thread) {
657       g_thread_unref (context_thread);
658       gst_object_unref (context);
659       prev = l;
660       l = l->next;
661       continue;
662     }
663 
664     if (context_thread)
665       g_thread_unref (context_thread);
666 
667     GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
668         "thread %p", context, thread);
669     return context;
670   }
671 
672   GST_DEBUG_OBJECT (display, "No GL context for thread %p", thread);
673   return NULL;
674 }
675 
676 /**
677  * gst_gl_display_get_gl_context_for_thread:
678  * @display: a #GstGLDisplay
679  * @thread: a #GThread
680  *
681  * Returns: (transfer full): the #GstGLContext current on @thread or %NULL
682  *
683  * Must be called with the object lock held.
684  *
685  * Since: 1.6
686  */
687 GstGLContext *
gst_gl_display_get_gl_context_for_thread(GstGLDisplay * display,GThread * thread)688 gst_gl_display_get_gl_context_for_thread (GstGLDisplay * display,
689     GThread * thread)
690 {
691   GstGLContext *context;
692 
693   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
694 
695   context = _get_gl_context_for_thread_unlocked (display, thread);
696   GST_DEBUG_OBJECT (display, "returning context %" GST_PTR_FORMAT " for thread "
697       "%p", context, thread);
698 
699   return context;
700 }
701 
702 static gboolean
_check_collision(GstGLContext * context,GstGLContext * collision)703 _check_collision (GstGLContext * context, GstGLContext * collision)
704 {
705   GThread *thread, *collision_thread;
706   gboolean ret = FALSE;
707 
708   if (!context || !collision)
709     return FALSE;
710 
711   thread = gst_gl_context_get_thread (context);
712   collision_thread = gst_gl_context_get_thread (collision);
713 
714   if (!thread || !collision_thread) {
715     ret = FALSE;
716     goto out;
717   }
718 
719   if (thread == collision_thread) {
720     ret = TRUE;
721     goto out;
722   }
723 
724 out:
725   if (thread)
726     g_thread_unref (thread);
727   if (collision_thread)
728     g_thread_unref (collision_thread);
729 
730   return ret;
731 }
732 
733 /**
734  * gst_gl_display_add_context:
735  * @display: a #GstGLDisplay
736  * @context: (transfer none): a #GstGLContext
737  *
738  * Returns: whether @context was successfully added. %FALSE may be returned
739  * if there already exists another context for @context's active thread.
740  *
741  * Must be called with the object lock held.
742  *
743  * Since: 1.6
744  */
745 gboolean
gst_gl_display_add_context(GstGLDisplay * display,GstGLContext * context)746 gst_gl_display_add_context (GstGLDisplay * display, GstGLContext * context)
747 {
748   GstGLContext *collision = NULL;
749   GstGLDisplay *context_display;
750   gboolean ret = TRUE;
751   GThread *thread;
752   GWeakRef *ref;
753 
754   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), FALSE);
755   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
756 
757   context_display = gst_gl_context_get_display (context);
758   g_assert (context_display == display);
759   gst_object_unref (context_display);
760 
761   thread = gst_gl_context_get_thread (context);
762   if (thread) {
763     collision = _get_gl_context_for_thread_unlocked (display, thread);
764     g_thread_unref (thread);
765 
766     /* adding the same context is a no-op */
767     if (context == collision) {
768       GST_LOG_OBJECT (display, "Attempting to add the same GL context %"
769           GST_PTR_FORMAT ". Ignoring", context);
770       ret = TRUE;
771       goto out;
772     }
773 
774     if (_check_collision (context, collision)) {
775       GST_DEBUG_OBJECT (display, "Collision detected adding GL context "
776           "%" GST_PTR_FORMAT, context);
777       ret = FALSE;
778       goto out;
779     }
780   }
781 
782   ref = g_new0 (GWeakRef, 1);
783   g_weak_ref_init (ref, context);
784 
785   GST_DEBUG_OBJECT (display, "Adding GL context %" GST_PTR_FORMAT, context);
786   display->priv->contexts = g_list_prepend (display->priv->contexts, ref);
787 
788 out:
789   if (collision)
790     gst_object_unref (collision);
791 
792   GST_DEBUG_OBJECT (display, "%ssuccessfully inserted context %" GST_PTR_FORMAT,
793       ret ? "" : "un", context);
794 
795   return ret;
796 }
797