• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
_unlock_main_thread(GstGLDisplay * display)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
_event_thread_main(GstGLDisplay * display)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
gst_gl_display_class_init(GstGLDisplayClass * klass)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
gst_gl_display_init(GstGLDisplay * display)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
gst_gl_display_dispose(GObject * object)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
gst_gl_display_finalize(GObject * object)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
init_debug(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
gst_gl_display_type_from_environment(void)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, 10, "egl-device")) {
306       return GST_GL_DISPLAY_TYPE_EGL_DEVICE;
307     } else if (g_strstr_len (env, 3, "egl")) {
308       return GST_GL_DISPLAY_TYPE_EGL;
309     } else if (g_strstr_len (env, 6, "viv-fb")) {
310       return GST_GL_DISPLAY_TYPE_VIV_FB;
311     } else if (g_strstr_len (env, 3, "gbm")) {
312       return GST_GL_DISPLAY_TYPE_GBM;
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 *
create_dummy_display(void)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 *
gst_gl_display_new_with_type(GstGLDisplayType type)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 *
gst_gl_display_new(void)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
gst_gl_display_get_handle(GstGLDisplay * display)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
gst_gl_display_default_get_handle(GstGLDisplay * display)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
gst_gl_display_filter_gl_api(GstGLDisplay * display,GstGLAPI gl_api)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
gst_gl_display_get_gl_api_unlocked(GstGLDisplay * display)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
gst_gl_display_get_gl_api(GstGLDisplay * display)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
gst_gl_display_get_handle_type(GstGLDisplay * display)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
gst_context_set_gl_display(GstContext * context,GstGLDisplay * display)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
gst_context_get_gl_display(GstContext * context,GstGLDisplay ** display)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
gst_gl_display_create_context(GstGLDisplay * display,GstGLContext * other_context,GstGLContext ** p_context,GError ** error)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 *
gst_gl_display_create_window(GstGLDisplay * display)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 *
gst_gl_display_default_create_window(GstGLDisplay * display)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
gst_gl_display_remove_window(GstGLDisplay * display,GstGLWindow * window)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 /**
739  * gst_gl_display_find_window:
740  * @display: a #GstGLDisplay
741  * @data: (closure): some data to pass to @compare_func
742  * @compare_func: (scope call): a comparison function to run
743  *
744  * Deprecated for gst_gl_display_retrieve_window().
745  *
746  * Execute @compare_func over the list of windows stored by @display.  The
747  * first argument to @compare_func is the #GstGLWindow being checked and the
748  * second argument is @data.
749  *
750  * Returns: (transfer none): The first #GstGLWindow that causes a match
751  *          from @compare_func
752  *
753  * Since: 1.12
754  */
755 GstGLWindow *
gst_gl_display_find_window(GstGLDisplay * display,gpointer data,GCompareFunc compare_func)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 
768 /**
769  * gst_gl_display_retrieve_window:
770  * @display: a #GstGLDisplay
771  * @data: (closure): some data to pass to @compare_func
772  * @compare_func: (scope call): a comparison function to run
773  *
774  * Execute @compare_func over the list of windows stored by @display.  The
775  * first argument to @compare_func is the #GstGLWindow being checked and the
776  * second argument is @data.
777  *
778  * Returns: (transfer full): The first #GstGLWindow that causes a match
779  *          from @compare_func
780  *
781  * Since: 1.18
782  */
783 GstGLWindow *
gst_gl_display_retrieve_window(GstGLDisplay * display,gpointer data,GCompareFunc compare_func)784 gst_gl_display_retrieve_window (GstGLDisplay * display, gpointer data,
785     GCompareFunc compare_func)
786 {
787   GstGLWindow *ret = NULL;
788   GList *l;
789 
790   g_mutex_lock (&display->priv->window_lock);
791   l = g_list_find_custom (display->windows, data, compare_func);
792   if (l)
793     ret = gst_object_ref (l->data);
794 
795   GST_DEBUG_OBJECT (display, "Found window %" GST_PTR_FORMAT
796       " (%p) in internal list", ret, ret);
797   g_mutex_unlock (&display->priv->window_lock);
798 
799   return ret;
800 }
801 
802 static GstGLContext *
_get_gl_context_for_thread_unlocked(GstGLDisplay * display,GThread * thread)803 _get_gl_context_for_thread_unlocked (GstGLDisplay * display, GThread * thread)
804 {
805   GstGLContext *context = NULL;
806   GList *prev = NULL, *l = display->priv->contexts;
807 
808   while (l) {
809     GWeakRef *ref = l->data;
810     GThread *context_thread;
811 
812     context = g_weak_ref_get (ref);
813     if (!context) {
814       /* remove dead contexts */
815       g_weak_ref_clear (l->data);
816       g_free (l->data);
817       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
818       l = prev ? prev->next : display->priv->contexts;
819       continue;
820     }
821 
822     if (thread == NULL) {
823       GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
824           "NULL thread", context);
825       return context;
826     }
827 
828     context_thread = gst_gl_context_get_thread (context);
829     if (thread != context_thread) {
830       g_thread_unref (context_thread);
831       gst_object_unref (context);
832       prev = l;
833       l = l->next;
834       continue;
835     }
836 
837     if (context_thread)
838       g_thread_unref (context_thread);
839 
840     GST_DEBUG_OBJECT (display, "Returning GL context %" GST_PTR_FORMAT " for "
841         "thread %p", context, thread);
842     return context;
843   }
844 
845   GST_DEBUG_OBJECT (display, "No GL context for thread %p", thread);
846   return NULL;
847 }
848 
849 /**
850  * gst_gl_display_get_gl_context_for_thread:
851  * @display: a #GstGLDisplay
852  * @thread: a #GThread
853  *
854  * Returns: (transfer full): the #GstGLContext current on @thread or %NULL
855  *
856  * Must be called with the object lock held.
857  *
858  * Since: 1.6
859  */
860 GstGLContext *
gst_gl_display_get_gl_context_for_thread(GstGLDisplay * display,GThread * thread)861 gst_gl_display_get_gl_context_for_thread (GstGLDisplay * display,
862     GThread * thread)
863 {
864   GstGLContext *context;
865 
866   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
867 
868   context = _get_gl_context_for_thread_unlocked (display, thread);
869   GST_DEBUG_OBJECT (display, "returning context %" GST_PTR_FORMAT " for thread "
870       "%p", context, thread);
871 
872   return context;
873 }
874 
875 static gboolean
_check_collision(GstGLContext * context,GstGLContext * collision)876 _check_collision (GstGLContext * context, GstGLContext * collision)
877 {
878   GThread *thread, *collision_thread;
879   gboolean ret = FALSE;
880 
881   if (!context || !collision)
882     return FALSE;
883 
884   thread = gst_gl_context_get_thread (context);
885   collision_thread = gst_gl_context_get_thread (collision);
886 
887   if (!thread || !collision_thread) {
888     ret = FALSE;
889     goto out;
890   }
891 
892   if (thread == collision_thread) {
893     ret = TRUE;
894     goto out;
895   }
896 
897 out:
898   if (thread)
899     g_thread_unref (thread);
900   if (collision_thread)
901     g_thread_unref (collision_thread);
902 
903   return ret;
904 }
905 
906 /**
907  * gst_gl_display_add_context:
908  * @display: a #GstGLDisplay
909  * @context: (transfer none): a #GstGLContext
910  *
911  * Returns: whether @context was successfully added. %FALSE may be returned
912  * if there already exists another context for @context's active thread.
913  *
914  * Must be called with the object lock held.
915  *
916  * Since: 1.6
917  */
918 gboolean
gst_gl_display_add_context(GstGLDisplay * display,GstGLContext * context)919 gst_gl_display_add_context (GstGLDisplay * display, GstGLContext * context)
920 {
921   GstGLContext *collision = NULL;
922   GstGLDisplay *context_display;
923   gboolean ret = TRUE;
924   GThread *thread;
925   GWeakRef *ref;
926 
927   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), FALSE);
928   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
929 
930   context_display = gst_gl_context_get_display (context);
931   g_assert (context_display == display);
932   gst_object_unref (context_display);
933 
934   thread = gst_gl_context_get_thread (context);
935   if (thread) {
936     collision = _get_gl_context_for_thread_unlocked (display, thread);
937     g_thread_unref (thread);
938 
939     /* adding the same context is a no-op */
940     if (context == collision) {
941       GST_LOG_OBJECT (display, "Attempting to add the same GL context %"
942           GST_PTR_FORMAT ". Ignoring", context);
943       ret = TRUE;
944       goto out;
945     }
946 
947     if (_check_collision (context, collision)) {
948       GST_DEBUG_OBJECT (display, "Collision detected adding GL context "
949           "%" GST_PTR_FORMAT, context);
950       ret = FALSE;
951       goto out;
952     }
953   }
954 
955   ref = g_new0 (GWeakRef, 1);
956   g_weak_ref_init (ref, context);
957 
958   GST_DEBUG_OBJECT (display, "Adding GL context %" GST_PTR_FORMAT, context);
959   display->priv->contexts = g_list_prepend (display->priv->contexts, ref);
960 
961 out:
962   if (collision)
963     gst_object_unref (collision);
964 
965   GST_DEBUG_OBJECT (display, "%ssuccessfully inserted context %" GST_PTR_FORMAT,
966       ret ? "" : "un", context);
967 
968   return ret;
969 }
970 
971 /**
972  * gst_gl_display_remove_context:
973  * @display: a #GstGLDisplay
974  * @context: (transfer none): the #GstGLContext to remove
975  *
976  * Must be called with the object lock held.
977  *
978  * Since: 1.18
979  */
980 void
gst_gl_display_remove_context(GstGLDisplay * display,GstGLContext * needle)981 gst_gl_display_remove_context (GstGLDisplay * display, GstGLContext * needle)
982 {
983   GstGLContext *context;
984   GList *prev = NULL, *l;
985 
986   g_return_if_fail (GST_IS_GL_DISPLAY (display));
987   g_return_if_fail (GST_IS_GL_CONTEXT (needle));
988 
989   l = display->priv->contexts;
990 
991   while (l) {
992     GWeakRef *ref = l->data;
993 
994     context = g_weak_ref_get (ref);
995     if (!context || context == needle) {
996       /* remove dead contexts */
997       g_weak_ref_clear (l->data);
998       g_free (l->data);
999       display->priv->contexts = g_list_delete_link (display->priv->contexts, l);
1000       if (context) {
1001         GST_INFO_OBJECT (display, "removed context %" GST_PTR_FORMAT
1002             " from internal list", context);
1003         gst_object_unref (context);
1004         return;
1005       }
1006       l = prev ? prev->next : display->priv->contexts;
1007       continue;
1008     }
1009     prev = l;
1010     l = l->next;
1011   }
1012 
1013   GST_WARNING_OBJECT (display, "%" GST_PTR_FORMAT " was not found in this "
1014       "display", needle);
1015 }
1016