]> git.sesse.net Git - vlc/blobdiff - modules/video_output/egl.c
Win32 Vout: if a crop/aspect ratio is received, force the texture update
[vlc] / modules / video_output / egl.c
index 270df0903b2de0b5aedd91847297e029a1c931ab..1f2d5b12283ef0b3724235614a038fd0dbd1b638 100644 (file)
@@ -3,7 +3,7 @@
  * @brief EGL OpenGL extension module
  */
 /*****************************************************************************
- * Copyright © 2010-2011 Rémi Denis-Courmont
+ * Copyright © 2010-2014 Rémi Denis-Courmont
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
 #include <stdlib.h>
 #include <assert.h>
 #include <EGL/egl.h>
+#include <EGL/eglext.h>
 
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_opengl.h>
 #include <vlc_vout_window.h>
-#ifdef __unix__
+#ifdef USE_PLATFORM_X11
 # include <vlc_xlib.h>
 #endif
-
-/* Plugin callbacks */
-static int OpenGLES2 (vlc_object_t *);
-static int OpenGLES (vlc_object_t *);
-static int OpenGL (vlc_object_t *);
-static void Close (vlc_object_t *);
-
-vlc_module_begin ()
-    set_shortname (N_("EGL"))
-    set_description (N_("EGL extension for OpenGL"))
-    set_category (CAT_VIDEO)
-    set_subcategory (SUBCAT_VIDEO_VOUT)
-    set_capability ("opengl", 50)
-    set_callbacks (OpenGL, Close)
-    add_shortcut ("egl")
-
-    add_submodule ()
-    set_capability ("opengl es2", 50)
-    set_callbacks (OpenGLES2, Close)
-    add_shortcut ("egl")
-
-    add_submodule ()
-    set_capability ("opengl es", 50)
-    set_callbacks (OpenGLES, Close)
-    add_shortcut ("egl")
-
-vlc_module_end ()
+#ifdef USE_PLATFORM_WAYLAND
+# include <wayland-egl.h>
+#endif
 
 typedef struct vlc_gl_sys_t
 {
     EGLDisplay display;
     EGLSurface surface;
     EGLContext context;
+#if defined (USE_PLATFORM_X11)
+    Display *x11;
+#endif
+#if defined (USE_PLATFORM_WAYLAND)
+    struct wl_egl_window *window;
+    unsigned width, height;
+#endif
 } vlc_gl_sys_t;
 
-/* OpenGL callbacks */
-static int MakeCurrent (vlc_gl_t *);
-static void ReleaseCurrent (vlc_gl_t *);
-static void SwapBuffers (vlc_gl_t *);
-static void *GetSymbol(vlc_gl_t *, const char *);
+static int MakeCurrent (vlc_gl_t *gl)
+{
+    vlc_gl_sys_t *sys = gl->sys;
 
-static bool CheckAPI (EGLDisplay dpy, const char *api)
+    if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
+                        sys->context) != EGL_TRUE)
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+static void ReleaseCurrent (vlc_gl_t *gl)
 {
-    const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
-    size_t apilen = strlen (api);
+    vlc_gl_sys_t *sys = gl->sys;
+
+    eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                    EGL_NO_CONTEXT);
+}
+
+#ifdef USE_PLATFORM_WAYLAND
+static void Resize (vlc_gl_t *gl, unsigned width, unsigned height)
+{
+    vlc_gl_sys_t *sys = gl->sys;
+
+    wl_egl_window_resize(sys->window, width, height,
+                         (sys->width - width) / 2,
+                         (sys->height - height) / 2);
+    sys->width = width;
+    sys->height = height;
+}
+#else
+# define Resize (NULL)
+#endif
 
-    /* Cannot use strtok_r() on constant string... */
-    do
+static void SwapBuffers (vlc_gl_t *gl)
+{
+    vlc_gl_sys_t *sys = gl->sys;
+
+    eglSwapBuffers (sys->display, sys->surface);
+}
+
+static void *GetSymbol(vlc_gl_t *gl, const char *procname)
+{
+    (void) gl;
+    return (void *)eglGetProcAddress (procname);
+}
+
+static bool CheckToken(const char *haystack, const char *needle)
+{
+    size_t len = strlen(needle);
+
+    while (haystack != NULL)
     {
-        while (*apis == ' ')
-            apis++;
-        if (!strncmp (apis, api, apilen)
-          && (memchr (" ", apis[apilen], 2) != NULL))
+        while (*haystack == ' ')
+            haystack++;
+        if (!strncmp(haystack, needle, len)
+         && (memchr(" ", haystack[len], 2) != NULL))
             return true;
 
-        apis = strchr (apis, ' ');
+        haystack = strchr(haystack, ' ');
     }
-    while (apis != NULL);
-
     return false;
 }
 
