[vlc-devel] [RFC PATCH] opengl: add converter_va

Thomas Guillem thomas at gllm.fr
Wed Feb 15 11:10:00 CET 2017


This converter allow to render a va_surface directly to a GL surface via EGL
(X11/WAYLAND and EXT_image_dma_buf_import[1]) or GLX.

The full working branch can be found here https://github.com/tguillem/vlc/commits/vagl-4

TODO:

 - I assume that the vaImage fourcc is NV12. There is a chicken-egg situtation
   here. It seems that we can only know the internal fourcc after a frame is
   decoded, but we need to know this fourcc during vout initialization in order
   to create the good fragment shader and generate textures.

 - CVPX to SW video filters.

[1]: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt.
---
 modules/hw/va/vlc_va.h                     |   7 +-
 modules/video_output/Makefile.am           |  20 ++
 modules/video_output/opengl/converter_va.c | 426 +++++++++++++++++++++++++++++
 modules/video_output/opengl/internal.h     |   6 +
 modules/video_output/opengl/vout_helper.c  |   3 +
 5 files changed, 458 insertions(+), 4 deletions(-)
 create mode 100644 modules/video_output/opengl/converter_va.c

diff --git a/modules/hw/va/vlc_va.h b/modules/hw/va/vlc_va.h
index 431b5a70d0..5caa4f9385 100644
--- a/modules/hw/va/vlc_va.h
+++ b/modules/hw/va/vlc_va.h
@@ -93,10 +93,6 @@ int vlc_va_VaFourcc(vlc_fourcc_t fourcc,
             *va_fourcc    = VA_FOURCC_444P;
             *va_rt_format = VA_RT_FORMAT_YUV444;
             break;
-        case VLC_CODEC_VAAPI_OPAQUE:
-            *va_fourcc    = VA_FOURCC_YV12;
-            *va_rt_format = VA_RT_FORMAT_YUV420;
-            break;
         default:
             return VA_STATUS_ERROR_UNIMPLEMENTED;
     }
