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