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