[vlc-commits] opengl: add converter_vaapi
Thomas Guillem
git at videolan.org
Fri Jun 16 16:48:45 CEST 2017
vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Fri Feb 10 11:10:23 2017 +0100| [97849bc67d3e285b0d7d164ad25d9b6ec68f2656] | committer: Thomas Guillem
opengl: add converter_vaapi
This converter allow to render a va_surface directly to a GL surface via EGL
(X11/WAYLAND and EXT_image_dma_buf_import[1]).
[1]: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import.txt.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=97849bc67d3e285b0d7d164ad25d9b6ec68f2656
---
modules/video_output/Makefile.am | 18 ++
modules/video_output/opengl/converter_vaapi.c | 343 ++++++++++++++++++++++++++
modules/video_output/opengl/internal.h | 6 +
modules/video_output/opengl/vout_helper.c | 3 +
4 files changed, 370 insertions(+)
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 121797c64f..973a8cd212 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -22,6 +22,24 @@ if HAVE_IOS
OPENGL_COMMONSOURCES += video_output/opengl/converter_cvpx.c
OPENGL_COMMONCFLAGS += -DVLCGL_CONV_CVPX
endif
+if HAVE_EGL
+if HAVE_VAAPI
+OPENGL_COMMONLIBS += $(LIBVA_LIBS) $(LIBVA_EGL_LIBS) libvlc_vaapi_instance.la
+OPENGL_COMMONSOURCES += video_output/opengl/converter_vaapi.c \
+ hw/vaapi/vlc_vaapi.c hw/vaapi/vlc_vaapi.h
+OPENGL_COMMONCFLAGS += -DVLCGL_CONV_VA
+if HAVE_WAYLAND_EGL
+if HAVE_VAAPI_WL
+OPENGL_COMMONLIBS += $(LIBVA_WL_LIBS)
+OPENGL_COMMONCFLAGS += -DHAVE_VA_WL
+endif
+endif
+if HAVE_VAAPI_X11
+OPENGL_COMMONLIBS += $(LIBVA_X11_LIBS)
+OPENGL_COMMONCFLAGS += -DHAVE_VA_X11
+endif
+endif
+endif
if HAVE_DECKLINK
libdecklinkoutput_plugin_la_SOURCES = video_output/decklink.cpp
diff --git a/modules/video_output/opengl/converter_vaapi.c b/modules/video_output/opengl/converter_vaapi.c
new file mode 100644
index 0000000000..7c9f9947b6
--- /dev/null
+++ b/modules/video_output/opengl/converter_vaapi.c
@@ -0,0 +1,343 @@
+/*****************************************************************************
+ * converter_vaapi.c: OpenGL VAAPI 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/vaapi/vlc_vaapi.h"
+#include <vlc_vout_window.h>
+
+#include <assert.h>
+
+#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
+
+struct priv
+{
+ VADisplay vadpy;
+ VASurfaceID *va_surface_ids;
+ PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
+ PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
+ EGLDisplay egldpy;
+
+ video_color_space_t yuv_space;
+ unsigned fourcc;
+ EGLint drm_fourccs[3];
+
+ struct {
+ picture_t * pic;
+ VAImage va_image;
+ VABufferInfo va_buffer_info;
+ void * egl_images[3];
+ } last;
+};
+
+static void
+vaegl_release_last_pic(vlc_object_t *o, struct priv *priv)
+{
+ for (unsigned i = 0; i < priv->last.va_image.num_planes; ++i)
+ priv->eglDestroyImageKHR(priv->egldpy, priv->last.egl_images[i]);
+
+ vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, priv->last.va_image.buf);
+
+ vlc_vaapi_DestroyImage(o, priv->vadpy, priv->last.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)
+{
+ (void) tc;
+ switch (va_fourcc)
+ {
+ case VA_FOURCC_NV12:
+ priv->drm_fourccs[0] = VLC_FOURCC('R', '8', ' ', ' ');
+ priv->drm_fourccs[1] = VLC_FOURCC('G', 'R', '8', '8');
+ break;
+#if 0
+ /* TODO: the following fourcc are not handled for now */
+ case VA_FOURCC_RGBA:
+ priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
+ break;
+ case VA_FOURCC_BGRA:
+ priv->drm_fourccs[0] = VLC_FOURCC('G', 'R', '3', '2');
+ break;
+ case VA_FOURCC_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:
+ 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:
+ priv->drm_fourccs[0] = VLC_FOURCC('R', '1', '6', ' ');
+ break;
+ case VA_FOURCC_444P:
+ 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;
+#endif
+ default: return VLC_EGENERIC;
+ }
+ priv->fourcc = va_fourcc;
+ 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;
+ vlc_object_t *o = VLC_OBJECT(tc->gl);
+ VAImage va_image;
+ VABufferInfo va_buffer_info;
+ EGLImageKHR egl_images[3] = { };
+ bool release_image = false, release_buffer_info = false;
+
+ if (pic == priv->last.pic)
+ {
+ va_image = priv->last.va_image;
+ va_buffer_info = priv->last.va_buffer_info;
+ for (unsigned i = 0; i < priv->last.va_image.num_planes; ++i)
+ egl_images[i] = priv->last.egl_images[i];
+ }
+ else
+ {
+ if (vlc_vaapi_DeriveImage(o, priv->vadpy, vlc_vaapi_PicGetSurface(pic),
+ &va_image))
+ goto error;
+ release_image = true;
+
+ if (va_image.format.fourcc != priv->fourcc)
+ {
+ msg_Err(tc->gl, "fourcc changed");
+ /* TODO: fetch new fourcc and reconfigure shader */
+ goto error;
+ }
+
+ va_buffer_info = (VABufferInfo) {
+ .mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME
+ };
+ if (vlc_vaapi_AcquireBufferHandle(o, priv->vadpy, va_image.buf,
+ &va_buffer_info))
+ goto error;
+ release_buffer_info = true;
+ }
+
+ for (unsigned i = 0; i < 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, va_buffer_info.handle,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image.offsets[i],
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, 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]);
+ }
+
+ if (pic != priv->last.pic)
+ {
+ if (priv->last.pic != NULL)
+ vaegl_release_last_pic(o, priv);
+ priv->last.pic = picture_Hold(pic);
+ priv->last.va_image = va_image;
+ priv->last.va_buffer_info = va_buffer_info;
+ for (unsigned i = 0; i < va_image.num_planes; ++i)
+ priv->last.egl_images[i] = egl_images[i];
+ }
+
+ return VLC_SUCCESS;
+
+error:
+ if (release_image)
+ {
+ if (release_buffer_info)
+ vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, va_image.buf);
+
+ for (unsigned i = 0; i < 3 && egl_images[i] != NULL; ++i)
+ priv->eglDestroyImageKHR(priv->egldpy, egl_images[i]);
+
+ vlc_vaapi_DestroyImage(o, priv->vadpy, 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_release_last_pic(VLC_OBJECT(tc->gl), priv);
+
+ vlc_vaapi_ReleaseInstance(priv->vadpy);
+
+ free(tc->priv);
+}
+
+static GLuint
+tc_vaegl_init(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;
+
+ if (vaegl_init_fourcc(tc, priv, VA_FOURCC_NV12))
+ return 0;
+
+ GETPROC(eglCreateImageKHR);
+ GETPROC(eglDestroyImageKHR);
+ GETPROC(glEGLImageTargetTexture2DOES);
+
+ tc->pf_update = tc_vaegl_update;
+ tc->pf_release = tc_vaegl_release;
+
+ if (vlc_vaapi_Initialize(VLC_OBJECT(tc->gl), priv->vadpy))
+ return 0;
+
+ if (vlc_vaapi_SetInstance(priv->vadpy))
+ {
+ msg_Err(tc->gl, "VAAPI instance already in use");
+ return 0;
+ }
+
+ return opengl_fragment_shader_init(tc, GL_TEXTURE_2D, VLC_CODEC_NV12,
+ fmt->space);
+#undef GETPROC
+}
+
+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;
+
+ picture_pool_t *pool =
+ vlc_vaapi_PoolNew(VLC_OBJECT(tc->gl), priv->vadpy, requested_count,
+ &priv->va_surface_ids, fmt, VA_RT_FORMAT_YUV420,
+ VA_FOURCC_NV12);
+ if (!pool)
+ return NULL;
+
+ return pool;
+}
+
+GLuint
+opengl_tex_converter_vaapi_init(video_format_t *fmt, opengl_tex_converter_t *tc)
+{
+ if (fmt->i_chroma != VLC_CODEC_VAAPI_420)
+ return 0;
+
+ struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
+ if (unlikely(priv == NULL))
+ return VLC_ENOMEM;
+
+ GLuint fshader = 0;
+ switch (tc->gl->surface->type)
+ {
+#ifdef HAVE_VA_X11
+ case VOUT_WINDOW_TYPE_XID:
+ {
+ struct vlc_gl_sys_t *glsys = tc->gl->sys;
+ fshader = tc_vaegl_init(fmt, tc, priv, vaGetDisplay(glsys->x11));
+ break;
+ }
+#endif
+#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;
+
+ tc->priv = priv;
+ tc->pf_get_pool = tc_va_get_pool;
+
+ return fshader;
+
+error:
+ free(priv);
+ return 0;
+}
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index bd990c2d5d..9f0f75d931 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -343,4 +343,10 @@ opengl_tex_converter_cvpx_init(video_format_t *fmt,
opengl_tex_converter_t *tc);
#endif
+#ifdef VLCGL_CONV_VA
+GLuint
+opengl_tex_converter_vaapi_init(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 83f6a714e9..e5b0e8b5fc 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_vaapi_init,
+#endif
#ifdef __ANDROID__
opengl_tex_converter_anop_init,
#endif
More information about the vlc-commits
mailing list