]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
EGL: need to load libGL too...
[vlc] / modules / video_output / egl.c
1 /**
2  * @file egl.c
3  * @brief EGL video output module
4  */
5 /*****************************************************************************
6  * Copyright © 2010-2011 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1
11  * 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
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <EGL/egl.h>
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_vout_display.h>
34 #include <vlc_opengl.h>
35 #include "opengl.h"
36 #ifdef __unix__
37 # include <vlc_xlib.h>
38 #endif
39
40 #if USE_OPENGL_ES
41 # define VLC_API_NAME "OpenGL_ES"
42 # define VLC_API EGL_OPENGL_ES_API
43 # if USE_OPENGL_ES == 2
44 #  define VLC_RENDERABLE_BIT EGL_OPENGL_ES2_BIT
45 # else
46 #  define VLC_RENDERABLE_BIT EGL_OPENGL_ES_BIT
47 # endif
48 #else
49 # define VLC_API_NAME "OpenGL"
50 # define VLC_API EGL_OPENGL_API
51 # define VLC_RENDERABLE_BIT EGL_OPENGL_BIT
52 #endif
53
54 #ifdef __unix__
55 # include <dlfcn.h>
56 #endif
57
58 /* Plugin callbacks */
59 static int Open (vlc_object_t *);
60 static void Close (vlc_object_t *);
61
62 vlc_module_begin ()
63     set_shortname (N_("EGL"))
64     set_description (N_("EGL video output"))
65     set_category (CAT_VIDEO)
66     set_subcategory (SUBCAT_VIDEO_VOUT)
67     set_capability ("vout display", 0)
68     set_callbacks (Open, Close)
69 vlc_module_end ()
70
71 struct vout_display_sys_t
72 {
73     EGLDisplay display;
74     EGLSurface surface;
75
76     vlc_gl_t gl;
77     vout_display_opengl_t vgl;
78
79     picture_pool_t *pool;
80     vout_window_t *window;
81 };
82
83 /* Display callbacks */
84 static picture_pool_t *Pool (vout_display_t *, unsigned);
85 static void PictureRender (vout_display_t *, picture_t *, subpicture_t *);
86 static void PictureDisplay (vout_display_t *, picture_t *, subpicture_t *);
87 static int Control (vout_display_t *, int, va_list);
88 /* OpenGL callbacks */
89 static void SwapBuffers (vlc_gl_t *gl);
90
91 static bool CheckAPI (EGLDisplay dpy, const char *api)
92 {
93     const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
94     size_t apilen = strlen (api);
95
96     /* Cannot use strtok_r() on constant string... */
97     do
98     {
99         if (!strncmp (apis, api, apilen)
100           && (memchr (" ", apis[apilen], 2) != NULL))
101             return true;
102
103         apis = strchr (apis, ' ');
104     }
105     while (apis != NULL);
106
107     return false;
108 }
109
110 static vout_window_t *MakeWindow (vout_display_t *vd, EGLNativeWindowType *id)
111 {
112     vout_window_cfg_t wnd_cfg;
113
114     memset (&wnd_cfg, 0, sizeof (wnd_cfg));
115 #if defined (WIN32)
116     wnd_cfg.type = VOUT_WINDOW_TYPE_HWND;
117 #elif defined (__unix__)
118     wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
119 #else
120 # error Unknown native window type!
121 #endif
122     wnd_cfg.x = var_InheritInteger (vd, "video-x");
123     wnd_cfg.y = var_InheritInteger (vd, "video-y");
124     wnd_cfg.width  = vd->cfg->display.width;
125     wnd_cfg.height = vd->cfg->display.height;
126
127     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
128     if (wnd != NULL)
129 #if defined (WIN32)
130         *id = wnd->handle.hwnd;
131 #elif defined (__unix__)
132         *id = wnd->handle.xid;
133 #endif
134     else
135         msg_Err (vd, "parent window not available");
136     return wnd;
137 }
138
139 /**
140  * Probe EGL display availability
141  */
142 static int Open (vlc_object_t *obj)
143 {
144 #ifdef __unix__
145     if (!vlc_xlib_init (obj))
146         return VLC_EGENERIC;
147 #endif
148     vout_display_t *vd = (vout_display_t *)obj;
149
150     /* Initialize EGL display */
151     /* TODO: support various display types */
152     EGLDisplay dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
153     if (dpy == EGL_NO_DISPLAY)
154         return VLC_EGENERIC;
155
156     vout_display_sys_t *sys = malloc (sizeof (*sys));
157     if (unlikely(sys == NULL))
158         return VLC_ENOMEM;
159     vd->sys = sys;
160     sys->display = dpy;
161     sys->gl.sys = NULL;
162     sys->pool = NULL;
163
164 #ifdef __unix__
165     /* XXX Explicit hack!
166      * Mesa EGL plugins (as of version 7.8.2) are not properly linked to
167      * libEGL.so even though they import some of its symbols. This is
168      * typically not a problem. Unfortunately, LibVLC loads plugins as
169      * RTLD_LOCAL so that they do not pollute the namespace. Then the
170      * libEGL symbols are not visible to EGL plugins, and the run-time
171      * linker exits the whole process. */
172     if (dlopen ("libEGL.so", RTLD_GLOBAL|RTLD_NOW) == NULL)
173         msg_Warn (gl, "libEGL cannot be loaded. Process might crash.");
174     if (dlopen ("libGL.so", RTLD_GLOBAL|RTLD_NOW) == NULL)
175         msg_Warn (gl, "libGL cannot be loaded. Process might crash.");
176 #endif
177
178     EGLint major, minor;
179     if (eglInitialize (dpy, &major, &minor) != EGL_TRUE)
180     {
181         /* No need to call eglTerminate() in this case */
182         free (sys);
183         return VLC_EGENERIC;
184     }
185
186     if (major != 1)
187         goto abort;
188 #if USE_OPENGL_ES == 2
189     if (minor < 3) /* Well well, this wouldn't compile with 1.2 anyway */
190         goto abort;
191 #elif USE_OPENGL_ES == 0
192     if (minor < 4)
193         goto abort;
194 #endif
195
196     if (!CheckAPI (dpy, VLC_API_NAME))
197         goto abort;
198
199     msg_Dbg (obj, "EGL version %s by %s", eglQueryString (dpy, EGL_VERSION),
200              eglQueryString (dpy, EGL_VENDOR));
201     {
202         const char *ext = eglQueryString (dpy, EGL_EXTENSIONS);
203         if (*ext)
204             msg_Dbg (obj, " extensions: %s", ext);
205     }
206
207     static const EGLint conf_attr[] = {
208         EGL_RED_SIZE, 5,
209         EGL_GREEN_SIZE, 5,
210         EGL_BLUE_SIZE, 5,
211         EGL_RENDERABLE_TYPE, VLC_RENDERABLE_BIT,
212         EGL_NONE
213     };
214     EGLConfig cfgv[1];
215     EGLint cfgc;
216
217     if (eglChooseConfig (dpy, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
218      || cfgc == 0)
219         goto abort;
220
221     /* Create a drawing surface */
222     EGLNativeWindowType win;
223     sys->window = MakeWindow (vd, &win);
224     if (sys->window == NULL)
225         goto abort;
226
227     EGLSurface surface = eglCreateWindowSurface (dpy, cfgv[0], win, NULL);
228     if (surface == EGL_NO_SURFACE)
229     {
230         msg_Err (obj, "cannot create EGL window surface");
231         goto error;
232     }
233     sys->surface = surface;
234
235     if (eglBindAPI (VLC_API) != EGL_TRUE)
236     {
237         msg_Err (obj, "cannot bind EGL API");
238         goto error;
239     }
240
241     static const EGLint ctx_attr[] = {
242 #if USE_OPENGL_ES
243         EGL_CONTEXT_CLIENT_VERSION, USE_OPENGL_ES,
244 #endif
245         EGL_NONE
246     };   
247
248     EGLContext ctx = eglCreateContext (dpy, cfgv[0], EGL_NO_CONTEXT,
249                                        ctx_attr);
250     if (ctx == EGL_NO_CONTEXT)
251     {
252         msg_Err (obj, "cannot create EGL context");
253         goto error;
254     }
255
256     if (eglMakeCurrent (dpy, surface, surface, ctx) != EGL_TRUE)
257         goto error;
258
259     /* Initialize OpenGL callbacks */
260     sys->gl.lock = NULL;
261     sys->gl.unlock = NULL;
262     sys->gl.swap = SwapBuffers;
263     sys->gl.sys = sys;
264
265     if (vout_display_opengl_Init (&sys->vgl, &vd->fmt, &sys->gl))
266         goto error;
267
268     /* Initialize video display */
269     vd->info.has_pictures_invalid = false;
270     vd->info.has_event_thread = false;
271     vd->pool = Pool;
272     vd->prepare = PictureRender;
273     vd->display = PictureDisplay;
274     vd->control = Control;
275     vd->manage = NULL;
276
277     return VLC_SUCCESS;
278 error:
279     vout_display_DeleteWindow (vd, sys->window);
280 abort:
281     eglTerminate (dpy);
282     free (sys);
283     return VLC_EGENERIC;
284 }
285
286
287 /**
288  * Destrisconnect from the X server.
289  */
290 static void Close (vlc_object_t *obj)
291 {
292     vout_display_t *vd = (vout_display_t *)obj;
293     vout_display_sys_t *sys = vd->sys;
294     EGLDisplay dpy = sys->display;
295
296     if (sys->gl.sys != NULL)
297         vout_display_opengl_Clean (&sys->vgl);
298     eglTerminate (dpy);
299     vout_display_DeleteWindow (vd, sys->window);
300     free (sys);
301 }
302
303 static void SwapBuffers (vlc_gl_t *gl)
304 {
305     vout_display_sys_t *sys = gl->sys;
306
307     eglSwapBuffers (sys->display, sys->surface);
308 }
309
310 /**
311  * Return a direct buffer
312  */
313 static picture_pool_t *Pool (vout_display_t *vd, unsigned count)
314 {
315     vout_display_sys_t *sys = vd->sys;
316
317     if (!sys->pool)
318         sys->pool = vout_display_opengl_GetPool (&sys->vgl);
319     (void) count;
320     return sys->pool;
321 }
322
323 static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
324 {
325     vout_display_sys_t *sys = vd->sys;
326
327     vout_display_opengl_Prepare (&sys->vgl, pic);
328     (void)subpicture;
329 }
330
331 static void PictureDisplay (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
332 {
333     vout_display_sys_t *sys = vd->sys;
334
335     vout_display_opengl_Display (&sys->vgl, &vd->source);
336     picture_Release (pic);
337     (void)subpicture;
338 }
339
340 static int Control (vout_display_t *vd, int query, va_list ap)
341 {
342     vout_display_sys_t *sys = vd->sys;
343
344     switch (query)
345     {
346       case VOUT_DISPLAY_HIDE_MOUSE:
347       case VOUT_DISPLAY_RESET_PICTURES: // not needed?
348           break;
349
350       case VOUT_DISPLAY_CHANGE_FULLSCREEN:
351       {
352         const vout_display_cfg_t *cfg =
353             va_arg (ap, const vout_display_cfg_t *);
354
355         return vout_window_SetFullScreen (sys->window, cfg->is_fullscreen);
356       }
357
358       case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
359       {
360         unsigned state = va_arg (ap, unsigned);
361
362         return vout_window_SetState (sys->window, state);
363       }
364
365       case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
366       case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
367       case VOUT_DISPLAY_CHANGE_ZOOM:
368       {
369         const vout_display_cfg_t *cfg = va_arg (ap, const vout_display_cfg_t *);
370         const video_format_t *src = &vd->source;
371
372         if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
373         {
374             bool force = false;
375
376             force = va_arg (ap, int);
377             if (force
378              && (cfg->display.width  != vd->cfg->display.width
379               || cfg->display.height != vd->cfg->display.height)
380              && vout_window_SetSize (sys->window,
381                                      cfg->display.width, cfg->display.height))
382                 return VLC_EGENERIC;
383         }
384
385         vout_display_place_t place;
386
387         vout_display_PlacePicture (&place, src, cfg, false);
388         glViewport (0, 0, place.width, place.height);
389         return VLC_SUCCESS;
390       }
391
392       case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
393       case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
394       {
395         const vout_display_cfg_t *cfg = vd->cfg;
396         const video_format_t *src = va_arg (ap, const video_format_t *);
397         vout_display_place_t place;
398
399         vout_display_PlacePicture (&place, src, cfg, false);
400         glViewport (0, 0, place.width, place.height);
401         return VLC_SUCCESS;
402       }
403
404       case VOUT_DISPLAY_GET_OPENGL:
405       {
406         vlc_gl_t **gl = va_arg (ap, vlc_gl_t **);
407
408         *gl = &sys->gl;
409         return VLC_SUCCESS;
410       }
411
412       default:
413         msg_Err (vd, "Unknown request %d", query);
414     }
415     return VLC_EGENERIC;
416 }
417