Revert "gstgldisplay: Add public foreign_display property"
[platform/upstream/gstreamer.git] / gst-libs / gst / gl / gstgldisplay.c
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 propagate
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/gstgldisplay_egl_device.h>
71 #include <gst/gl/egl/gsteglimage.h>
72 #include <gst/gl/egl/gstglmemoryegl.h>
73 #endif
74 #if GST_GL_HAVE_WINDOW_VIV_FB
75 #include <gst/gl/viv-fb/gstgldisplay_viv_fb.h>
76 #endif
77 #if GST_GL_HAVE_WINDOW_GBM
78 #include <gst/gl/gbm/gstgldisplay_gbm.h>
79 #endif
80
81 GST_DEBUG_CATEGORY_STATIC (gst_context);
82 GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
83 #define GST_CAT_DEFAULT gst_gl_display_debug
84
85 enum
86 {
87   SIGNAL_0,
88   CREATE_CONTEXT,
89   LAST_SIGNAL
90 };
91
92 static guint gst_gl_display_signals[LAST_SIGNAL] = { 0 };
93
94
95 static void gst_gl_display_dispose (GObject * object);
96 static void gst_gl_display_finalize (GObject * object);
97 static guintptr gst_gl_display_default_get_handle (GstGLDisplay * display);
98 static GstGLWindow *gst_gl_display_default_create_window (GstGLDisplay *
99     display);
100
101 struct _GstGLDisplayPrivate
102 {
103   GstGLAPI gl_api;
104
105   GList *contexts;
106
107   GThread *event_thread;
108
109   GMutex thread_lock;
110   GCond thread_cond;
111
112   GMutex window_lock;
113 };
114
115 #define DEBUG_INIT \
116   GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0, "opengl display"); \
117   GST_DEBUG_CATEGORY_GET (gst_context, "GST_CONTEXT");
118
119 G_DEFINE_TYPE_WITH_CODE (GstGLDisplay, gst_gl_display, GST_TYPE_OBJECT,
120     G_ADD_PRIVATE (GstGLDisplay)
121     DEBUG_INIT);
122
123 static gboolean
124 _unlock_main_thread (GstGLDisplay * display)
125 {
126   g_mutex_unlock (&display->priv->thread_lock);
127
128   return G_SOURCE_REMOVE;
129 }
130
131 static gpointer
132 _event_thread_main (GstGLDisplay * display)
133 {
134   g_mutex_lock (&display->priv->thread_lock);
135
136   display->main_context = g_main_context_new ();
137   display->main_loop = g_main_loop_new (display->main_context, FALSE);
138
139   g_main_context_invoke (display->main_context,
140       (GSourceFunc) _unlock_main_thread, display);
141
142   g_cond_broadcast (&display->priv->thread_cond);
143
144   g_main_loop_run (display->main_loop);
145
146   g_mutex_lock (&display->priv->thread_lock);
147   g_main_loop_unref (display->main_loop);
148   g_main_context_unref (display->main_context);
149
150   display->main_loop = NULL;
151   display->main_context = NULL;
152
153   g_cond_broadcast (&display->priv->thread_cond);
154   g_mutex_unlock (&display->priv->thread_lock);
155
156   return NULL;
157 }
158
159 static void
160 gst_gl_display_class_init (GstGLDisplayClass * klass)
161 {
162   /**
163    * GstGLDisplay::create-context:
164    * @object: the #GstGLDisplay
165    * @context: (transfer none): other context to share resources with.
166    *
167    * Overrides the @GstGLContext creation mechanism.
168    * It can be called in any thread and it is emitted with
169    * display's object lock held.
170    *
171    * Returns: (transfer full): the new context.
172    */
173   gst_gl_display_signals[CREATE_CONTEXT] =
174       g_signal_new ("create-context", G_TYPE_FROM_CLASS (klass),
175       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_GL_CONTEXT, 1,
176       GST_TYPE_GL_CONTEXT);
177
178   klass->get_handle = gst_gl_display_default_get_handle;
179   klass->create_window = gst_gl_display_default_create_window;
180
181   G_OBJECT_CLASS (klass)->finalize = gst_gl_display_finalize;
182   G_OBJECT_CLASS (klass)->dispose = gst_gl_display_dispose;
183 }
184
185 static void
186 gst_gl_display_init (GstGLDisplay * display)
187 {
188   display->priv = gst_gl_display_get_instance_private (display);
189
190   display->type = GST_GL_DISPLAY_TYPE_ANY;
191   display->priv->gl_api = GST_GL_API_ANY;
192
193   g_mutex_init (&display->priv->thread_lock);
194   g_cond_init (&display->priv->thread_cond);
195
196   g_mutex_init (&display->priv->window_lock);
197
198   display->priv->event_thread = g_thread_new ("gldisplay-event",
199       (GThreadFunc) _event_thread_main, display);
200
201   g_mutex_lock (&display->priv->thread_lock);
202   while (!display->main_loop)
203     g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
204   g_mutex_unlock (&display->priv->thread_lock);
205
206   GST_TRACE ("init %p", display);
207
208   gst_gl_buffer_init_once ();
209   gst_gl_memory_pbo_init_once ();
210   gst_gl_renderbuffer_init_once ();
211
212 #if GST_GL_HAVE_PLATFORM_EGL
213   gst_gl_memory_egl_init_once ();
214 #endif
215 }
216
217 static void
218 gst_gl_display_dispose (GObject * object)
219 {
220   GstGLDisplay *display = GST_GL_DISPLAY (object);
221
222   if (display->main_loop)
223     g_main_loop_quit (display->main_loop);
224
225   if (display->priv->event_thread) {
226     /* can't use g_thread_join() as we could lose the last ref from a user
227      * function */
228     g_mutex_lock (&display->priv->thread_lock);
229     while (display->main_loop)
230       g_cond_wait (&display->priv->thread_cond, &display->priv->thread_lock);
231     g_mutex_unlock (&display->priv->thread_lock);
232     g_thread_unref (display->priv->event_thread);
233   }
234   display->priv->event_thread = NULL;
235
236   if (display->event_source) {
237     g_source_destroy (display->event_source);
238     g_source_unref (display->event_source);
239   }
240   display->event_source = NULL;
241
242   G_OBJECT_CLASS (gst_gl_display_parent_class)->dispose (object);
243 }
244
245 static void
246 gst_gl_display_finalize (GObject * object)
247 {
248   GstGLDisplay *display = GST_GL_DISPLAY (object);
249   GList *l;
250
251   GST_TRACE_OBJECT (object, "finalizing");
252
253   for (l = display->priv->contexts; l; l = l->next) {
254     g_weak_ref_clear ((GWeakRef *) l->data);
255     g_free (l->data);
256   }
257
258   g_list_free (display->windows);
259   g_list_free (display->priv->contexts);
260
261   g_cond_clear (&display->priv->thread_cond);
262   g_mutex_clear (&display->priv->thread_lock);
263   g_mutex_clear (&display->priv->window_lock);
264
265   G_OBJECT_CLASS (gst_gl_display_parent_class)->finalize (object);
266 }
267
268 /**
269  * gst_gl_display_new:
270  *
271  * Returns: (transfer full): a new #GstGLDisplay
272  *
273  * Since: 1.4
274  */
275 GstGLDisplay *
276 gst_gl_display_new (void)
277 {
278   GstGLDisplay *display = NULL;
279   const gchar *user_choice, *platform_choice;
280   static volatile gsize _init = 0;
281
282   if (g_once_init_enter (&_init)) {
283     GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0,
284         "gldisplay element");
285     g_once_init_leave (&_init, 1);
286   }
287
288   user_choice = g_getenv ("GST_GL_WINDOW");
289   platform_choice = g_getenv ("GST_GL_PLATFORM");
290   GST_INFO ("creating a display, user choice:%s (platform: %s)",
291       GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
292
293 #if GST_GL_HAVE_WINDOW_COCOA
294   if (!display && (!user_choice || g_strstr_len (user_choice, 5, "cocoa"))) {
295     display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
296     if (!display)
297       return NULL;
298   }
299 #endif
300 #if GST_GL_HAVE_WINDOW_WAYLAND
301   if (!display && (!user_choice || g_strstr_len (user_choice, 7, "wayland")))
302     display = GST_GL_DISPLAY (gst_gl_display_wayland_new (NULL));
303 #endif
304 #if GST_GL_HAVE_WINDOW_X11
305   if (!display && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
306     display = GST_GL_DISPLAY (gst_gl_display_x11_new (NULL));
307 #endif
308 #if GST_GL_HAVE_WINDOW_VIV_FB
309   if (!display && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb"))) {
310     const gchar *disp_idx_str = NULL;
311     gint disp_idx = 0;
312     disp_idx_str = g_getenv ("GST_GL_VIV_FB");
313     if (disp_idx_str) {
314       gint64 v = g_ascii_strtoll (disp_idx_str, NULL, 10);
315       if (v >= G_MININT && v <= G_MAXINT)
316         disp_idx = v;
317     }
318     display = GST_GL_DISPLAY (gst_gl_display_viv_fb_new (disp_idx));
319   }
320 #endif
321 #if GST_GL_HAVE_WINDOW_GBM
322   if (!display && (!user_choice || g_strstr_len (user_choice, 3, "gbm"))) {
323     display = GST_GL_DISPLAY (gst_gl_display_gbm_new ());
324   }
325 #endif
326 #if GST_GL_HAVE_PLATFORM_EGL
327   if (!display && (user_choice && g_strstr_len (user_choice, 10, "egl-device"))) {
328     display = GST_GL_DISPLAY (gst_gl_display_egl_device_new (0));
329   }
330
331   if (!display && (!platform_choice
332           || g_strstr_len (platform_choice, 3, "egl"))) {
333     display = GST_GL_DISPLAY (gst_gl_display_egl_new ());
334   }
335 #endif
336   if (!display) {
337     GST_INFO ("Could not create platform/winsys display. user specified %s "
338         "(platform: %s), creating dummy",
339         GST_STR_NULL (user_choice), GST_STR_NULL (platform_choice));
340
341     display = g_object_new (GST_TYPE_GL_DISPLAY, NULL);
342     gst_object_ref_sink (display);
343   }
344
345   return display;
346 }
347
348 /**
349  * gst_gl_display_get_handle:
350  * @display: a #GstGLDisplay
351  *
352  * Returns: the native handle for the display
353  *
354  * Since: 1.4
355  */
356 guintptr
357 gst_gl_display_get_handle (GstGLDisplay * display)
358 {
359   GstGLDisplayClass *klass;
360
361   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), 0);
362   klass = GST_GL_DISPLAY_GET_CLASS (display);
363   g_return_val_if_fail (klass->get_handle != NULL, 0);
364
365   return klass->get_handle (display);
366 }
367
368 static guintptr
369 gst_gl_display_default_get_handle (GstGLDisplay * display)
370 {
371   return 0;
372 }
373
374 /**
375  * gst_gl_display_filter_gl_api:
376  * @display: a #GstGLDisplay
377  * @gl_api: a #GstGLAPI to filter with
378  *
379  * limit the use of OpenGL to the requested @gl_api.  This is intended to allow
380  * application and elements to request a specific set of OpenGL API's based on
381  * what they support.  See gst_gl_context_get_gl_api() for the retrieving the
382  * API supported by a #GstGLContext.
383  */
384 void
385 gst_gl_display_filter_gl_api (GstGLDisplay * display, GstGLAPI gl_api)
386 {
387   gchar *gl_api_s;
388
389   g_return_if_fail (GST_IS_GL_DISPLAY (display));
390
391   gl_api_s = gst_gl_api_to_string (gl_api);
392   GST_TRACE_OBJECT (display, "filtering with api %s", gl_api_s);
393   g_free (gl_api_s);
394
395   GST_OBJECT_LOCK (display);
396   display->priv->gl_api &= gl_api;
397   GST_OBJECT_UNLOCK (display);
398 }
399
400 GstGLAPI
401 gst_gl_display_get_gl_api_unlocked (GstGLDisplay * display)
402 {
403   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
404
405   return display->priv->gl_api;
406 }
407
408 /**
409  * gst_gl_display_get_gl_api:
410  * @display: a #GstGLDisplay
411  *
412  * see gst_gl_display_filter_gl_api() for what the returned value represents
413  *
414  * Returns: the #GstGLAPI configured for @display
415  */
416 GstGLAPI
417 gst_gl_display_get_gl_api (GstGLDisplay * display)
418 {
419   GstGLAPI ret;
420
421   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
422
423   GST_OBJECT_LOCK (display);
424   ret = display->priv->gl_api;
425   GST_OBJECT_UNLOCK (display);
426
427   return ret;
428 }
429
430 /**
431  * gst_gl_display_get_handle_type:
432  * @display: a #GstGLDisplay
433  *
434  * Returns: the #GstGLDisplayType of @display
435  *
436  * Since: 1.4
437  */
438 GstGLDisplayType
439 gst_gl_display_get_handle_type (GstGLDisplay * display)
440 {
441   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_DISPLAY_TYPE_NONE);
442
443   return display->type;
444 }
445
446 /**
447  * gst_context_set_gl_display:
448  * @context: a #GstContext
449  * @display: (transfer none): resulting #GstGLDisplay
450  *
451  * Sets @display on @context
452  *
453  * Since: 1.4
454  */
455 void
456 gst_context_set_gl_display (GstContext * context, GstGLDisplay * display)
457 {
458   GstStructure *s;
459
460   g_return_if_fail (context != NULL);
461
462   if (display)
463     GST_CAT_LOG (gst_context,
464         "setting GstGLDisplay(%" GST_PTR_FORMAT ") on context(%" GST_PTR_FORMAT
465         ")", display, context);
466
467   s = gst_context_writable_structure (context);
468   gst_structure_set (s, GST_GL_DISPLAY_CONTEXT_TYPE, GST_TYPE_GL_DISPLAY,
469       display, NULL);
470 }
471
472 /**
473  * gst_context_get_gl_display:
474  * @context: a #GstContext
475  * @display: (out) (transfer full): resulting #GstGLDisplay
476  *
477  * Returns: Whether @display was in @context
478  *
479  * Since: 1.4
480  */
481 gboolean
482 gst_context_get_gl_display (GstContext * context, GstGLDisplay ** display)
483 {
484   const GstStructure *s;
485   gboolean ret;
486
487   g_return_val_if_fail (display != NULL, FALSE);
488   g_return_val_if_fail (context != NULL, FALSE);
489
490   s = gst_context_get_structure (context);
491   ret = gst_structure_get (s, GST_GL_DISPLAY_CONTEXT_TYPE,
492       GST_TYPE_GL_DISPLAY, display, NULL);
493
494   GST_CAT_LOG (gst_context, "got GstGLDisplay(%p) from context(%p)", *display,
495       context);
496
497   return ret;
498 }
499
500 /**
501  * gst_gl_display_create_context:
502  * @display: a #GstGLDisplay
503  * @other_context: (transfer none): other #GstGLContext to share resources with.
504  * @p_context: (transfer full) (out): resulting #GstGLContext
505  * @error: (allow-none): resulting #GError
506  *
507  * It requires the display's object lock to be held.
508  *
509  * Returns: whether a new context could be created.
510  *
511  * Since: 1.6
512  */
513 gboolean
514 gst_gl_display_create_context (GstGLDisplay * display,
515     GstGLContext * other_context, GstGLContext ** p_context, GError ** error)
516 {
517   GstGLContext *context = NULL;
518   gboolean ret = FALSE;
519
520   g_return_val_if_fail (display != NULL, FALSE);
521   g_return_val_if_fail (p_context != NULL, FALSE);
522   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
523
524   g_signal_emit (display, gst_gl_display_signals[CREATE_CONTEXT], 0,
525       other_context, &context);
526
527   if (context) {
528     *p_context = context;
529     return TRUE;
530   }
531
532   context = gst_gl_context_new (display);
533   if (!context) {
534     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
535         "Failed to create GL context");
536     return FALSE;
537   }
538
539   GST_DEBUG_OBJECT (display,
540       "creating context %" GST_PTR_FORMAT " from other context %"
541       GST_PTR_FORMAT, context, other_context);
542
543   ret = gst_gl_context_create (context, other_context, error);
544
545   if (ret)
546     *p_context = context;
547   else
548     gst_object_unref (context);
549
550   return ret;
551 }
552
553 /**
554  * gst_gl_display_create_window:
555  * @display: a #GstGLDisplay
556  *
557  * Returns: (transfer full): a new #GstGLWindow for @display or %NULL.
558  */
559 /* XXX: previous versions had documentation requiring the OBJECT lock to be
560  * held when this fuction is called so that needs to always work. */
561 GstGLWindow *
562 gst_gl_display_create_window (GstGLDisplay * display)
563 {
564   GstGLDisplayClass *klass;
565   GstGLWindow *window;
566
567   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
568   klass = GST_GL_DISPLAY_GET_CLASS (display);
569   g_return_val_if_fail (klass->create_window != NULL, NULL);
570
571   g_mutex_lock (&display->priv->window_lock);
572   window = klass->create_window (display);
573
574   if (window) {
575     display->windows = g_list_prepend (display->windows, window);
576   }
577   g_mutex_unlock (&display->priv->window_lock);
578   GST_DEBUG_OBJECT (display, "Adding window %" GST_PTR_FORMAT
579       " (%p) to internal list", window, window);
580
581   return window;
582 }
583
584 static GstGLWindow *
585 gst_gl_display_default_create_window (GstGLDisplay * display)
586 {
587   return gst_gl_window_new (display);
588 }
589
590 /**
591  * gst_gl_display_remove_window:
592  * @display: a #GstGLDisplay
593  * @window: a #GstGLWindow to remove
594  *
595  * Returns: if @window could be removed from @display
596  *
597  * Since: 1.12
598  */
599 gboolean
600 gst_gl_display_remove_window (GstGLDisplay * display, GstGLWindow * window)
601 {
602   gboolean ret = FALSE;
603   GList *l;
604
605   g_mutex_lock (&display->priv->window_lock);
606   l = g_list_find (display->windows, window);
607   if (l) {
608     display->windows = g_list_delete_link (display->windows, l);
609     ret = TRUE;
610   }
611   GST_DEBUG_OBJECT (display, "Removing window %" GST_PTR_FORMAT
612       " (%p) from internal list", window, window);
613   g_mutex_unlock (&display->priv->window_lock);
614
615   return ret;
616 }
617
618 /**
619  * gst_gl_display_find_window:
620  * @display: a #GstGLDisplay
621  * @data: (closure): some data to pass to @compare_func
622  * @compare_func: (scope call): a comparison function to run
623  *
624  * Deprecated for gst_gl_display_retrieve_window().
625  *
626  * Execute @compare_func over the list of windows stored by @display.  The
627  * first argument to @compare_func is the #GstGLWindow being checked and the
628  * second argument is @data.
629  *
630  * Returns: (transfer none): The first #GstGLWindow that causes a match
631  *          from @compare_func
632  *
633  * Since: 1.12
634  */
635 GstGLWindow *
636 gst_gl_display_find_window (GstGLDisplay * display, gpointer data,
637     GCompareFunc compare_func)
638 {
639   GstGLWindow *ret;
640
641   ret = gst_gl_display_retrieve_window (display, data, compare_func);
642   if (ret)
643     gst_object_unref (ret);
644
645   return ret;
646 }
647
648 /**
649  * gst_gl_display_retrieve_window:
650  * @display: a #GstGLDisplay
651  * @data: (closure): some data to pass to @compare_func
652  * @compare_func: (scope call): a comparison function to run
653  *
654  * Execute @compare_func over the list of windows stored by @display.  The
655  * first argument to @compare_func is the #GstGLWindow being checked and the
656  * second argument is @data.
657  *
658  * Returns: (transfer full): The first #GstGLWindow that causes a match
659  *          from @compare_func
660  *
661  * Since: 1.18
662  */
663 GstGLWindow *
664 gst_gl_display_retrieve_window (GstGLDisplay * display, gpointer data,
665     GCompareFunc compare_func)
666 {
667   GstGLWindow *ret = NULL;
668   GList *l;
669
670   g_mutex_lock (&display->priv->window_lock);
671   l = g_list_find_custom (display->windows, data, compare_func);
672   if (l)
673     ret = gst_object_ref (l->data);
674
675   GST_DEBUG_OBJECT (display, "Found window %" GST_PTR_FORMAT
676       " (%p) in internal list", ret, ret);
677   g_mutex_unlock (&display->priv->window_lock);
678
679   return ret;
680 }
681
682 static GstGLContext *
683 _get_gl_context_for_thread_unlocked (GstGLDisplay * display, GThread * thread)
684 {
685   GstGLContext *context = NULL;
686   GList *prev = NULL, *l = display->priv->contexts;
687
688   while (l) {
689     GWeakRef *ref = l->data;
690     GThread *context_thread;
691
692     context = g_weak_ref_get (ref);
693     if (!context) {
694       /* remove dead contexts */
695       g_weak_ref_clear (l->data);
696       g_free (l->data);
697       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
698       l = prev ? prev->next : display->priv->contexts;
699       continue;
700     }
701
702     if (thread == NULL) {
703       GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
704           "NULL thread", context);
705       return context;
706     }
707
708     context_thread = gst_gl_context_get_thread (context);
709     if (thread != context_thread) {
710       g_thread_unref (context_thread);
711       gst_object_unref (context);
712       prev = l;
713       l = l->next;
714       continue;
715     }
716
717     if (context_thread)
718       g_thread_unref (context_thread);
719
720     GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
721         "thread %p", context, thread);
722     return context;
723   }
724
725   GST_DEBUG_OBJECT (display, "No GL context for thread %p", thread);
726   return NULL;
727 }
728
729 /**
730  * gst_gl_display_get_gl_context_for_thread:
731  * @display: a #GstGLDisplay
732  * @thread: a #GThread
733  *
734  * Returns: (transfer full): the #GstGLContext current on @thread or %NULL
735  *
736  * Must be called with the object lock held.
737  *
738  * Since: 1.6
739  */
740 GstGLContext *
741 gst_gl_display_get_gl_context_for_thread (GstGLDisplay * display,
742     GThread * thread)
743 {
744   GstGLContext *context;
745
746   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
747
748   context = _get_gl_context_for_thread_unlocked (display, thread);
749   GST_DEBUG_OBJECT (display, "returning context %" GST_PTR_FORMAT " for thread "
750       "%p", context, thread);
751
752   return context;
753 }
754
755 static gboolean
756 _check_collision (GstGLContext * context, GstGLContext * collision)
757 {
758   GThread *thread, *collision_thread;
759   gboolean ret = FALSE;
760
761   if (!context || !collision)
762     return FALSE;
763
764   thread = gst_gl_context_get_thread (context);
765   collision_thread = gst_gl_context_get_thread (collision);
766
767   if (!thread || !collision_thread) {
768     ret = FALSE;
769     goto out;
770   }
771
772   if (thread == collision_thread) {
773     ret = TRUE;
774     goto out;
775   }
776
777 out:
778   if (thread)
779     g_thread_unref (thread);
780   if (collision_thread)
781     g_thread_unref (collision_thread);
782
783   return ret;
784 }
785
786 /**
787  * gst_gl_display_add_context:
788  * @display: a #GstGLDisplay
789  * @context: (transfer none): a #GstGLContext
790  *
791  * Returns: whether @context was successfully added. %FALSE may be returned
792  * if there already exists another context for @context's active thread.
793  *
794  * Must be called with the object lock held.
795  *
796  * Since: 1.6
797  */
798 gboolean
799 gst_gl_display_add_context (GstGLDisplay * display, GstGLContext * context)
800 {
801   GstGLContext *collision = NULL;
802   GstGLDisplay *context_display;
803   gboolean ret = TRUE;
804   GThread *thread;
805   GWeakRef *ref;
806
807   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), FALSE);
808   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
809
810   context_display = gst_gl_context_get_display (context);
811   g_assert (context_display == display);
812   gst_object_unref (context_display);
813
814   thread = gst_gl_context_get_thread (context);
815   if (thread) {
816     collision = _get_gl_context_for_thread_unlocked (display, thread);
817     g_thread_unref (thread);
818
819     /* adding the same context is a no-op */
820     if (context == collision) {
821       GST_LOG_OBJECT (display, "Attempting to add the same GL context %"
822           GST_PTR_FORMAT ". Ignoring", context);
823       ret = TRUE;
824       goto out;
825     }
826
827     if (_check_collision (context, collision)) {
828       GST_DEBUG_OBJECT (display, "Collision detected adding GL context "
829           "%" GST_PTR_FORMAT, context);
830       ret = FALSE;
831       goto out;
832     }
833   }
834
835   ref = g_new0 (GWeakRef, 1);
836   g_weak_ref_init (ref, context);
837
838   GST_DEBUG_OBJECT (display, "Adding GL context %" GST_PTR_FORMAT, context);
839   display->priv->contexts = g_list_prepend (display->priv->contexts, ref);
840
841 out:
842   if (collision)
843     gst_object_unref (collision);
844
845   GST_DEBUG_OBJECT (display, "%ssuccessfully inserted context %" GST_PTR_FORMAT,
846       ret ? "" : "un", context);
847
848   return ret;
849 }
850
851 /**
852  * gst_gl_display_remove_context:
853  * @display: a #GstGLDisplay
854  * @context: (transfer none): the #GstGLContext to remove
855  *
856  * Must be called with the object lock held.
857  *
858  * Since: 1.18
859  */
860 void
861 gst_gl_display_remove_context (GstGLDisplay * display, GstGLContext * needle)
862 {
863   GstGLContext *context;
864   GList *prev = NULL, *l;
865
866   g_return_if_fail (GST_IS_GL_DISPLAY (display));
867   g_return_if_fail (GST_IS_GL_CONTEXT (needle));
868
869   l = display->priv->contexts;
870
871   while (l) {
872     GWeakRef *ref = l->data;
873
874     context = g_weak_ref_get (ref);
875     if (!context || context == needle) {
876       /* remove dead contexts */
877       g_weak_ref_clear (l->data);
878       g_free (l->data);
879       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
880       if (context) {
881         GST_INFO_OBJECT (display, "removed context %" GST_PTR_FORMAT
882             " from internal list", context);
883         gst_object_unref (context);
884         return;
885       }
886       l = prev ? prev->next : display->priv->contexts;
887       continue;
888     }
889     prev = l;
890     l = l->next;
891   }
892
893   GST_WARNING_OBJECT (display, "%" GST_PTR_FORMAT " was not found in this "
894       "display", needle);
895 }