@@ -112,6 +108,9 @@ struct picture_sys_t {
     VASurfaceID   va_surface_id;
     void *render_targets;
     int num_render_targets;
+    VAImage      va_image;
+    VABufferInfo va_buffer_info;
+    void *       egl_images[3];
 };
 
 picture_pool_t *vlc_va_PoolAlloc(vlc_object_t *o, VADisplay va_dpy, unsigned requested_count,
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index eb9dedf0af..de9e428b75 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -10,6 +10,26 @@ OPENGL_DISPLAY_SRCS = video_output/opengl/vout_helper.c \
 if HAVE_ANDROID
 OPENGL_DISPLAY_SRCS += video_output/opengl/converter_android.c
 endif
+if HAVE_VAAPI
+OPENGL_DISPLAY_LIBS += $(LIBVA_LIBS)
+OPENGL_DISPLAY_SRCS += video_output/opengl/converter_va.c hw/va/vlc_va.c
+		       hw/va/vlc_va.h
+OPENGL_DISPLAY_CFLGS += -DVLCGL_CONV_VA
+if HAVE_WAYLAND_EGL
+if HAVE_VAAPI_WL
+OPENGL_DISPLAY_LIBS += $(LIBVA_WL_LIBS) $(LIBVA_EGL_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_WL
+endif
+if HAVE_VAAPI_X11
+OPENGL_DISPLAY_LIBS += $(LIBVA_X11_LIBS) $(LIBVA_EGL_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_X11
+endif
+endif
+if HAVE_VAAPI_GLX
+OPENGL_DISPLAY_LIBS += $(LIBVA_GLX_LIBS)
+OPENGL_DISPLAY_CFLGS += -DHAVE_VA_GLX
+endif
+endif
 
 if HAVE_DECKLINK
 libdecklinkoutput_plugin_la_SOURCES = video_output/decklink.cpp
diff --git a/modules/video_output/opengl/converter_va.c b/modules/video_output/opengl/converter_va.c
new file mode 100644
index 0000000000..f20cc6c6a6
--- /dev/null
+++ b/modules/video_output/opengl/converter_va.c
@@ -0,0 +1,426 @@
+/*****************************************************************************
+ * converter_va.c: OpenGL VA opaque converter
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors and VideoLAN
+ *
+ * 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
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "internal.h"
+#include "../../hw/va/vlc_va.h"
+#include <vlc_vout_window.h>
+
+#include <assert.h>
+
+#if defined(HAVE_VA_WL) || defined(HAVE_VA_X11)
+# define HAVE_VA_EGL
+# include <EGL/egl.h>
+# include <EGL/eglext.h>
+# include <va/va_drmcommon.h>
+
+# ifdef HAVE_VA_WL
+#  include <va/va_wayland.h>
+# endif
+
+# ifdef HAVE_VA_X11
+#  include <va/va_x11.h>
+/* TODO ugly way to get the X11 Display via EGL. */
+struct vlc_gl_sys_t
+{
+    EGLDisplay display;
+    EGLSurface surface;
+    EGLContext context;
+    Display *x11;
+};
+# endif
+
+#endif
+
+#ifdef HAVE_VA_GLX
+# include <va/va_glx.h>
+# include <GL/glxext.h>
+#endif
+
+struct priv
+{
+    VADisplay vadpy;
+    unsigned va_rt_format;
+#ifdef HAVE_VA_WL
+    PFNEGLCREATEIMAGEKHRPROC            eglCreateImageKHR;
+    PFNEGLDESTROYIMAGEKHRPROC           eglDestroyImageKHR;
+    PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
+    EGLDisplay egldpy;
+
+    video_color_space_t yuv_space;
+    unsigned fourcc;
+    picture_t *last_pic;
+    EGLint drm_fourccs[3];
+#endif
+#ifdef HAVE_VA_GLX
+    void *gl_surface;
+#endif
+};
+
+#ifdef HAVE_VA_GLX
+static int
+tc_vaglx_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
+                           const GLsizei *tex_width, const GLsizei *tex_height)
+{
+    struct priv *priv = tc->priv;
+
+    glBindTexture(tc->tex_target, textures[0]);
+    glTexImage2D(tc->tex_target, 0, tc->texs[0].internal,
+                 tex_width[0], tex_height[0], 0, tc->texs[0].format,
+                 tc->texs[0].type, NULL);
+    VAStatus status = vaCreateSurfaceGLX(priv->vadpy, tc->tex_target,
+                                         textures[0], &priv->gl_surface);
+    fprintf(stderr, "status: %X\n", status);
+    return status == VA_STATUS_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static int
+tc_vaglx_update(const opengl_tex_converter_t *tc, GLuint *textures,
+                const GLsizei *tex_width, const GLsizei *tex_height,
+                picture_t *pic, const size_t *plane_offset)
+{
+    (void) textures; (void) tex_width; (void) tex_height; (void) plane_offset;
+    struct priv *priv = tc->priv;
+
+    VAStatus status = vaCopySurfaceGLX(priv->vadpy, priv->gl_surface,
+                                       pic->p_sys->va_surface_id, 0);
+    return status == VA_STATUS_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void
+tc_vaglx_release(const opengl_tex_converter_t *tc)
+{
+    struct priv *priv = tc->priv;
+
+    if (priv->gl_surface != NULL)
+        vaDestroySurfaceGLX(priv->vadpy, priv->gl_surface);
+
+    vlc_va_Terminate(priv->vadpy);
+    free(tc->priv);
+}
+
+static GLuint
+tc_vaglx_init(opengl_tex_converter_t *tc, struct priv *priv, VADisplay *vadpy)
+{
+    if (vadpy == NULL)
+        return 0;
+    priv->vadpy = vadpy;
+    tc->pf_allocate_textures = tc_vaglx_allocate_textures;
+    tc->pf_update            = tc_vaglx_update;
+    tc->pf_release           = tc_vaglx_release;
+
+    int status = vlc_va_Initialize(VLC_OBJECT(tc->gl), priv->vadpy);
+    if (status != VA_STATUS_SUCCESS)
+        return 0;
+
+    return opengl_fragment_shader_init(tc, GL_TEXTURE_2D, VLC_CODEC_RGBA,
+                                       COLOR_SPACE_UNDEF);
+}
+
+#endif
+
+#ifdef HAVE_VA_EGL
+static void
+vaegl_pic_release(struct priv *priv, picture_t *pic)
+{
+    picture_sys_t *sys = pic->p_sys;
+
+    for (unsigned i = 0; i < sys->va_image.num_planes; ++i)
+        priv->eglDestroyImageKHR(priv->egldpy, sys->egl_images[i]);
+
+    vaReleaseBufferHandle(priv->vadpy, sys->va_image.buf);
+
+    vaDestroyImage(priv->vadpy, sys->va_image.image_id);
+
+    picture_Release(priv->last_pic);
+}
+
+static int
+vaegl_init_fourcc(const opengl_tex_converter_t *tc, struct priv *priv,
+                  unsigned va_fourcc)
+{
+    vlc_fourcc_t vlc_fourcc;
+    switch (va_fourcc)
+    {
+        case VA_FOURCC_NV12:
+            vlc_fourcc = VLC_CODEC_NV12;
+            priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
+            priv->drm_fourccs[1] = VLC_FOURCC('G', 'R', '8', '8');
+            break;
+        case VA_FOURCC_RGBA:
+            vlc_fourcc = VLC_CODEC_RGBA;
+            priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
+            break;
+        case VA_FOURCC_BGRA:
+            vlc_fourcc = VLC_CODEC_BGRA;
+            priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
+            break;
+        case VA_FOURCC_YV12:
+            vlc_fourcc = VLC_CODEC_YV12;
+            priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
+            priv->drm_fourccs[1] = VLC_FOURCC('R', '8', ' ', ' ');
+            priv->drm_fourccs[2] = VLC_FOURCC('R', '8', ' ', ' ');
+            break;
+        case VA_FOURCC_422H:
+            vlc_fourcc = VLC_CODEC_I422;
+            priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
+            priv->drm_fourccs[1] = VLC_FOURCC('R', '8', ' ', ' ');
+            priv->drm_fourccs[2] = VLC_FOURCC('R', '8', ' ', ' ');
+            break;
+        case VA_FOURCC_UYVY:
+            vlc_fourcc = VLC_CODEC_UYVY;
+            priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
+            break;
+        case VA_FOURCC_444P:
+            vlc_fourcc = VLC_CODEC_I444;
+            priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
+            priv->drm_fourccs[1] = VLC_FOURCC('R', '1', '6', ' ');
+            priv->drm_fourccs[2] = VLC_FOURCC('R', '1', '6', ' ');
+            break;
+        default: return VLC_EGENERIC;
+    }
+    return VLC_SUCCESS;
+}
+
+static int
+tc_vaegl_update(const opengl_tex_converter_t *tc, GLuint *textures,
+                const GLsizei *tex_width, const GLsizei *tex_height,
+                picture_t *pic, const size_t *plane_offset)
+{
+    (void) plane_offset;
+    struct priv *priv = tc->priv;
+    picture_sys_t *sys = pic->p_sys;
+    VAStatus status;
+    bool got_image = false, got_buffer_info = false;
+    EGLImageKHR egl_images[3] = { };
+
+    status = vaDeriveImage(priv->vadpy, pic->p_sys->va_surface_id,
+                           &sys->va_image);
+assert(status == VA_STATUS_SUCCESS); /*XXX */
+    if (status != VA_STATUS_SUCCESS)
+    {
+        /* TODO: if derive fail, do extra memcpy viavaCreateImage/vaPutImage */
+        goto error;
+    }
+    got_image = true;
+
+    if (sys->va_image.format.fourcc != priv->fourcc)
+    {
+        int ret = vaegl_init_fourcc(tc, priv, sys->va_image.format.fourcc);
+        if (ret != VLC_SUCCESS)
+            goto error;
+        priv->fourcc = sys->va_image.format.fourcc;
+    }
+
+    sys->va_buffer_info = (VABufferInfo) {
+        .mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
+    };
+    status = vaAcquireBufferHandle(priv->vadpy, sys->va_image.buf,
+                                   &sys->va_buffer_info);
+assert(status == VA_STATUS_SUCCESS); /* XXX */
+    if (status != VA_STATUS_SUCCESS)
+        goto error;
+    got_buffer_info = true;
+
+    for (unsigned i = 0; i < sys->va_image.num_planes; ++i)
+    {
+        EGLint attribs[] = {
+            EGL_WIDTH, tex_width[i],
+            EGL_HEIGHT, tex_height[i],
+            EGL_LINUX_DRM_FOURCC_EXT, priv->drm_fourccs[i],
+            EGL_DMA_BUF_PLANE0_FD_EXT, sys->va_buffer_info.handle,
+            EGL_DMA_BUF_PLANE0_OFFSET_EXT, sys->va_image.offsets[i],
+            EGL_DMA_BUF_PLANE0_PITCH_EXT, sys->va_image.pitches[i],
+            EGL_NONE
+        };
+
+        egl_images[i] = priv->eglCreateImageKHR(priv->egldpy, EGL_NO_CONTEXT,
+                                                EGL_LINUX_DMA_BUF_EXT, NULL,
+                                                attribs);
+        if (egl_images[i] == NULL)
+            goto error;
+
+        glBindTexture(tc->tex_target, textures[i]);
+
+        priv->glEGLImageTargetTexture2DOES(tc->tex_target, egl_images[i]);
+        sys->egl_images[i] = egl_images[i];
+    }
+    fprintf(stderr, "update: %lX\n", sys->va_buffer_info.handle);
+
+    if (pic != priv->last_pic)
+    {
+        if (priv->last_pic != NULL)
+            vaegl_pic_release(priv, priv->last_pic);
+        priv->last_pic = picture_Hold(pic);
+    }
+
+    return VLC_SUCCESS;
+
+error:
+    for (unsigned i = 0; i < 3; ++i)
+    {
+        if (egl_images[i] != NULL)
+        {
+            priv->eglDestroyImageKHR(priv->egldpy, egl_images[i]);
+            sys->egl_images[i] = NULL;
+        }
+    }
+    if (got_buffer_info)
+        vaReleaseBufferHandle(priv->vadpy, sys->va_image.buf);
+    if (got_image)
+        vaDestroyImage(priv->vadpy, sys->va_image.image_id);
+    return VLC_EGENERIC;
+}
+
+static void
+tc_vaegl_release(const opengl_tex_converter_t *tc)
+{
+    struct priv *priv = tc->priv;
+
+    if (priv->last_pic != NULL)
+        vaegl_pic_release(priv, priv->last_pic);
+
+    vlc_va_Terminate(priv->vadpy);
+    free(tc->priv);
+}
+
+static GLuint
+tc_vaegl_init(const video_format_t *fmt, opengl_tex_converter_t *tc,
+              struct priv *priv, VADisplay *vadpy)
+{
+#define GETPROC(x) do { \
+    if ((priv->x = vlc_gl_GetProcAddress(tc->gl, #x)) == NULL) return -1; \
+} while(0)
+
+    if (vadpy == NULL)
+        return 0;
+    priv->vadpy = vadpy;
+    priv->fourcc = 0;
+    priv->yuv_space = fmt->space;
+
+    if (!HasExtension(tc->glexts, "GL_OES_EGL_image"))
+        return 0;
+
+    void *(*func)() = vlc_gl_GetProcAddress(tc->gl, "eglGetCurrentDisplay");
+    priv->egldpy = func ? func() : NULL;
+    if (priv->egldpy == NULL)
+        return 0;
+
+    func = vlc_gl_GetProcAddress(tc->gl, "eglQueryString");
+    const char *eglexts = func ? func(priv->egldpy, EGL_EXTENSIONS) : "";
+    if (!HasExtension(eglexts, "EGL_EXT_image_dma_buf_import"))
+        return 0;
+
+    GETPROC(eglCreateImageKHR);
+    GETPROC(eglDestroyImageKHR);
+    GETPROC(glEGLImageTargetTexture2DOES);
+
+    tc->pf_update  = tc_vaegl_update;
+    tc->pf_release = tc_vaegl_release;
+
+    int status = vlc_va_Initialize(VLC_OBJECT(tc->gl), priv->vadpy);
+    if (status != VA_STATUS_SUCCESS)
+        return 0;
+
+    return opengl_fragment_shader_init(tc, GL_TEXTURE_2D, VLC_CODEC_NV12,
+                                       fmt->space);
+#undef GETPROC
+}
+
+#endif
+
+static picture_pool_t *
+tc_va_get_pool(const opengl_tex_converter_t *tc, const video_format_t *fmt,
+               unsigned requested_count)
+{
+    struct priv *priv = tc->priv;
+
+    return vlc_va_PoolAlloc(VLC_OBJECT(tc->gl), priv->vadpy, requested_count,
+                            fmt, priv->va_rt_format);
+}
+
+GLuint
+opengl_tex_converter_va_init(const video_format_t *fmt,
+                             opengl_tex_converter_t *tc)
+{
+    fprintf(stderr, "opengl_tex_converter_va_init ? %4.4s\n", (const char *)&fmt->i_chroma);
+
+    int status;
+    unsigned va_fourcc, va_rt_format;
+    if (fmt->i_chroma == VLC_CODEC_VAAPI_OPAQUE)
+        va_rt_format = VA_RT_FORMAT_YUV420;
+    else
+    {
+        status = vlc_va_VaFourcc(fmt->i_chroma, &va_fourcc, &va_rt_format);
+        if (status != VA_STATUS_SUCCESS)
+            return 0;
+    }
+
+    struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
+    if (unlikely(priv == NULL))
+        return VLC_ENOMEM;
+    priv->va_rt_format = va_rt_format;
+
+    GLuint fshader = 0;
+    switch (tc->gl->surface->type)
+    {
+        case VOUT_WINDOW_TYPE_XID:
+        {
+#ifdef HAVE_VA_GLX
+            Display *dpy = glXGetCurrentDisplay();
+            if (dpy != NULL)
+                fshader = tc_vaglx_init(tc, priv, vaGetDisplayGLX(dpy));
+            else
+#endif
+#ifdef HAVE_VA_X11
+            {
+                struct vlc_gl_sys_t *glsys = tc->gl->sys;
+                fshader = tc_vaegl_init(fmt, tc, priv, vaGetDisplay(glsys->x11));
+            }
+#endif
+            break;
+        }
+#ifdef HAVE_VA_WL
+        case VOUT_WINDOW_TYPE_WAYLAND:
+            fshader = tc_vaegl_init(fmt, tc, priv,
+                                    vaGetDisplayWl(tc->gl->surface->display.wl));
+            break;
+#endif
+        default:
+            goto error;
+    }
+    if (fshader == 0)
+        goto error;
+fprintf(stderr, "vadpy: %p\n", priv->vadpy); /* XXX */
+
+    tc->priv              = priv;
+    tc->chroma            = VLC_CODEC_VAAPI_OPAQUE;
+    tc->pf_get_pool       = tc_va_get_pool;
+
+fprintf(stderr, "opengl_tex_converter_va_init !\n"); /* XXX */
+    return fshader;
+
+error:
+    free(priv);
+    return 0;
+}
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index 6c711b5e18..ef44be93de 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -333,4 +333,10 @@ opengl_tex_converter_cvpx_init(const video_format_t *fmt,
                                opengl_tex_converter_t *tc);
 #endif
 
+#ifdef VLCGL_CONV_VA
+GLuint
+opengl_tex_converter_va_init(const video_format_t *fmt,
+                             opengl_tex_converter_t *tc);
+#endif
+
 #endif /* include-guard */
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index e343e12856..a4c174c31f 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -50,6 +50,9 @@
 
 static opengl_tex_converter_init_cb opengl_tex_converter_init_cbs[] =
 {
+#ifdef VLCGL_CONV_VA
+    opengl_tex_converter_va_init,
+#endif
     opengl_tex_converter_generic_init,
 #ifdef __ANDROID__
     opengl_tex_converter_anop_init,
-- 
2.11.0



More information about the vlc-devel mailing list