+static bool CheckAPI (EGLDisplay dpy, const char *api)
+{
+    const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
+    return CheckToken(apis, api);
+}
+
+static bool CheckClientExt(const char *name)
+{
+    const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+    return CheckToken(exts, name);
+}
+
 struct gl_api
 {
    const char name[10];
@@ -106,66 +138,179 @@ struct gl_api
    EGLint     attr[3];
 };
 
+#ifdef EGL_EXT_platform_base
+static EGLDisplay GetDisplayEXT(EGLenum plat, void *dpy, const EGLint *attrs)
+{
+    PFNEGLGETPLATFORMDISPLAYEXTPROC getDisplay =
+        (PFNEGLGETPLATFORMDISPLAYEXTPROC)
+        eglGetProcAddress("eglGetPlatformDisplayEXT");
+
+    assert(getDisplay != NULL);
+    return getDisplay(plat, dpy, attrs);
+}
+
+static EGLSurface CreateWindowSurfaceEXT(EGLDisplay dpy, EGLConfig config,
+                                         void *window, const EGLint *attrs)
+{
+    PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createSurface =
+        (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)
+        eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
+
+    assert(createSurface != NULL);
+    return createSurface(dpy, config, window, attrs);
+}
+#endif
+
+static EGLSurface CreateWindowSurface(EGLDisplay dpy, EGLConfig config,
+                                      void *window, const EGLint *attrs)
+{
+    EGLNativeWindowType *native = window;
+
+    return eglCreateWindowSurface(dpy, config, *native, attrs);
+}
+
+static void Close (vlc_object_t *obj)
+{
+    vlc_gl_t *gl = (vlc_gl_t *)obj;
+    vlc_gl_sys_t *sys = gl->sys;
+
+    if (sys->display != EGL_NO_DISPLAY)
+    {
+        if (sys->surface != EGL_NO_SURFACE)
+            eglDestroySurface(sys->display, sys->surface);
+        eglTerminate(sys->display);
+    }
+#ifdef USE_PLATFORM_X11
+    if (sys->x11 != NULL)
+        XCloseDisplay(sys->x11);
+#endif
+#ifdef USE_PLATFORM_WAYLAND
+    if (sys->window != NULL)
+        wl_egl_window_destroy(sys->window);
+#endif
+    free (sys);
+}
+
 /**
  * Probe EGL display availability
  */
 static int Open (vlc_object_t *obj, const struct gl_api *api)
 {
     vlc_gl_t *gl = (vlc_gl_t *)obj;
+    vlc_gl_sys_t *sys = malloc(sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
 
-    /* http://www.khronos.org/registry/egl/api/EGL/eglplatform.h defines the
-     * list and order of EGL platforms. */
-#if defined(_WIN32) || defined(__VC32__) \
- && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
-# define vlc_eglGetWindow(w) ((w)->handle.hwnd)
+    gl->sys = sys;
+    sys->display = EGL_NO_DISPLAY;
+    sys->surface = EGL_NO_SURFACE;
 
-#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
-# error Symbian EGL not supported.
+    vout_window_t *wnd = gl->surface;
+    EGLSurface (*createSurface)(EGLDisplay, EGLConfig, void *, const EGLint *)
+        = CreateWindowSurface;
+    void *window;
 
-#elif defined(__ANDROID__) || defined(ANDROID)
-# error Android EGL not supported
+#ifdef USE_PLATFORM_X11
+    sys->x11 = NULL;
 
-#elif defined(__unix__) /* X11 (tentative) */
-# define vlc_eglGetWindow(w) ((w)->handle.xid)
-    /* EGL can only use the default X11 display */
-    if (gl->surface->display.x11 != NULL)
-        return VLC_EGENERIC;
-    if (!vlc_xlib_init (obj))
-        return VLC_EGENERIC;
-
-#else
-# error EGL platform not recognized.
-#endif
+    if (wnd->type != VOUT_WINDOW_TYPE_XID || !vlc_xlib_init(obj))
+        goto error;
 
-    /* Initialize EGL display */
-    /* TODO: support various display types */
-    EGLDisplay dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
-    if (dpy == EGL_NO_DISPLAY)
-        return VLC_EGENERIC;
+    window = &wnd->handle.xid;
+    sys->x11 = XOpenDisplay(wnd->display.x11);
+    if (sys->x11 == NULL)
+        goto error;
 
-    vlc_gl_sys_t *sys = malloc (sizeof (*sys));
-    if (unlikely(sys == NULL))
-        return VLC_ENOMEM;
-    gl->sys = sys;
-    sys->display = dpy;
+    int snum;
+    {
+        XWindowAttributes wa;
 
-    EGLint major, minor;
-    if (eglInitialize (dpy, &major, &minor) != EGL_TRUE)
+        if (!XGetWindowAttributes(sys->x11, wnd->handle.xid, &wa))
+            goto error;
+        snum = XScreenNumberOfScreen(wa.screen);
+    }
+# ifdef EGL_EXT_platform_x11
+    if (CheckClientExt("EGL_EXT_platform_x11"))
     {
-        /* No need to call eglTerminate() in this case */
-        free (sys);
-        return VLC_EGENERIC;
+        const EGLint attrs[] = {
+            EGL_PLATFORM_X11_SCREEN_EXT, snum,
+            EGL_NONE
+        };
+        sys->display = GetDisplayEXT(EGL_PLATFORM_X11_EXT, sys->x11, attrs);
+        createSurface = CreateWindowSurfaceEXT;
     }
+    else
+# endif
+    {
+# ifdef __unix__
+        if (snum == XDefaultScreen(sys->x11))
+            sys->display = eglGetDisplay(sys->x11);
+    }
+# endif
+
+#elif defined (USE_PLATFORM_WAYLAND)
+    sys->window = NULL;
 
-    if (major != 1 || minor < api->min_minor || !CheckAPI (dpy, api->name))
+    if (wnd->type != VOUT_WINDOW_TYPE_WAYLAND)
         goto error;
 
-    msg_Dbg (obj, "EGL version %s by %s", eglQueryString (dpy, EGL_VERSION),
-             eglQueryString (dpy, EGL_VENDOR));
+# ifdef EGL_EXT_platform_wayland
+    if (!CheckClientExt("EGL_EXT_platform_wayland"))
+        goto error;
+
+    /* Resize() should be called with the proper size before Swap() */
+    window = wl_egl_window_create(wnd->handle.wl, 1, 1);
+    if (window == NULL)
+        goto error;
+    sys->window = window;
+
+    sys->display = GetDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, wnd->display.wl,
+                                 NULL);
+    createSurface = CreateWindowSurfaceEXT;
+
+# endif
+
+#elif defined (USE_PLATFORM_WIN32)
+    if (wnd->type != VOUT_WINDOW_TYPE_HWND)
+        goto error;
+
+    window = &wnd->handle.hwnd;
+# if defined (_WIN32) || defined (__VC32__) \
+  && !defined (__CYGWIN__) && !defined (__SCITECH_SNAP__)
+    sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+# endif
+
+#elif defined (USE_PLATFORM_ANDROID)
+    if (wnd->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
+        goto error;
+
+    window = &wnd->handle.anativewindow;
+# if defined (__ANDROID__) || defined (ANDROID)
+    sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+# endif
+
+#endif
+
+    if (sys->display == EGL_NO_DISPLAY)
+        goto error;
+
+    /* Initialize EGL display */
+    EGLint major, minor;
+    if (eglInitialize(sys->display, &major, &minor) != EGL_TRUE)
+        goto error;
+    msg_Dbg(obj, "EGL version %s by %s",
+            eglQueryString(sys->display, EGL_VERSION),
+            eglQueryString(sys->display, EGL_VENDOR));
+
+    const char *ext = eglQueryString(sys->display, EGL_EXTENSIONS);
+    if (*ext)
+        msg_Dbg(obj, " extensions: %s", ext);
+
+    if (major != 1 || minor < api->min_minor
+     || !CheckAPI(sys->display, api->name))
     {
-        const char *ext = eglQueryString (dpy, EGL_EXTENSIONS);
-        if (*ext)
-            msg_Dbg (obj, " extensions: %s", ext);
+        msg_Err(obj, "cannot select %s API", api->name);
+        goto error;
     }
 
     const EGLint conf_attr[] = {
@@ -178,7 +323,7 @@ static int Open (vlc_object_t *obj, const struct gl_api *api)
     EGLConfig cfgv[1];
     EGLint cfgc;
 
-    if (eglChooseConfig (dpy, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
+    if (eglChooseConfig(sys->display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
      || cfgc == 0)
     {
         msg_Err (obj, "cannot choose EGL configuration");
@@ -186,14 +331,12 @@ static int Open (vlc_object_t *obj, const struct gl_api *api)
     }
 
     /* Create a drawing surface */
-    EGLNativeWindowType win = vlc_eglGetWindow(gl->surface);
-    EGLSurface surface = eglCreateWindowSurface (dpy, cfgv[0], win, NULL);
-    if (surface == EGL_NO_SURFACE)
+    sys->surface = createSurface(sys->display, cfgv[0], window, NULL);
+    if (sys->surface == EGL_NO_SURFACE)
     {
         msg_Err (obj, "cannot create EGL window surface");
         goto error;
     }
-    sys->surface = surface;
 
     if (eglBindAPI (api->api) != EGL_TRUE)
     {
@@ -201,8 +344,8 @@ static int Open (vlc_object_t *obj, const struct gl_api *api)
         goto error;
     }
 
-    EGLContext ctx = eglCreateContext (dpy, cfgv[0], EGL_NO_CONTEXT,
-                                       api->attr);
+    EGLContext ctx = eglCreateContext(sys->display, cfgv[0], EGL_NO_CONTEXT,
+                                      api->attr);
     if (ctx == EGL_NO_CONTEXT)
     {
         msg_Err (obj, "cannot create EGL context");
@@ -211,13 +354,11 @@ static int Open (vlc_object_t *obj, const struct gl_api *api)
     sys->context = ctx;
 
     /* Initialize OpenGL callbacks */
-    gl->sys = sys;
     gl->makeCurrent = MakeCurrent;
     gl->releaseCurrent = ReleaseCurrent;
+    gl->resize = Resize;
     gl->swap = SwapBuffers;
     gl->getProcAddress = GetSymbol;
-    gl->lock = NULL;
-    gl->unlock = NULL;
     return VLC_SUCCESS;
 
 error:
@@ -252,42 +393,23 @@ static int OpenGL (vlc_object_t *obj)
     return Open (obj, &api);
 }
 
-static void Close (vlc_object_t *obj)
-{
-    vlc_gl_t *gl = (vlc_gl_t *)obj;
-    vlc_gl_sys_t *sys = gl->sys;
-
-    eglTerminate (sys->display);
-    free (sys);
-}
-
-static int MakeCurrent (vlc_gl_t *gl)
-{
-    vlc_gl_sys_t *sys = gl->sys;
-
-    if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
-                        sys->context) != EGL_TRUE)
-        return VLC_EGENERIC;
-    return VLC_SUCCESS;
-}
-
-static void ReleaseCurrent (vlc_gl_t *gl)
-{
-    vlc_gl_sys_t *sys = gl->sys;
-
-    eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                    EGL_NO_CONTEXT);
-}
+vlc_module_begin ()
+    set_shortname (N_("EGL"))
+    set_description (N_("EGL extension for OpenGL"))
+    set_category (CAT_VIDEO)
+    set_subcategory (SUBCAT_VIDEO_VOUT)
+    set_capability ("opengl", 50)
+    set_callbacks (OpenGL, Close)
+    add_shortcut ("egl")
 
-static void SwapBuffers (vlc_gl_t *gl)
-{
-    vlc_gl_sys_t *sys = gl->sys;
+    add_submodule ()
+    set_capability ("opengl es2", 50)
+    set_callbacks (OpenGLES2, Close)
+    add_shortcut ("egl")
 
-    eglSwapBuffers (sys->display, sys->surface);
-}
+    add_submodule ()
+    set_capability ("opengl es", 50)
+    set_callbacks (OpenGLES, Close)
+    add_shortcut ("egl")
 
-static void *GetSymbol(vlc_gl_t *gl, const char *procname)
-{
-    (void) gl;
-    return (void *)eglGetProcAddress (procname);
-}
+vlc_module_end ()