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