1 /*
2  * GStreamer
3  * Copyright (C) 2014 Matthew Waters <ystreet00@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "gstgldisplay_egl.h"
26 
27 #include <gst/gl/gstglfeature.h>
28 
29 #include "gstegl.h"
30 #include "gsteglimage.h"
31 #include "gstglmemoryegl.h"
32 
33 GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
34 #define GST_CAT_DEFAULT gst_gl_display_debug
35 
36 #ifndef EGL_PLATFORM_X11
37 #define EGL_PLATFORM_X11 0x31D5
38 #endif
39 #ifndef EGL_PLATFORM_WAYLAND
40 #define EGL_PLATFORM_WAYLAND 0x31D8
41 #endif
42 #ifndef EGL_PLATFORM_GBM_MESA
43 #define EGL_PLATFORM_GBM_MESA 0x31D7
44 #endif
45 #ifndef EGL_PLATFORM_ANDROID
46 #define EGL_PLATFORM_ANDROID 0x3141
47 #endif
48 
49 typedef EGLDisplay (*_gst_eglGetPlatformDisplay_type) (EGLenum platform,
50     void *native_display, const EGLint * attrib_list);
51 
52 G_DEFINE_TYPE (GstGLDisplayEGL, gst_gl_display_egl, GST_TYPE_GL_DISPLAY);
53 
54 static void gst_gl_display_egl_finalize (GObject * object);
55 static guintptr gst_gl_display_egl_get_handle (GstGLDisplay * display);
56 
57 static void
gst_gl_display_egl_class_init(GstGLDisplayEGLClass * klass)58 gst_gl_display_egl_class_init (GstGLDisplayEGLClass * klass)
59 {
60   GST_GL_DISPLAY_CLASS (klass)->get_handle =
61       GST_DEBUG_FUNCPTR (gst_gl_display_egl_get_handle);
62 
63   G_OBJECT_CLASS (klass)->finalize = gst_gl_display_egl_finalize;
64 }
65 
66 static void
gst_gl_display_egl_init(GstGLDisplayEGL * display_egl)67 gst_gl_display_egl_init (GstGLDisplayEGL * display_egl)
68 {
69   GstGLDisplay *display = (GstGLDisplay *) display_egl;
70 
71   display->type = GST_GL_DISPLAY_TYPE_EGL;
72   display_egl->foreign_display = FALSE;
73 
74   gst_gl_memory_egl_init_once ();
75 }
76 
77 static void
gst_gl_display_egl_finalize(GObject * object)78 gst_gl_display_egl_finalize (GObject * object)
79 {
80   GstGLDisplayEGL *display_egl = GST_GL_DISPLAY_EGL (object);
81 
82   if (display_egl->display && !display_egl->foreign_display) {
83     eglTerminate (display_egl->display);
84     display_egl->display = NULL;
85   }
86 
87   G_OBJECT_CLASS (gst_gl_display_egl_parent_class)->finalize (object);
88 }
89 
90 /**
91  * gst_gl_display_egl_get_from_native:
92  * @type: a #GstGLDisplayType
93  * @display: pointer to a display (or 0)
94  *
95  * Attempts to create a new #EGLDisplay from @display.  If @type is
96  * %GST_GL_DISPLAY_TYPE_ANY, then @display must be 0. @type must not be
97  * %GST_GL_DISPLAY_TYPE_NONE.
98  *
99  * Returns: A #EGLDisplay or %EGL_NO_DISPLAY
100  *
101  * Since: 1.12
102  */
103 gpointer
gst_gl_display_egl_get_from_native(GstGLDisplayType type,guintptr display)104 gst_gl_display_egl_get_from_native (GstGLDisplayType type, guintptr display)
105 {
106   const gchar *egl_exts;
107   EGLDisplay ret = EGL_NO_DISPLAY;
108   _gst_eglGetPlatformDisplay_type _gst_eglGetPlatformDisplay;
109 
110   g_return_val_if_fail (type != GST_GL_DISPLAY_TYPE_NONE, EGL_NO_DISPLAY);
111   g_return_val_if_fail ((type != GST_GL_DISPLAY_TYPE_ANY && display != 0)
112       || (type == GST_GL_DISPLAY_TYPE_ANY && display == 0), EGL_NO_DISPLAY);
113 
114   /* given an EGLDisplay already */
115   if (type == GST_GL_DISPLAY_TYPE_EGL)
116     return (gpointer) display;
117 
118   egl_exts = eglQueryString (EGL_NO_DISPLAY, EGL_EXTENSIONS);
119   GST_DEBUG ("egl no display extensions: %s", egl_exts);
120 
121   if (eglGetError () != EGL_SUCCESS || !egl_exts)
122     goto default_display;
123 
124   /* check if we can actually choose the egl display type */
125   if (!gst_gl_check_extension ("EGL_KHR_client_get_all_proc_addresses",
126           egl_exts))
127     goto default_display;
128   if (!gst_gl_check_extension ("EGL_EXT_platform_base", egl_exts))
129     goto default_display;
130 
131   _gst_eglGetPlatformDisplay = (_gst_eglGetPlatformDisplay_type)
132       eglGetProcAddress ("eglGetPlatformDisplay");
133   if (!_gst_eglGetPlatformDisplay)
134     _gst_eglGetPlatformDisplay = (_gst_eglGetPlatformDisplay_type)
135         eglGetProcAddress ("eglGetPlatformDisplayEXT");
136   if (!_gst_eglGetPlatformDisplay)
137     goto default_display;
138 
139   /* try each platform in turn */
140 #if GST_GL_HAVE_WINDOW_X11
141   if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_X11) &&
142       (gst_gl_check_extension ("EGL_KHR_platform_x11", egl_exts) ||
143           gst_gl_check_extension ("EGL_EXT_platform_x11", egl_exts))) {
144     ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_X11, (gpointer) display,
145         NULL);
146   }
147 #endif
148 #if GST_GL_HAVE_WINDOW_WAYLAND
149   if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_WAYLAND) &&
150       (gst_gl_check_extension ("EGL_KHR_platform_wayland", egl_exts) ||
151           gst_gl_check_extension ("EGL_EXT_platform_wayland", egl_exts))) {
152     ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_WAYLAND, (gpointer) display,
153         NULL);
154   }
155 #endif
156 #if GST_GL_HAVE_WINDOW_GBM
157   if (ret == EGL_NO_DISPLAY && (type & GST_GL_DISPLAY_TYPE_GBM) &&
158       (gst_gl_check_extension ("EGL_MESA_platform_gbm", egl_exts) ||
159           gst_gl_check_extension ("EGL_KHR_platform_gbm", egl_exts))) {
160     ret = _gst_eglGetPlatformDisplay (EGL_PLATFORM_GBM_MESA, (gpointer) display,
161         NULL);
162   }
163 #endif
164   /* android only has one winsys/display connection */
165 
166   if (ret != EGL_NO_DISPLAY)
167     return ret;
168 
169   /* otherwise rely on the implementation to choose the correct display
170    * based on the pointer */
171 default_display:
172   return (gpointer) eglGetDisplay ((EGLNativeDisplayType) display);
173 }
174 
175 /**
176  * gst_gl_display_egl_new:
177  *
178  * Create a new #GstGLDisplayEGL using the default EGL_DEFAULT_DISPLAY.
179  *
180  * Returns: (transfer full): a new #GstGLDisplayEGL or %NULL
181  */
182 GstGLDisplayEGL *
gst_gl_display_egl_new(void)183 gst_gl_display_egl_new (void)
184 {
185   GstGLDisplayEGL *ret;
186 
187   GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay");
188 
189   ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
190   gst_object_ref_sink (ret);
191   ret->display =
192       gst_gl_display_egl_get_from_native (GST_GL_DISPLAY_TYPE_ANY, 0);
193 
194   if (!ret->display) {
195     GST_INFO ("Failed to open EGL display connection");
196   }
197 
198   return ret;
199 }
200 
201 /**
202  * gst_gl_display_egl_new_with_display:
203  * @display: an existing and connected EGLDisplay
204  *
205  * Creates a new display connection from a EGLDisplay.
206  *
207  * Returns: (transfer full): a new #GstGLDisplayEGL
208  *
209  * Since: 1.12
210  */
211 GstGLDisplayEGL *
gst_gl_display_egl_new_with_egl_display(gpointer display)212 gst_gl_display_egl_new_with_egl_display (gpointer display)
213 {
214   GstGLDisplayEGL *ret;
215 
216   g_return_val_if_fail (display != NULL, NULL);
217 
218   GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay");
219 
220   ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
221   gst_object_ref_sink (ret);
222 
223   ret->display = display;
224   ret->foreign_display = TRUE;
225 
226   return ret;
227 }
228 
229 static gpointer
_ref_if_set(gpointer data,gpointer user_data)230 _ref_if_set (gpointer data, gpointer user_data)
231 {
232   if (data)
233     gst_object_ref (data);
234   return data;
235 }
236 
237 /**
238  * gst_gl_display_egl_from_gl_display:
239  * @display: an existing #GstGLDisplay
240  *
241  * Creates a EGL display connection from a native Display.
242  *
243  * This function will return the same value for multiple calls with the same
244  * @display.
245  *
246  * Returns: (transfer full): a new #GstGLDisplayEGL
247  *
248  * Since: 1.12
249  */
250 GstGLDisplayEGL *
gst_gl_display_egl_from_gl_display(GstGLDisplay * display)251 gst_gl_display_egl_from_gl_display (GstGLDisplay * display)
252 {
253   GstGLDisplayEGL *ret;
254   GstGLDisplayType display_type;
255   guintptr native_display;
256 
257   g_return_val_if_fail (GST_IS_GL_DISPLAY (display), NULL);
258 
259   GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay");
260 
261   if (GST_IS_GL_DISPLAY_EGL (display)) {
262     GST_LOG_OBJECT (display, "display %" GST_PTR_FORMAT "is already a "
263         "GstGLDisplayEGL", display);
264     return gst_object_ref (display);
265   }
266 
267   /* try to get a previously set GstGLDisplayEGL */
268   ret = g_object_dup_data (G_OBJECT (display), GST_GL_DISPLAY_EGL_NAME,
269       (GDuplicateFunc) _ref_if_set, NULL);
270   if (ret && GST_IS_GL_DISPLAY_EGL (ret)) {
271     GST_LOG_OBJECT (display, "display %" GST_PTR_FORMAT "already has a "
272         "GstGLDisplayEGL %" GST_PTR_FORMAT, display, ret);
273     return ret;
274   }
275 
276   if (ret)
277     gst_object_unref (ret);
278 
279   display_type = gst_gl_display_get_handle_type (display);
280   native_display = gst_gl_display_get_handle (display);
281 
282   g_return_val_if_fail (native_display != 0, NULL);
283   g_return_val_if_fail (display_type != GST_GL_DISPLAY_TYPE_NONE, NULL);
284 
285   ret = g_object_new (GST_TYPE_GL_DISPLAY_EGL, NULL);
286   gst_object_ref_sink (ret);
287 
288   ret->display =
289       gst_gl_display_egl_get_from_native (display_type, native_display);
290 
291   if (!ret->display) {
292     GST_WARNING_OBJECT (ret, "failed to get EGLDisplay from native display");
293     gst_object_unref (ret);
294     return NULL;
295   }
296   g_object_set_data_full (G_OBJECT (display), GST_GL_DISPLAY_EGL_NAME,
297       gst_object_ref (ret), (GDestroyNotify) gst_object_unref);
298 
299   return ret;
300 }
301 
302 static guintptr
gst_gl_display_egl_get_handle(GstGLDisplay * display)303 gst_gl_display_egl_get_handle (GstGLDisplay * display)
304 {
305   return (guintptr) GST_GL_DISPLAY_EGL (display)->display;
306 }
307