270d215ad6da8c4429d4195d72edecb6f083d283
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / 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 static void
269 init_debug (void)
270 {
271   static gsize _init = 0;
272
273   if (g_once_init_enter (&_init)) {
274     GST_DEBUG_CATEGORY_INIT (gst_gl_display_debug, "gldisplay", 0,
275         "gldisplay element");
276     g_once_init_leave (&_init, 1);
277   }
278 }
279
280 static GstGLDisplayType
281 gst_gl_display_type_from_environment (void)
282 {
283   const char *env = g_getenv ("GST_GL_WINDOW");
284   const char *platform = g_getenv ("GST_GL_PLATFORM");
285
286   init_debug ();
287
288   GST_INFO ("creating a display, user choice:%s (platform: %s)",
289       GST_STR_NULL (env), GST_STR_NULL (platform));
290
291   if (!env && !platform)
292     return GST_GL_DISPLAY_TYPE_ANY;
293
294   if (env) {
295     if (g_strstr_len (env, 3, "x11")) {
296       return GST_GL_DISPLAY_TYPE_X11;
297     } else if (g_strstr_len (env, 7, "wayland")) {
298       return GST_GL_DISPLAY_TYPE_WAYLAND;
299     } else if (g_strstr_len (env, 5, "cocoa")) {
300       return GST_GL_DISPLAY_TYPE_COCOA;
301     } else if (g_strstr_len (env, 5, "win32")) {
302       return GST_GL_DISPLAY_TYPE_WIN32;
303     } else if (g_strstr_len (env, 8, "dispmanx")) {
304       return GST_GL_DISPLAY_TYPE_DISPMANX;
305     } else if (g_strstr_len (env, 3, "egl")) {
306       return GST_GL_DISPLAY_TYPE_EGL;
307     } else if (g_strstr_len (env, 6, "viv-fb")) {
308       return GST_GL_DISPLAY_TYPE_VIV_FB;
309     } else if (g_strstr_len (env, 3, "gbm")) {
310       return GST_GL_DISPLAY_TYPE_GBM;
311     } else if (g_strstr_len (env, 10, "egl-device")) {
312       return GST_GL_DISPLAY_TYPE_EGL_DEVICE;
313     } else if (g_strstr_len (env, 4, "eagl")) {
314       return GST_GL_DISPLAY_TYPE_EAGL;
315     } else if (g_strstr_len (env, 7, "android")) {
316       return GST_GL_DISPLAY_TYPE_EGL;
317     } else if (g_strstr_len (env, 4, "winrt")) {
318       return GST_GL_DISPLAY_TYPE_EGL;
319     } else {
320       return GST_GL_DISPLAY_TYPE_NONE;
321     }
322   }
323
324   return GST_GL_DISPLAY_TYPE_ANY;
325 }
326
327 static GstGLDisplay *
328 create_dummy_display (void)
329 {
330   GstGLDisplay *display = g_object_new (GST_TYPE_GL_DISPLAY, NULL);
331   return gst_object_ref_sink (display);
332 }
333
334 /**
335  * gst_gl_display_new_with_type:
336  * @type: #GstGLDisplayType
337  *
338  * Will always return a #GstGLDisplay of a single type.  This differs from
339  * gst_gl_display_new() and the seemingly equivalent call
340  * gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_ANY) in that the latter
341  * may return NULL.
342  *
343  * Returns: (transfer full) (nullable): a new #GstGLDisplay or %NULL if @type is
344  *          not supported
345  *
346  * Since: 1.20
347  */
348 GstGLDisplay *
349 gst_gl_display_new_with_type (GstGLDisplayType type)
350 {
351   GstGLDisplay *display = NULL;
352   GstGLDisplayType custom_new_types = 0;
353
354   init_debug ();
355
356 #if GST_GL_HAVE_WINDOW_COCOA
357   if (!display && (type & GST_GL_DISPLAY_TYPE_COCOA)) {
358     display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
359     if (!display)
360       return NULL;
361   }
362 #endif
363   custom_new_types |= GST_GL_DISPLAY_TYPE_COCOA;
364 #if GST_GL_HAVE_WINDOW_WAYLAND
365   if (!display && (type & GST_GL_DISPLAY_TYPE_WAYLAND))
366     display = GST_GL_DISPLAY (gst_gl_display_wayland_new (NULL));
367 #endif
368   custom_new_types |= GST_GL_DISPLAY_TYPE_WAYLAND;
369 #if GST_GL_HAVE_WINDOW_X11
370   if (!display && (type & GST_GL_DISPLAY_TYPE_X11))
371     display = GST_GL_DISPLAY (gst_gl_display_x11_new (NULL));
372 #endif
373   custom_new_types |= GST_GL_DISPLAY_TYPE_X11;
374 #if GST_GL_HAVE_WINDOW_VIV_FB
375   if (!display && (GST_GL_DISPLAY_TYPE_VIV_FB)) {
376     const gchar *disp_idx_str = NULL;
377     gint disp_idx = 0;
378     disp_idx_str = g_getenv ("GST_GL_VIV_FB");
379     if (disp_idx_str) {
380       gint64 v = g_ascii_strtoll (disp_idx_str, NULL, 10);
381       if (v >= G_MININT && v <= G_MAXINT)
382         disp_idx = v;
383     }
384     display = GST_GL_DISPLAY (gst_gl_display_viv_fb_new (disp_idx));
385   }
386 #endif
387   custom_new_types |= GST_GL_DISPLAY_TYPE_VIV_FB;
388 #if GST_GL_HAVE_WINDOW_GBM
389   if (!display && (type & GST_GL_DISPLAY_TYPE_GBM)) {
390     display = GST_GL_DISPLAY (gst_gl_display_gbm_new ());
391   }
392 #endif
393   custom_new_types |= GST_GL_DISPLAY_TYPE_GBM;
394 #if GST_GL_HAVE_PLATFORM_EGL
395   if (!display && (type == GST_GL_DISPLAY_TYPE_EGL_DEVICE)) {
396     display = GST_GL_DISPLAY (gst_gl_display_egl_device_new (0));
397   }
398
399   if (!display && (type & GST_GL_DISPLAY_TYPE_EGL)) {
400     display = GST_GL_DISPLAY (gst_gl_display_egl_new ());
401   }
402 #endif
403   custom_new_types |= GST_GL_DISPLAY_TYPE_EGL_DEVICE;
404   custom_new_types |= GST_GL_DISPLAY_TYPE_EGL;
405   custom_new_types |= GST_GL_DISPLAY_TYPE_DISPMANX;
406   custom_new_types |= GST_GL_DISPLAY_TYPE_WINRT;
407   custom_new_types |= GST_GL_DISPLAY_TYPE_ANDROID;
408 #if GST_GL_HAVE_WINDOW_WIN32 || GST_GL_HAVE_WINDOW_EAGL
409   if (!display) {
410     GstGLDisplayType create_type = 0;
411 #if GST_GL_HAVE_WINDOW_WIN32
412     if (type & GST_GL_DISPLAY_TYPE_WIN32)
413       create_type = GST_GL_DISPLAY_TYPE_WIN32;
414 #endif
415 #if GST_GL_HAVE_WINDOW_EAGL
416     if (type & GST_GL_DISPLAY_TYPE_EAGL)
417       create_type = GST_GL_DISPLAY_TYPE_EAGL;
418 #endif
419     if (create_type) {
420       GST_INFO ("Creating display with type %u(0x%x)",
421           create_type, create_type);
422       display = create_dummy_display ();
423       display->type = create_type;
424     }
425   }
426 #endif
427   custom_new_types |= GST_GL_DISPLAY_TYPE_WIN32;
428   custom_new_types |= GST_GL_DISPLAY_TYPE_EAGL;
429
430   if (!display && type != GST_GL_DISPLAY_TYPE_ANY
431       && type != GST_GL_DISPLAY_TYPE_NONE) {
432     /* remove all the display types that we know about */
433     type &= ~custom_new_types;
434     if (type && (type & (type - 1)) == 0) {
435       /* only create a dummy display if we only have a single type */
436       GST_INFO_OBJECT (display, "Creating dummy display with type %u(0x%x)",
437           type, type);
438       display = create_dummy_display ();
439       display->type = type;
440     }
441   }
442
443   return display;
444 }
445
446 /**
447  * gst_gl_display_new:
448  *
449  * Returns: (transfer full): a new #GstGLDisplay
450  *
451  * Since: 1.4
452  */
453 GstGLDisplay *
454 gst_gl_display_new (void)
455 {
456   GstGLDisplayType env_choice = gst_gl_display_type_from_environment ();
457   GstGLDisplay *display = gst_gl_display_new_with_type (env_choice);
458
459   if (!display) {
460     display = g_object_new (GST_TYPE_GL_DISPLAY, NULL);
461     GST_INFO_OBJECT (display, "Creating dummy display");
462     gst_object_ref_sink (display);
463   }
464
465   return display;
466 }
467
468 /**
469  * gst_gl_display_get_handle:
470  * @display: a #GstGLDisplay
471  *
472  * Returns: the native handle for the display
473  *
474  * Since: 1.4
475  */
476 guintptr
477 gst_gl_display_get_handle (GstGLDisplay * display)
478 {
479   GstGLDisplayClass *klass;
480
481   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), 0);
482   klass = GST_GL_DISPLAY_GET_CLASS (display);
483   g_return_val_if_fail (klass->get_handle != NULL, 0);
484
485   return klass->get_handle (display);
486 }
487
488 static guintptr
489 gst_gl_display_default_get_handle (GstGLDisplay * display)
490 {
491   return 0;
492 }
493
494 /**
495  * gst_gl_display_filter_gl_api:
496  * @display: a #GstGLDisplay
497  * @gl_api: a #GstGLAPI to filter with
498  *
499  * limit the use of OpenGL to the requested @gl_api.  This is intended to allow
500  * application and elements to request a specific set of OpenGL API's based on
501  * what they support.  See gst_gl_context_get_gl_api() for the retrieving the
502  * API supported by a #GstGLContext.
503  */
504 void
505 gst_gl_display_filter_gl_api (GstGLDisplay * display, GstGLAPI gl_api)
506 {
507   gchar *gl_api_s;
508
509   g_return_if_fail (GST_IS_GL_DISPLAY (display));
510
511   gl_api_s = gst_gl_api_to_string (gl_api);
512   GST_TRACE_OBJECT (display, "filtering with api %s", gl_api_s);
513   g_free (gl_api_s);
514
515   GST_OBJECT_LOCK (display);
516   display->priv->gl_api &= gl_api;
517   GST_OBJECT_UNLOCK (display);
518 }
519
520 GstGLAPI
521 gst_gl_display_get_gl_api_unlocked (GstGLDisplay * display)
522 {
523   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
524
525   return display->priv->gl_api;
526 }
527
528 /**
529  * gst_gl_display_get_gl_api:
530  * @display: a #GstGLDisplay
531  *
532  * see gst_gl_display_filter_gl_api() for what the returned value represents
533  *
534  * Returns: the #GstGLAPI configured for @display
535  */
536 GstGLAPI
537 gst_gl_display_get_gl_api (GstGLDisplay * display)
538 {
539   GstGLAPI ret;
540
541   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_API_NONE);
542
543   GST_OBJECT_LOCK (display);
544   ret = display->priv->gl_api;
545   GST_OBJECT_UNLOCK (display);
546
547   return ret;
548 }
549
550 /**
551  * gst_gl_display_get_handle_type:
552  * @display: a #GstGLDisplay
553  *
554  * Returns: the #GstGLDisplayType of @display
555  *
556  * Since: 1.4
557  */
558 GstGLDisplayType
559 gst_gl_display_get_handle_type (GstGLDisplay * display)
560 {
561   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), GST_GL_DISPLAY_TYPE_NONE);
562
563   return display->type;
564 }
565
566 /**
567  * gst_context_set_gl_display:
568  * @context: a #GstContext
569  * @display: (transfer none): resulting #GstGLDisplay
570  *
571  * Sets @display on @context
572  *
573  * Since: 1.4
574  */
575 void
576 gst_context_set_gl_display (GstContext * context, GstGLDisplay * display)
577 {
578   GstStructure *s;
579
580   g_return_if_fail (context != NULL);
581
582   if (display)
583     GST_CAT_LOG (gst_context,
584         "setting GstGLDisplay(%" GST_PTR_FORMAT ") on context(%" GST_PTR_FORMAT
585         ")", display, context);
586
587   s = gst_context_writable_structure (context);
588   gst_structure_set (s, GST_GL_DISPLAY_CONTEXT_TYPE, GST_TYPE_GL_DISPLAY,
589       display, NULL);
590 }
591
592 /**
593  * gst_context_get_gl_display:
594  * @context: a #GstContext
595  * @display: (out) (transfer full): resulting #GstGLDisplay
596  *
597  * Returns: Whether @display was in @context
598  *
599  * Since: 1.4
600  */
601 gboolean
602 gst_context_get_gl_display (GstContext * context, GstGLDisplay ** display)
603 {
604   const GstStructure *s;
605   gboolean ret;
606
607   g_return_val_if_fail (display != NULL, FALSE);
608   g_return_val_if_fail (context != NULL, FALSE);
609
610   s = gst_context_get_structure (context);
611   ret = gst_structure_get (s, GST_GL_DISPLAY_CONTEXT_TYPE,
612       GST_TYPE_GL_DISPLAY, display, NULL);
613
614   GST_CAT_LOG (gst_context, "got GstGLDisplay(%p) from context(%p)", *display,
615       context);
616
617   return ret;
618 }
619
620 /**
621  * gst_gl_display_create_context:
622  * @display: a #GstGLDisplay
623  * @other_context: (transfer none): other #GstGLContext to share resources with.
624  * @p_context: (transfer full) (out): resulting #GstGLContext
625  * @error: (allow-none): resulting #GError
626  *
627  * It requires the display's object lock to be held.
628  *
629  * Returns: whether a new context could be created.
630  *
631  * Since: 1.6
632  */
633 gboolean
634 gst_gl_display_create_context (GstGLDisplay * display,
635     GstGLContext * other_context, GstGLContext ** p_context, GError ** error)
636 {
637   GstGLContext *context = NULL;
638   gboolean ret = FALSE;
639
640   g_return_val_if_fail (display != NULL, FALSE);
641   g_return_val_if_fail (p_context != NULL, FALSE);
642   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
643
644   g_signal_emit (display, gst_gl_display_signals[CREATE_CONTEXT], 0,
645       other_context, &context);
646
647   if (context) {
648     *p_context = context;
649     return TRUE;
650   }
651
652   context = gst_gl_context_new (display);
653   if (!context) {
654     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
655         "Failed to create GL context");
656     return FALSE;
657   }
658
659   GST_DEBUG_OBJECT (display,
660       "creating context %" GST_PTR_FORMAT " from other context %"
661       GST_PTR_FORMAT, context, other_context);
662
663   ret = gst_gl_context_create (context, other_context, error);
664
665   if (ret)
666     *p_context = context;
667   else
668     gst_object_unref (context);
669
670   return ret;
671 }
672
673 /**
674  * gst_gl_display_create_window:
675  * @display: a #GstGLDisplay
676  *
677  * Returns: (transfer full): a new #GstGLWindow for @display or %NULL.
678  */
679 /* XXX: previous versions had documentation requiring the OBJECT lock to be
680  * held when this fuction is called so that needs to always work. */
681 GstGLWindow *
682 gst_gl_display_create_window (GstGLDisplay * display)
683 {
684   GstGLDisplayClass *klass;
685   GstGLWindow *window;
686
687   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
688   klass = GST_GL_DISPLAY_GET_CLASS (display);
689   g_return_val_if_fail (klass->create_window != NULL, NULL);
690
691   g_mutex_lock (&display->priv->window_lock);
692   window = klass->create_window (display);
693
694   if (window) {
695     display->windows = g_list_prepend (display->windows, window);
696   }
697   g_mutex_unlock (&display->priv->window_lock);
698   GST_DEBUG_OBJECT (display, "Adding window %" GST_PTR_FORMAT
699       " (%p) to internal list", window, window);
700
701   return window;
702 }
703
704 static GstGLWindow *
705 gst_gl_display_default_create_window (GstGLDisplay * display)
706 {
707   return gst_gl_window_new (display);
708 }
709
710 /**
711  * gst_gl_display_remove_window:
712  * @display: a #GstGLDisplay
713  * @window: a #GstGLWindow to remove
714  *
715  * Returns: if @window could be removed from @display
716  *
717  * Since: 1.12
718  */
719 gboolean
720 gst_gl_display_remove_window (GstGLDisplay * display, GstGLWindow * window)
721 {
722   gboolean ret = FALSE;
723   GList *l;
724
725   g_mutex_lock (&display->priv->window_lock);
726   l = g_list_find (display->windows, window);
727   if (l) {
728     display->windows = g_list_delete_link (display->windows, l);
729     ret = TRUE;
730   }
731   GST_DEBUG_OBJECT (display, "Removing window %" GST_PTR_FORMAT
732       " (%p) from internal list", window, window);
733   g_mutex_unlock (&display->priv->window_lock);
734
735   return ret;
736 }
737
738 #ifndef GST_REMOVE_DEPRECATED
739 /**
740  * gst_gl_display_find_window:
741  * @display: a #GstGLDisplay
742  * @data: (closure): some data to pass to @compare_func
743  * @compare_func: (scope call): a comparison function to run
744  *
745  * Execute @compare_func over the list of windows stored by @display.  The
746  * first argument to @compare_func is the #GstGLWindow being checked and the
747  * second argument is @data.
748  *
749  * Returns: (transfer none): The first #GstGLWindow that causes a match
750  *          from @compare_func
751  *
752  * Since: 1.12
753  * Deprecated: 1.18: Use gst_gl_display_retrieve_window() instead.
754  */
755 GstGLWindow *
756 gst_gl_display_find_window (GstGLDisplay * display, gpointer data,
757     GCompareFunc compare_func)
758 {
759   GstGLWindow *ret;
760
761   ret = gst_gl_display_retrieve_window (display, data, compare_func);
762   if (ret)
763     gst_object_unref (ret);
764
765   return ret;
766 }
767 #endif
768
769 /**
770  * gst_gl_display_retrieve_window:
771  * @display: a #GstGLDisplay
772  * @data: (closure): some data to pass to @compare_func
773  * @compare_func: (scope call): a comparison function to run
774  *
775  * Execute @compare_func over the list of windows stored by @display.  The
776  * first argument to @compare_func is the #GstGLWindow being checked and the
777  * second argument is @data.
778  *
779  * Returns: (transfer full): The first #GstGLWindow that causes a match
780  *          from @compare_func
781  *
782  * Since: 1.18
783  */
784 GstGLWindow *
785 gst_gl_display_retrieve_window (GstGLDisplay * display, gpointer data,
786     GCompareFunc compare_func)
787 {
788   GstGLWindow *ret = NULL;
789   GList *l;
790
791   g_mutex_lock (&display->priv->window_lock);
792   l = g_list_find_custom (display->windows, data, compare_func);
793   if (l)
794     ret = gst_object_ref (l->data);
795
796   GST_DEBUG_OBJECT (display, "Found window %" GST_PTR_FORMAT
797       " (%p) in internal list", ret, ret);
798   g_mutex_unlock (&display->priv->window_lock);
799
800   return ret;
801 }
802
803 static GstGLContext *
804 _get_gl_context_for_thread_unlocked (GstGLDisplay * display, GThread * thread)
805 {
806   GstGLContext *context = NULL;
807   GList *prev = NULL, *l = display->priv->contexts;
808
809   while (l) {
810     GWeakRef *ref = l->data;
811     GThread *context_thread;
812
813     context = g_weak_ref_get (ref);
814     if (!context) {
815       /* remove dead contexts */
816       g_weak_ref_clear (l->data);
817       g_free (l->data);
818       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
819       l = prev ? prev->next : display->priv->contexts;
820       continue;
821     }
822
823     if (thread == NULL) {
824       GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
825           "NULL thread", context);
826       return context;
827     }
828
829     context_thread = gst_gl_context_get_thread (context);
830     if (thread != context_thread) {
831       g_thread_unref (context_thread);
832       gst_object_unref (context);
833       prev = l;
834       l = l->next;
835       continue;
836     }
837
838     if (context_thread)
839       g_thread_unref (context_thread);
840
841     GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
842         "thread %p", context, thread);
843     return context;
844   }
845
846   GST_DEBUG_OBJECT (display, "No GL context for thread %p", thread);
847   return NULL;
848 }
849
850 /**
851  * gst_gl_display_get_gl_context_for_thread:
852  * @display: a #GstGLDisplay
853  * @thread: a #GThread
854  *
855  * Returns: (transfer full): the #GstGLContext current on @thread or %NULL
856  *
857  * Must be called with the object lock held.
858  *
859  * Since: 1.6
860  */
861 GstGLContext *
862 gst_gl_display_get_gl_context_for_thread (GstGLDisplay * display,
863     GThread * thread)
864 {
865   GstGLContext *context;
866
867   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
868
869   context = _get_gl_context_for_thread_unlocked (display, thread);
870   GST_DEBUG_OBJECT (display, "returning context %" GST_PTR_FORMAT " for thread "
871       "%p", context, thread);
872
873   return context;
874 }
875
876 static gboolean
877 _check_collision (GstGLContext * context, GstGLContext * collision)
878 {
879   GThread *thread, *collision_thread;
880   gboolean ret = FALSE;
881
882   if (!context || !collision)
883     return FALSE;
884
885   thread = gst_gl_context_get_thread (context);
886   collision_thread = gst_gl_context_get_thread (collision);
887
888   if (!thread || !collision_thread) {
889     ret = FALSE;
890     goto out;
891   }
892
893   if (thread == collision_thread) {
894     ret = TRUE;
895     goto out;
896   }
897
898 out:
899   if (thread)
900     g_thread_unref (thread);
901   if (collision_thread)
902     g_thread_unref (collision_thread);
903
904   return ret;
905 }
906
907 /**
908  * gst_gl_display_add_context:
909  * @display: a #GstGLDisplay
910  * @context: (transfer none): a #GstGLContext
911  *
912  * Returns: whether @context was successfully added. %FALSE may be returned
913  * if there already exists another context for @context's active thread.
914  *
915  * Must be called with the object lock held.
916  *
917  * Since: 1.6
918  */
919 gboolean
920 gst_gl_display_add_context (GstGLDisplay * display, GstGLContext * context)
921 {
922   GstGLContext *collision = NULL;
923   GstGLDisplay *context_display;
924   gboolean ret = TRUE;
925   GThread *thread;
926   GWeakRef *ref;
927
928   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), FALSE);
929   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
930
931   context_display = gst_gl_context_get_display (context);
932   g_assert (context_display == display);
933   gst_object_unref (context_display);
934
935   thread = gst_gl_context_get_thread (context);
936   if (thread) {
937     collision = _get_gl_context_for_thread_unlocked (display, thread);
938     g_thread_unref (thread);
939
940     /* adding the same context is a no-op */
941     if (context == collision) {
942       GST_LOG_OBJECT (display, "Attempting to add the same GL context %"
943           GST_PTR_FORMAT ". Ignoring", context);
944       ret = TRUE;
945       goto out;
946     }
947
948     if (_check_collision (context, collision)) {
949       GST_DEBUG_OBJECT (display, "Collision detected adding GL context "
950           "%" GST_PTR_FORMAT, context);
951       ret = FALSE;
952       goto out;
953     }
954   }
955
956   ref = g_new0 (GWeakRef, 1);
957   g_weak_ref_init (ref, context);
958
959   GST_DEBUG_OBJECT (display, "Adding GL context %" GST_PTR_FORMAT, context);
960   display->priv->contexts = g_list_prepend (display->priv->contexts, ref);
961
962 out:
963   if (collision)
964     gst_object_unref (collision);
965
966   GST_DEBUG_OBJECT (display, "%ssuccessfully inserted context %" GST_PTR_FORMAT,
967       ret ? "" : "un", context);
968
969   return ret;
970 }
971
972 /**
973  * gst_gl_display_remove_context:
974  * @display: a #GstGLDisplay
975  * @context: (transfer none): the #GstGLContext to remove
976  *
977  * Must be called with the object lock held.
978  *
979  * Since: 1.18
980  */
981 void
982 gst_gl_display_remove_context (GstGLDisplay * display, GstGLContext * needle)
983 {
984   GstGLContext *context;
985   GList *prev = NULL, *l;
986
987   g_return_if_fail (GST_IS_GL_DISPLAY (display));
988   g_return_if_fail (GST_IS_GL_CONTEXT (needle));
989
990   l = display->priv->contexts;
991
992   while (l) {
993     GWeakRef *ref = l->data;
994
995     context = g_weak_ref_get (ref);
996     if (!context || context == needle) {
997       /* remove dead contexts */
998       g_weak_ref_clear (l->data);
999       g_free (l->data);
1000       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
1001       if (context) {
1002         GST_INFO_OBJECT (display, "removed context %" GST_PTR_FORMAT
1003             " from internal list", context);
1004         gst_object_unref (context);
1005         return;
1006       }
1007       l = prev ? prev->next : display->priv->contexts;
1008       continue;
1009     }
1010     prev = l;
1011     l = l->next;
1012   }
1013
1014   GST_WARNING_OBJECT (display, "%" GST_PTR_FORMAT " was not found in this "
1015       "display", needle);
1016 }