[vlc-devel] [PATCH 02/18] opengl: extract importer from converter

Romain Vimont rom1v at videolabs.io
Fri Dec 20 15:48:44 CET 2019


Extract fields specific to texture importer into a separate structure.
For now, it is still embedded in opengl_tex_converter_t.
---
 modules/video_output/opengl/converter.h       | 105 +----------
 .../video_output/opengl/converter_android.c   |  62 ++++---
 modules/video_output/opengl/converter_sw.c    | 136 ++++++++-------
 modules/video_output/opengl/converter_vaapi.c |  92 +++++-----
 modules/video_output/opengl/converter_vdpau.c |  41 +++--
 .../video_output/opengl/fragment_shaders.c    |  71 ++++----
 modules/video_output/opengl/importer.h        | 150 ++++++++++++++++
 modules/video_output/opengl/vout_helper.c     | 165 ++++++++++--------
 8 files changed, 467 insertions(+), 355 deletions(-)
 create mode 100644 modules/video_output/opengl/importer.h

diff --git a/modules/video_output/opengl/converter.h b/modules/video_output/opengl/converter.h
index 211d7e07c4..aa6649c816 100644
--- a/modules/video_output/opengl/converter.h
+++ b/modules/video_output/opengl/converter.h
@@ -27,6 +27,7 @@
 #include <vlc_picture_pool.h>
 #include <vlc_plugin.h>
 #include "gl_common.h"
+#include "importer.h"
 
 struct pl_context;
 struct pl_shader;
@@ -47,9 +48,6 @@ struct opengl_tex_converter_t
     /* Pointer to object gl, set by the caller */
     vlc_gl_t *gl;
 
-    /* Pointer to decoder video context, set by the caller (can be NULL) */
-    vlc_video_context *vctx;
-
     /* libplacebo context, created by the caller (optional) */
     struct pl_context *pl_ctx;
 
@@ -64,45 +62,16 @@ struct opengl_tex_converter_t
     GLuint (*pf_fragment_shader_init)(opengl_tex_converter_t *, GLenum,
                                       vlc_fourcc_t, video_color_space_t);
 
-    /* Available gl extensions (from GL_EXTENSIONS) */
-    const char *glexts;
-
-    /* True if the current API is OpenGL ES, set by the caller */
-    bool is_gles;
     /* GLSL version, set by the caller. 100 for GLSL ES, 120 for desktop GLSL */
     unsigned glsl_version;
     /* Precision header, set by the caller. In OpenGLES, the fragment language
      * has no default precision qualifier for floating point types. */
     const char *glsl_precision_header;
 
-    /* Can only be changed from the module open function */
-    video_format_t fmt;
-
     /* Fragment shader, must be set from the module open function. It will be
      * deleted by the caller. */
     GLuint fshader;
 
-    /* Number of textures, cannot be 0 */
-    unsigned tex_count;
-
-    /* Texture mapping (usually: GL_TEXTURE_2D), cannot be 0 */
-    GLenum tex_target;
-
-    /* Set to true if textures are generated from pf_update() */
-    bool handle_texs_gen;
-
-    struct opengl_tex_cfg {
-        /* Texture scale factor, cannot be 0 */
-        vlc_rational_t w;
-        vlc_rational_t h;
-
-        /* The following is used and filled by the opengl_fragment_shader_init
-         * function. */
-        GLint  internal;
-        GLenum format;
-        GLenum type;
-    } texs[PICTURE_PLANE_MAX];
-
     /* The following is used and filled by the opengl_fragment_shader_init
      * function. */
     struct {
@@ -118,57 +87,7 @@ struct opengl_tex_converter_t
     struct pl_shader *pl_sh;
     const struct pl_shader_res *pl_sh_res;
 
-    /* Private context */
-    void *priv;
-
-    /**
-     * Callback to allocate data for bound textures
-     *
-     * This function pointer can be NULL. Software converters should call
-     * glTexImage2D() to allocate textures data (it will be deallocated by the
-     * caller when calling glDeleteTextures()). Won't be called if
-     * handle_texs_gen is true.
-     *
-     * \param tc OpenGL tex converter
-     * \param textures array of textures to bind (one per plane)
-     * \param tex_width array of tex width (one per plane)
-     * \param tex_height array of tex height (one per plane)
-     * \return VLC_SUCCESS or a VLC error
-     */
-    int (*pf_allocate_textures)(const opengl_tex_converter_t *tc, GLuint *textures,
-                                const GLsizei *tex_width, const GLsizei *tex_height);
-
-    /**
-     * Callback to allocate a picture pool
-     *
-     * This function pointer *can* be NULL. If NULL, A generic pool with
-     * pictures allocated from the video_format_t will be used.
-     *
-     * \param tc OpenGL tex converter
-     * \param requested_count number of pictures to allocate
-     * \return the picture pool or NULL in case of error
-     */
-    picture_pool_t *(*pf_get_pool)(const opengl_tex_converter_t *tc,
-                                   unsigned requested_count);
-
-    /**
-     * Callback to update a picture
-     *
-     * This function pointer cannot be NULL. The implementation should upload
-     * every planes of the picture.
-     *
-     * \param tc OpenGL tex converter
-     * \param textures array of textures to bind (one per plane)
-     * \param tex_width array of tex width (one per plane)
-     * \param tex_height array of tex height (one per plane)
-     * \param pic picture to update
-     * \param plane_offset offsets of each picture planes to read data from
-     * (one per plane, can be NULL)
-     * \return VLC_SUCCESS or a VLC error
-     */
-    int (*pf_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);
+    struct vlc_gl_importer importer;
 
     /**
      * Callback to fetch locations of uniform or attributes variables
@@ -196,26 +115,6 @@ struct opengl_tex_converter_t
     void (*pf_prepare_shader)(const opengl_tex_converter_t *tc,
                               const GLsizei *tex_width, const GLsizei *tex_height,
                               float alpha);
-
-    /**
-     * Callback to retrieve the transform matrix to apply to texture coordinates
-     *
-     * This function pointer can be NULL. If it is set, it may return NULL.
-     *
-     * Otherwise, it must return a 4x4 matrix, as an array of 16 floats in
-     * column-major order.
-     *
-     * This transform matrix maps 2D homogeneous texture coordinates of the
-     * form (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the
-     * texture coordinate that should be used to sample that location from the
-     * texture.
-     *
-     * The returned pointer is owned by the converter module, and must not be
-     * freed before the module is closed.
-     *
-     * \param tc OpenGL tex converter
-     */
-    const float *(*pf_get_transform_matrix)(const opengl_tex_converter_t *tc);
 };
 
 /**
diff --git a/modules/video_output/opengl/converter_android.c b/modules/video_output/opengl/converter_android.c
index 6468068640..1d3e2348f2 100644
--- a/modules/video_output/opengl/converter_android.c
+++ b/modules/video_output/opengl/converter_android.c
@@ -38,15 +38,15 @@ struct priv
 };
 
 static int
-tc_anop_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_anop_allocate_textures(const struct vlc_gl_importer *imp, GLuint *textures,
                           const GLsizei *tex_width, const GLsizei *tex_height)
 {
     (void) tex_width; (void) tex_height;
-    struct priv *priv = tc->priv;
+    struct priv *priv = imp->priv;
     assert(textures[0] != 0);
     if (SurfaceTexture_attachToGLContext(priv->awh, textures[0]) != 0)
     {
-        msg_Err(tc->gl, "SurfaceTexture_attachToGLContext failed");
+        msg_Err(imp->gl, "SurfaceTexture_attachToGLContext failed");
         return VLC_EGENERIC;
     }
     priv->stex_attached = true;
@@ -54,7 +54,7 @@ tc_anop_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
 }
 
 static int
-tc_anop_update(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_anop_update(const struct vlc_gl_importer *imp, GLuint *textures,
                const GLsizei *tex_width, const GLsizei *tex_height,
                picture_t *pic, const size_t *plane_offset)
 {
@@ -65,7 +65,7 @@ tc_anop_update(const opengl_tex_converter_t *tc, GLuint *textures,
     if (plane_offset != NULL)
         return VLC_EGENERIC;
 
-    struct priv *priv = tc->priv;
+    struct priv *priv = imp->priv;
 
     if (!priv->avctx->render(pic->context))
         return VLC_SUCCESS; /* already rendered */
@@ -77,16 +77,16 @@ tc_anop_update(const opengl_tex_converter_t *tc, GLuint *textures,
         return VLC_EGENERIC;
     }
 
-    tc->vt->ActiveTexture(GL_TEXTURE0);
-    tc->vt->BindTexture(tc->tex_target, textures[0]);
+    imp->vt->ActiveTexture(GL_TEXTURE0);
+    imp->vt->BindTexture(imp->tex_target, textures[0]);
 
     return VLC_SUCCESS;
 }
 
 static const float *
-tc_get_transform_matrix(const opengl_tex_converter_t *tc)
+tc_get_transform_matrix(const struct vlc_gl_importer *imp)
 {
-    struct priv *priv = tc->priv;
+    struct priv *priv = imp->priv;
     return priv->transform_mtx;
 }
 
@@ -106,60 +106,64 @@ static int
 Open(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *) obj;
+    struct vlc_gl_importer *imp = &tc->importer;
 
-    if (tc->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE
-     || !tc->gl->surface->handle.anativewindow
-     || !tc->vctx)
+    if (imp->fmt.i_chroma != VLC_CODEC_ANDROID_OPAQUE
+     || !imp->gl->surface->handle.anativewindow
+     || !imp->vctx)
         return VLC_EGENERIC;
 
     android_video_context_t *avctx =
-        vlc_video_context_GetPrivate(tc->vctx, VLC_VIDEO_CONTEXT_AWINDOW);
+        vlc_video_context_GetPrivate(imp->vctx, VLC_VIDEO_CONTEXT_AWINDOW);
 
     if (avctx->id != AWindow_SurfaceTexture)
         return VLC_EGENERIC;
 
-    tc->priv = malloc(sizeof(struct priv));
-    if (unlikely(tc->priv == NULL))
+    imp->priv = malloc(sizeof(struct priv));
+    if (unlikely(imp->priv == NULL))
         return VLC_ENOMEM;
 
-    struct priv *priv = tc->priv;
+    struct priv *priv = imp->priv;
     priv->avctx = avctx;
-    priv->awh = tc->gl->surface->handle.anativewindow;
+    priv->awh = imp->gl->surface->handle.anativewindow;
     priv->transform_mtx = NULL;
     priv->stex_attached = false;
 
-    tc->pf_allocate_textures = tc_anop_allocate_textures;
-    tc->pf_update         = tc_anop_update;
-    tc->pf_get_transform_matrix = tc_get_transform_matrix;
+    static const struct vlc_gl_importer_ops ops = {
+        .allocate_textures = tc_anop_allocate_textures,
+        .update_textures = tc_anop_update,
+        .get_transform_matrix = tc_get_transform_matrix,
+    };
+    imp->ops = &ops;
 
     /* The transform Matrix (uSTMatrix) given by the SurfaceTexture is not
      * using the same origin than us. Ask the caller to rotate textures
      * coordinates, via the vertex shader, by forcing an orientation. */
-    switch (tc->fmt.orientation)
+    switch (imp->fmt.orientation)
     {
         case ORIENT_TOP_LEFT:
-            tc->fmt.orientation = ORIENT_BOTTOM_LEFT;
+            imp->fmt.orientation = ORIENT_BOTTOM_LEFT;
             break;
         case ORIENT_TOP_RIGHT:
-            tc->fmt.orientation = ORIENT_BOTTOM_RIGHT;
+            imp->fmt.orientation = ORIENT_BOTTOM_RIGHT;
             break;
         case ORIENT_BOTTOM_LEFT:
-            tc->fmt.orientation = ORIENT_TOP_LEFT;
+            imp->fmt.orientation = ORIENT_TOP_LEFT;
             break;
         case ORIENT_BOTTOM_RIGHT:
-            tc->fmt.orientation = ORIENT_TOP_RIGHT;
+            imp->fmt.orientation = ORIENT_TOP_RIGHT;
             break;
         case ORIENT_LEFT_TOP:
-            tc->fmt.orientation = ORIENT_RIGHT_TOP;
+            imp->fmt.orientation = ORIENT_RIGHT_TOP;
             break;
         case ORIENT_LEFT_BOTTOM:
-            tc->fmt.orientation = ORIENT_RIGHT_BOTTOM;
+            imp->fmt.orientation = ORIENT_RIGHT_BOTTOM;
             break;
         case ORIENT_RIGHT_TOP:
-            tc->fmt.orientation = ORIENT_LEFT_TOP;
+            imp->fmt.orientation = ORIENT_LEFT_TOP;
             break;
         case ORIENT_RIGHT_BOTTOM:
-            tc->fmt.orientation = ORIENT_LEFT_BOTTOM;
+            imp->fmt.orientation = ORIENT_LEFT_BOTTOM;
             break;
     }
 
diff --git a/modules/video_output/opengl/converter_sw.c b/modules/video_output/opengl/converter_sw.c
index 3eb3eeb3ae..9cea29b9bf 100644
--- a/modules/video_output/opengl/converter_sw.c
+++ b/modules/video_output/opengl/converter_sw.c
@@ -72,6 +72,8 @@ pbo_picture_destroy(picture_t *pic)
 static picture_t *
 pbo_picture_create(const opengl_tex_converter_t *tc)
 {
+    const struct vlc_gl_importer *imp = &tc->importer;
+
     picture_sys_t *picsys = calloc(1, sizeof(*picsys));
     if (unlikely(picsys == NULL))
         return NULL;
@@ -80,7 +82,7 @@ pbo_picture_create(const opengl_tex_converter_t *tc)
         .p_sys = picsys,
         .pf_destroy = pbo_picture_destroy,
     };
-    picture_t *pic = picture_NewFromResource(&tc->fmt, &rsc);
+    picture_t *pic = picture_NewFromResource(&imp->fmt, &rsc);
     if (pic == NULL)
     {
         free(picsys);
@@ -91,14 +93,14 @@ pbo_picture_create(const opengl_tex_converter_t *tc)
     picsys->DeleteBuffers = tc->vt->DeleteBuffers;
 
     /* XXX: needed since picture_NewFromResource override pic planes */
-    if (picture_Setup(pic, &tc->fmt))
+    if (picture_Setup(pic, &imp->fmt))
     {
         picture_Release(pic);
         return NULL;
     }
 
     assert(pic->i_planes > 0
-        && (unsigned) pic->i_planes == tc->tex_count);
+        && (unsigned) pic->i_planes == imp->tex_count);
 
     for (int i = 0; i < pic->i_planes; ++i)
     {
@@ -141,7 +143,7 @@ pbo_data_alloc(const opengl_tex_converter_t *tc, picture_t *pic)
 static int
 pbo_pics_alloc(const opengl_tex_converter_t *tc)
 {
-    struct priv *priv = tc->priv;
+    struct priv *priv = tc->importer.priv;
     for (size_t i = 0; i < PBO_DISPLAY_COUNT; ++i)
     {
         picture_t *pic = priv->pbo.display_pics[i] = pbo_picture_create(tc);
@@ -163,12 +165,12 @@ error:
 }
 
 static int
-tc_pbo_update(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_pbo_update(const struct vlc_gl_importer *imp, GLuint *textures,
               const GLsizei *tex_width, const GLsizei *tex_height,
               picture_t *pic, const size_t *plane_offset)
 {
     (void) plane_offset; assert(plane_offset == NULL);
-    struct priv *priv = tc->priv;
+    struct priv *priv = imp->priv;
 
     picture_t *display_pic = priv->pbo.display_pics[priv->pbo.display_idx];
     picture_sys_t *p_sys = display_pic->p_sys;
@@ -178,53 +180,52 @@ tc_pbo_update(const opengl_tex_converter_t *tc, GLuint *textures,
     {
         GLsizeiptr size = pic->p[i].i_lines * pic->p[i].i_pitch;
         const GLvoid *data = pic->p[i].p_pixels;
-        tc->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER,
+        imp->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER,
                            p_sys->buffers[i]);
-        tc->vt->BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);
+        imp->vt->BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);
 
-        tc->vt->ActiveTexture(GL_TEXTURE0 + i);
-        tc->vt->BindTexture(tc->tex_target, textures[i]);
+        imp->vt->ActiveTexture(GL_TEXTURE0 + i);
+        imp->vt->BindTexture(imp->tex_target, textures[i]);
 
-        tc->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch
+        imp->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch
             * tex_width[i] / (pic->p[i].i_visible_pitch ? pic->p[i].i_visible_pitch : 1));
 
-        tc->vt->TexSubImage2D(tc->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
-                              tc->texs[i].format, tc->texs[i].type, NULL);
-
-        tc->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+        imp->vt->TexSubImage2D(imp->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
+                               imp->texs[i].format, imp->texs[i].type, NULL);
+        imp->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
     }
 
     /* turn off pbo */
-    tc->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+    imp->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
     return VLC_SUCCESS;
 }
 
 static int
-tc_common_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_common_allocate_textures(const struct vlc_gl_importer *imp, GLuint *textures,
                             const GLsizei *tex_width, const GLsizei *tex_height)
 {
-    for (unsigned i = 0; i < tc->tex_count; i++)
+    for (unsigned i = 0; i < imp->tex_count; i++)
     {
-        tc->vt->BindTexture(tc->tex_target, textures[i]);
-        tc->vt->TexImage2D(tc->tex_target, 0, tc->texs[i].internal,
-                           tex_width[i], tex_height[i], 0, tc->texs[i].format,
-                           tc->texs[i].type, NULL);
+        imp->vt->BindTexture(imp->tex_target, textures[i]);
+        imp->vt->TexImage2D(imp->tex_target, 0, imp->texs[i].internal,
+                           tex_width[i], tex_height[i], 0, imp->texs[i].format,
+                           imp->texs[i].type, NULL);
     }
     return VLC_SUCCESS;
 }
 
 static int
-upload_plane(const opengl_tex_converter_t *tc, unsigned tex_idx,
+upload_plane(const struct vlc_gl_importer *imp, unsigned tex_idx,
              GLsizei width, GLsizei height,
              unsigned pitch, unsigned visible_pitch, const void *pixels)
 {
-    struct priv *priv = tc->priv;
-    GLenum tex_format = tc->texs[tex_idx].format;
-    GLenum tex_type = tc->texs[tex_idx].type;
+    struct priv *priv = imp->priv;
+    GLenum tex_format = imp->texs[tex_idx].format;
+    GLenum tex_type = imp->texs[tex_idx].type;
 
     /* This unpack alignment is the default, but setting it just in case. */
-    tc->vt->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    imp->vt->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
     if (!priv->has_unpack_subimage)
     {
@@ -255,41 +256,41 @@ upload_plane(const opengl_tex_converter_t *tc, unsigned tex_idx,
                 source += pitch;
                 destination += visible_pitch;
             }
-            tc->vt->TexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
-                                  tex_format, tex_type, priv->texture_temp_buf);
+            imp->vt->TexSubImage2D(imp->tex_target, 0, 0, 0, width, height,
+                                   tex_format, tex_type, priv->texture_temp_buf);
         }
         else
         {
-            tc->vt->TexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
-                                  tex_format, tex_type, pixels);
+            imp->vt->TexSubImage2D(imp->tex_target, 0, 0, 0, width, height,
+                                   tex_format, tex_type, pixels);
         }
     }
     else
     {
-        tc->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pitch * width / (visible_pitch ? visible_pitch : 1));
-        tc->vt->TexSubImage2D(tc->tex_target, 0, 0, 0, width, height,
+        imp->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pitch * width / (visible_pitch ? visible_pitch : 1));
+        imp->vt->TexSubImage2D(imp->tex_target, 0, 0, 0, width, height,
                               tex_format, tex_type, pixels);
-        tc->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+        imp->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
     }
     return VLC_SUCCESS;
 }
 
 static int
-tc_common_update(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_common_update(const struct vlc_gl_importer *imp, GLuint *textures,
                  const GLsizei *tex_width, const GLsizei *tex_height,
                  picture_t *pic, const size_t *plane_offset)
 {
     int ret = VLC_SUCCESS;
-    for (unsigned i = 0; i < tc->tex_count && ret == VLC_SUCCESS; i++)
+    for (unsigned i = 0; i < imp->tex_count && ret == VLC_SUCCESS; i++)
     {
         assert(textures[i] != 0);
-        tc->vt->ActiveTexture(GL_TEXTURE0 + i);
-        tc->vt->BindTexture(tc->tex_target, textures[i]);
+        imp->vt->ActiveTexture(GL_TEXTURE0 + i);
+        imp->vt->BindTexture(imp->tex_target, textures[i]);
         const void *pixels = plane_offset != NULL ?
                              &pic->p[i].p_pixels[plane_offset[i]] :
                              pic->p[i].p_pixels;
 
-        ret = upload_plane(tc, i, tex_width[i], tex_height[i],
+        ret = upload_plane(imp, i, tex_width[i], tex_height[i],
                            pic->p[i].i_pitch, pic->p[i].i_visible_pitch, pixels);
     }
     return ret;
@@ -298,21 +299,23 @@ tc_common_update(const opengl_tex_converter_t *tc, GLuint *textures,
 int
 opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
 {
+    struct vlc_gl_importer *imp = &tc->importer;
+
     GLuint fragment_shader = 0;
     video_color_space_t space;
     const vlc_fourcc_t *list;
 
-    if (vlc_fourcc_IsYUV(tc->fmt.i_chroma))
+    if (vlc_fourcc_IsYUV(imp->fmt.i_chroma))
     {
         GLint max_texture_units = 0;
         tc->vt->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
         if (max_texture_units < 3)
             return VLC_EGENERIC;
 
-        list = vlc_fourcc_GetYUVFallback(tc->fmt.i_chroma);
-        space = tc->fmt.space;
+        list = vlc_fourcc_GetYUVFallback(imp->fmt.i_chroma);
+        space = imp->fmt.space;
     }
-    else if (tc->fmt.i_chroma == VLC_CODEC_XYZ12)
+    else if (imp->fmt.i_chroma == VLC_CODEC_XYZ12)
     {
         static const vlc_fourcc_t xyz12_list[] = { VLC_CODEC_XYZ12, 0 };
         list = xyz12_list;
@@ -320,7 +323,7 @@ opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
     }
     else
     {
-        list = vlc_fourcc_GetRGBFallback(tc->fmt.i_chroma);
+        list = vlc_fourcc_GetRGBFallback(imp->fmt.i_chroma);
         space = COLOR_SPACE_UNDEF;
     }
 
@@ -330,20 +333,20 @@ opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
             opengl_fragment_shader_init(tc, GL_TEXTURE_2D, *list, space);
         if (fragment_shader != 0)
         {
-            tc->fmt.i_chroma = *list;
+            imp->fmt.i_chroma = *list;
 
-            if (tc->fmt.i_chroma == VLC_CODEC_RGB32)
+            if (imp->fmt.i_chroma == VLC_CODEC_RGB32)
             {
 #if defined(WORDS_BIGENDIAN)
-                tc->fmt.i_rmask  = 0xff000000;
-                tc->fmt.i_gmask  = 0x00ff0000;
-                tc->fmt.i_bmask  = 0x0000ff00;
+                imp->fmt.i_rmask  = 0xff000000;
+                imp->fmt.i_gmask  = 0x00ff0000;
+                imp->fmt.i_bmask  = 0x0000ff00;
 #else
-                tc->fmt.i_rmask  = 0x000000ff;
-                tc->fmt.i_gmask  = 0x0000ff00;
-                tc->fmt.i_bmask  = 0x00ff0000;
+                imp->fmt.i_rmask  = 0x000000ff;
+                imp->fmt.i_gmask  = 0x0000ff00;
+                imp->fmt.i_bmask  = 0x00ff0000;
 #endif
-                video_format_FixRgb(&tc->fmt);
+                video_format_FixRgb(&imp->fmt);
             }
             break;
         }
@@ -352,19 +355,22 @@ opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
     if (fragment_shader == 0)
         return VLC_EGENERIC;
 
-    struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
+    struct priv *priv = imp->priv = calloc(1, sizeof(struct priv));
     if (unlikely(priv == NULL))
     {
         tc->vt->DeleteShader(fragment_shader);
         return VLC_ENOMEM;
     }
 
-    tc->pf_update            = tc_common_update;
-    tc->pf_allocate_textures = tc_common_allocate_textures;
+    static const struct vlc_gl_importer_ops ops = {
+        .allocate_textures = tc_common_allocate_textures,
+        .update_textures = tc_common_update,
+    };
+    imp->ops = &ops;
 
     /* OpenGL or OpenGL ES2 with GL_EXT_unpack_subimage ext */
     priv->has_unpack_subimage =
-        !tc->is_gles || vlc_gl_StrHasToken(tc->glexts, "GL_EXT_unpack_subimage");
+        !imp->is_gles || vlc_gl_StrHasToken(imp->glexts, "GL_EXT_unpack_subimage");
 
     if (allow_dr && priv->has_unpack_subimage)
     {
@@ -373,15 +379,19 @@ opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
         const bool glver_ok = strverscmp((const char *)ogl_version, "3.0") >= 0;
 
         const bool has_pbo = glver_ok &&
-            (vlc_gl_StrHasToken(tc->glexts, "GL_ARB_pixel_buffer_object") ||
-             vlc_gl_StrHasToken(tc->glexts, "GL_EXT_pixel_buffer_object"));
+            (vlc_gl_StrHasToken(imp->glexts, "GL_ARB_pixel_buffer_object") ||
+             vlc_gl_StrHasToken(imp->glexts, "GL_EXT_pixel_buffer_object"));
 
         const bool supports_pbo = has_pbo && tc->vt->BufferData
             && tc->vt->BufferSubData;
         if (supports_pbo && pbo_pics_alloc(tc) == VLC_SUCCESS)
         {
-            tc->pf_update  = tc_pbo_update;
-            msg_Dbg(tc->gl, "PBO support enabled");
+            static const struct vlc_gl_importer_ops pbo_ops = {
+                .allocate_textures = tc_common_allocate_textures,
+                .update_textures = tc_pbo_update,
+            };
+            imp->ops = &pbo_ops;
+            msg_Dbg(imp->gl, "PBO support enabled");
         }
     }
 
@@ -393,9 +403,9 @@ opengl_tex_converter_generic_init(opengl_tex_converter_t *tc, bool allow_dr)
 void
 opengl_tex_converter_generic_deinit(opengl_tex_converter_t *tc)
 {
-    struct priv *priv = tc->priv;
+    struct priv *priv = tc->importer.priv;
     for (size_t i = 0; i < PBO_DISPLAY_COUNT && priv->pbo.display_pics[i]; ++i)
         picture_Release(priv->pbo.display_pics[i]);
     free(priv->texture_temp_buf);
-    free(tc->priv);
+    free(priv);
 }
diff --git a/modules/video_output/opengl/converter_vaapi.c b/modules/video_output/opengl/converter_vaapi.c
index 6fb1085ec8..177b9cda41 100644
--- a/modules/video_output/opengl/converter_vaapi.c
+++ b/modules/video_output/opengl/converter_vaapi.c
@@ -62,7 +62,7 @@ struct priv
 };
 
 static EGLImageKHR
-vaegl_image_create(const opengl_tex_converter_t *tc, EGLint w, EGLint h,
+vaegl_image_create(const struct vlc_gl_importer *imp, EGLint w, EGLint h,
                    EGLint fourcc, EGLint fd, EGLint offset, EGLint pitch)
 {
     EGLint attribs[] = {
@@ -75,23 +75,23 @@ vaegl_image_create(const opengl_tex_converter_t *tc, EGLint w, EGLint h,
         EGL_NONE
     };
 
-    return tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL,
-                                      attribs);
+    return imp->gl->egl.createImageKHR(imp->gl, EGL_LINUX_DMA_BUF_EXT, NULL,
+                                       attribs);
 }
 
 static void
-vaegl_image_destroy(const opengl_tex_converter_t *tc, EGLImageKHR image)
+vaegl_image_destroy(const struct vlc_gl_importer *imp, EGLImageKHR image)
 {
-    tc->gl->egl.destroyImageKHR(tc->gl, image);
+    imp->gl->egl.destroyImageKHR(imp->gl, image);
 }
 
 static void
-vaegl_release_last_pic(const opengl_tex_converter_t *tc, struct priv *priv)
+vaegl_release_last_pic(const struct vlc_gl_importer *imp, struct priv *priv)
 {
-    vlc_object_t *o = VLC_OBJECT(tc->gl);
+    vlc_object_t *o = VLC_OBJECT(imp->gl);
 
     for (unsigned i = 0; i < priv->last.va_image.num_planes; ++i)
-        vaegl_image_destroy(tc, priv->last.egl_images[i]);
+        vaegl_image_destroy(imp, priv->last.egl_images[i]);
 
     vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, priv->last.va_image.buf);
 
@@ -149,13 +149,13 @@ vaegl_init_fourcc(const opengl_tex_converter_t *tc, struct priv *priv,
 }
 
 static int
-tc_vaegl_update(const opengl_tex_converter_t *tc, GLuint *textures,
+tc_vaegl_update(const struct vlc_gl_importer *imp, 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);
+    struct priv *priv = imp->priv;
+    vlc_object_t *o = VLC_OBJECT(imp->gl);
     VAImage va_image;
     VABufferInfo va_buffer_info;
     EGLImageKHR egl_images[3] = { };
@@ -189,21 +189,21 @@ tc_vaegl_update(const opengl_tex_converter_t *tc, GLuint *textures,
     for (unsigned i = 0; i < va_image.num_planes; ++i)
     {
         egl_images[i] =
-            vaegl_image_create(tc, tex_width[i], tex_height[i],
+            vaegl_image_create(imp, tex_width[i], tex_height[i],
                                priv->drm_fourccs[i], va_buffer_info.handle,
                                va_image.offsets[i], va_image.pitches[i]);
         if (egl_images[i] == NULL)
             goto error;
 
-        tc->vt->BindTexture(tc->tex_target, textures[i]);
+        imp->vt->BindTexture(imp->tex_target, textures[i]);
 
-        priv->glEGLImageTargetTexture2DOES(tc->tex_target, egl_images[i]);
+        priv->glEGLImageTargetTexture2DOES(imp->tex_target, egl_images[i]);
     }
 
     if (pic != priv->last.pic)
     {
         if (priv->last.pic != NULL)
-            vaegl_release_last_pic(tc, priv);
+            vaegl_release_last_pic(imp, priv);
         priv->last.pic = picture_Hold(pic);
         priv->last.va_image = va_image;
         priv->last.va_buffer_info = va_buffer_info;
@@ -220,7 +220,7 @@ error:
             vlc_vaapi_ReleaseBufferHandle(o, priv->vadpy, va_image.buf);
 
         for (unsigned i = 0; i < 3 && egl_images[i] != NULL; ++i)
-            vaegl_image_destroy(tc, egl_images[i]);
+            vaegl_image_destroy(imp, egl_images[i]);
 
         vlc_vaapi_DestroyImage(o, priv->vadpy, va_image.image_id);
     }
@@ -228,14 +228,14 @@ error:
 }
 
 static picture_pool_t *
-tc_vaegl_get_pool(const opengl_tex_converter_t *tc, unsigned requested_count)
+tc_vaegl_get_pool(const struct vlc_gl_importer *imp, unsigned requested_count)
 {
-    vlc_object_t *o = VLC_OBJECT(tc->gl);
-    struct priv *priv = tc->priv;
+    vlc_object_t *o = VLC_OBJECT(imp->gl);
+    struct priv *priv = imp->priv;
 
     picture_pool_t *pool =
-        vlc_vaapi_PoolNew(VLC_OBJECT(tc->gl), tc->vctx, priv->vadpy,
-                          requested_count, &priv->va_surface_ids, &tc->fmt);
+        vlc_vaapi_PoolNew(o, imp->vctx, priv->vadpy,
+                          requested_count, &priv->va_surface_ids, &imp->fmt);
     if (!pool)
         return NULL;
 
@@ -258,17 +258,17 @@ tc_vaegl_get_pool(const opengl_tex_converter_t *tc, unsigned requested_count)
 
     for (unsigned i = 0; i < va_image.num_planes; ++i)
     {
-        EGLint w = (va_image.width * tc->texs[i].w.num) / tc->texs[i].w.den;
-        EGLint h = (va_image.height * tc->texs[i].h.num) / tc->texs[i].h.den;
+        EGLint w = (va_image.width * imp->texs[i].w.num) / imp->texs[i].w.den;
+        EGLint h = (va_image.height * imp->texs[i].h.num) / imp->texs[i].h.den;
         EGLImageKHR egl_image =
-            vaegl_image_create(tc, w, h, priv->drm_fourccs[i], va_buffer_info.handle,
+            vaegl_image_create(imp, w, h, priv->drm_fourccs[i], va_buffer_info.handle,
                                va_image.offsets[i], va_image.pitches[i]);
         if (egl_image == NULL)
         {
             msg_Warn(o, "Can't create Image KHR: kernel too old ?");
             goto error;
         }
-        vaegl_image_destroy(tc, egl_image);
+        vaegl_image_destroy(imp, egl_image);
     }
 
     success = true;
@@ -291,12 +291,12 @@ static void
 Close(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *)obj;
-    struct priv *priv = tc->priv;
+    struct priv *priv = tc->importer.priv;
 
     if (priv->last.pic != NULL)
-        vaegl_release_last_pic(tc, priv);
+        vaegl_release_last_pic(&tc->importer, priv);
 
-    free(tc->priv);
+    free(priv);
 }
 
 static int strcasecmp_void(const void *a, const void *b)
@@ -334,14 +334,14 @@ tc_va_check_interop_blacklist(opengl_tex_converter_t *tc, VADisplay *vadpy)
 }
 
 static int
-tc_va_check_derive_image(opengl_tex_converter_t *tc)
+tc_va_check_derive_image(const struct vlc_gl_importer *imp)
 {
-    vlc_object_t *o = VLC_OBJECT(tc->gl);
-    struct priv *priv = tc->priv;
+    vlc_object_t *o = VLC_OBJECT(imp->gl);
+    struct priv *priv = imp->priv;
     VASurfaceID *va_surface_ids;
 
-    picture_pool_t *pool = vlc_vaapi_PoolNew(o, tc->vctx, priv->vadpy, 1,
-                                             &va_surface_ids, &tc->fmt);
+    picture_pool_t *pool = vlc_vaapi_PoolNew(o, imp->vctx, priv->vadpy, 1,
+                                             &va_surface_ids, &imp->fmt);
     if (!pool)
         return VLC_EGENERIC;
 
@@ -358,12 +358,13 @@ static int
 Open(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *) obj;
+    struct vlc_gl_importer *imp = &tc->importer;
 
-    if (tc->vctx == NULL)
+    if (imp->vctx == NULL)
         return VLC_EGENERIC;
-    vlc_decoder_device *dec_device = vlc_video_context_HoldDevice(tc->vctx);
+    vlc_decoder_device *dec_device = vlc_video_context_HoldDevice(imp->vctx);
     if (dec_device->type != VLC_DECODER_DEVICE_VAAPI
-     || !vlc_vaapi_IsChromaOpaque(tc->fmt.i_chroma)
+     || !vlc_vaapi_IsChromaOpaque(imp->fmt.i_chroma)
      || tc->gl->ext != VLC_GL_EXT_EGL
      || tc->gl->egl.createImageKHR == NULL
      || tc->gl->egl.destroyImageKHR == NULL)
@@ -372,7 +373,7 @@ Open(vlc_object_t *obj)
         return VLC_EGENERIC;
     }
 
-    if (!vlc_gl_StrHasToken(tc->glexts, "GL_OES_EGL_image"))
+    if (!vlc_gl_StrHasToken(imp->glexts, "GL_OES_EGL_image"))
     {
         vlc_decoder_device_Release(dec_device);
         return VLC_EGENERIC;
@@ -385,14 +386,14 @@ Open(vlc_object_t *obj)
         return VLC_EGENERIC;
     }
 
-    struct priv *priv = tc->priv = calloc(1, sizeof(struct priv));
-    if (unlikely(tc->priv == NULL))
+    struct priv *priv = imp->priv = calloc(1, sizeof(struct priv));
+    if (unlikely(priv == NULL))
         goto error;
     priv->fourcc = 0;
 
     int va_fourcc;
     int vlc_sw_chroma;
-    switch (tc->fmt.i_chroma)
+    switch (imp->fmt.i_chroma)
     {
         case VLC_CODEC_VAAPI_420:
             va_fourcc = VA_FOURCC_NV12;
@@ -420,16 +421,19 @@ Open(vlc_object_t *obj)
     if (tc_va_check_interop_blacklist(tc, priv->vadpy))
         goto error;
 
-    if (tc_va_check_derive_image(tc))
+    if (tc_va_check_derive_image(imp))
         goto error;
 
     tc->fshader = opengl_fragment_shader_init(tc, GL_TEXTURE_2D, vlc_sw_chroma,
-                                              tc->fmt.space);
+                                              imp->fmt.space);
     if (tc->fshader == 0)
         goto error;
 
-    tc->pf_update  = tc_vaegl_update;
-    tc->pf_get_pool = tc_vaegl_get_pool;
+    static const struct vlc_gl_importer_ops ops = {
+        .update_textures = tc_vaegl_update,
+        .get_pool = tc_vaegl_get_pool,
+    };
+    imp->ops = &ops;
 
     vlc_decoder_device_Release(dec_device);
 
diff --git a/modules/video_output/opengl/converter_vdpau.c b/modules/video_output/opengl/converter_vdpau.c
index e5b3794e84..6a18090c68 100644
--- a/modules/video_output/opengl/converter_vdpau.c
+++ b/modules/video_output/opengl/converter_vdpau.c
@@ -40,10 +40,10 @@
 #define INTEROP_CALL(fct, ...) \
     _##fct(__VA_ARGS__); \
     { \
-        GLenum ret = tc->vt->GetError(); \
+        GLenum ret = imp->vt->GetError(); \
         if (ret != GL_NO_ERROR) \
         { \
-            msg_Err(tc->gl, #fct " failed: 0x%x", ret); \
+            msg_Err(imp->gl, #fct " failed: 0x%x", ret); \
             return VLC_EGENERIC; \
         } \
     }
@@ -63,18 +63,18 @@ typedef struct {
 } converter_sys_t;
 
 static picture_pool_t *
-tc_vdpau_gl_get_pool(opengl_tex_converter_t const *tc,
+tc_vdpau_gl_get_pool(const struct vlc_gl_importer *imp,
                      unsigned int requested_count)
 {
-    converter_sys_t *sys = tc->priv;
+    converter_sys_t *sys = imp->priv;
     vlc_decoder_device *dec_device = sys->dec_device;
     return vlc_vdp_output_pool_create(GetVDPAUOpaqueDevice(dec_device),
                                       VDP_RGBA_FORMAT_B8G8R8A8,
-                                      &tc->fmt, requested_count);
+                                      &imp->fmt, requested_count);
 }
 
 static int
-tc_vdpau_gl_update(opengl_tex_converter_t const *tc, GLuint textures[],
+tc_vdpau_gl_update(const struct vlc_gl_importer *imp, GLuint textures[],
                    GLsizei const tex_widths[], GLsizei const tex_heights[],
                    picture_t *pic, size_t const plane_offsets[])
 {
@@ -105,7 +105,7 @@ tc_vdpau_gl_update(opengl_tex_converter_t const *tc, GLuint textures[],
     gl_nv_surface =
         INTEROP_CALL(glVDPAURegisterOutputSurfaceNV,
                      (void *)(size_t)p_sys->surface,
-                     GL_TEXTURE_2D, tc->tex_count, textures);
+                     GL_TEXTURE_2D, imp->tex_count, textures);
     INTEROP_CALL(glVDPAUSurfaceAccessNV, gl_nv_surface, GL_READ_ONLY);
     INTEROP_CALL(glVDPAUMapSurfacesNV, 1, &gl_nv_surface);
 
@@ -118,7 +118,7 @@ Close(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *)obj;
     _glVDPAUFiniNV(); assert(tc->vt->GetError() == GL_NO_ERROR);
-    converter_sys_t *sys = tc->priv;
+    converter_sys_t *sys = tc->importer.priv;
     vlc_decoder_device *dec_device = sys->dec_device;
     vlc_decoder_device_Release(dec_device);
 }
@@ -127,14 +127,16 @@ static int
 Open(vlc_object_t *obj)
 {
     opengl_tex_converter_t *tc = (void *) obj;
-    if (tc->vctx == NULL)
+    struct vlc_gl_importer *imp = &tc->importer;
+
+    if (imp->vctx == NULL)
         return VLC_EGENERIC;
-    vlc_decoder_device *dec_device = vlc_video_context_HoldDevice(tc->vctx);
+    vlc_decoder_device *dec_device = vlc_video_context_HoldDevice(imp->vctx);
     if (GetVDPAUOpaqueDevice(dec_device) == NULL
-     || (tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
-      && tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
-      && tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
-     || !vlc_gl_StrHasToken(tc->glexts, "GL_NV_vdpau_interop")
+     || (imp->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
+      && imp->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
+      && imp->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
+     || !vlc_gl_StrHasToken(imp->glexts, "GL_NV_vdpau_interop")
      || tc->gl->surface->type != VOUT_WINDOW_TYPE_XID)
     {
         vlc_decoder_device_Release(dec_device);
@@ -149,7 +151,7 @@ Open(vlc_object_t *obj)
     }
     sys->dec_device = dec_device;
 
-    tc->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
+    imp->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
 
     VdpDevice device;
     vdpau_decoder_device_t *vdpau_dev = GetVDPAUOpaqueDevice(dec_device);
@@ -194,9 +196,12 @@ Open(vlc_object_t *obj)
         return VLC_EGENERIC;
     }
 
-    tc->pf_get_pool = tc_vdpau_gl_get_pool;
-    tc->pf_update = tc_vdpau_gl_update;
-    tc->priv = sys;
+    static const struct vlc_gl_importer_ops ops = {
+        .get_pool = tc_vdpau_gl_get_pool,
+        .update_textures = tc_vdpau_gl_update,
+    };
+    imp->ops = &ops;
+    imp->priv = sys;
 
     return VLC_SUCCESS;
 }
diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c
index bd9978fda4..7bcb8f52ce 100644
--- a/modules/video_output/opengl/fragment_shaders.c
+++ b/modules/video_output/opengl/fragment_shaders.c
@@ -33,6 +33,7 @@
 
 #include <vlc_common.h>
 #include <vlc_memstream.h>
+#include "importer.h"
 #include "internal.h"
 #include "vout_helper.h"
 
@@ -102,10 +103,12 @@ tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
                  video_color_space_t yuv_space,
                  bool *swap_uv, const char *swizzle_per_tex[])
 {
+    struct vlc_gl_importer *imp = &tc->importer;
+
     GLint oneplane_texfmt, oneplane16_texfmt,
           twoplanes_texfmt, twoplanes16_texfmt;
 
-    if (vlc_gl_StrHasToken(tc->glexts, "GL_ARB_texture_rg"))
+    if (vlc_gl_StrHasToken(imp->glexts, "GL_ARB_texture_rg"))
     {
         oneplane_texfmt = GL_RED;
         oneplane16_texfmt = GL_R16;
@@ -153,10 +156,10 @@ tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
 
         assert(internal != 0 && type != 0);
 
-        tc->tex_count = 3;
-        for (unsigned i = 0; i < tc->tex_count; ++i )
+        imp->tex_count = 3;
+        for (unsigned i = 0; i < imp->tex_count; ++i )
         {
-            tc->texs[i] = (struct opengl_tex_cfg) {
+            imp->texs[i] = (struct vlc_gl_tex_cfg) {
                 { desc->p[i].w.num, desc->p[i].w.den },
                 { desc->p[i].h.num, desc->p[i].h.den },
                 internal, oneplane_texfmt, type
@@ -168,15 +171,15 @@ tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
     }
     else if (desc->plane_count == 2)
     {
-        tc->tex_count = 2;
+        imp->tex_count = 2;
 
         if (desc->pixel_size == 1)
         {
-            tc->texs[0] = (struct opengl_tex_cfg) {
+            imp->texs[0] = (struct vlc_gl_tex_cfg) {
                 { 1, 1 }, { 1, 1 }, oneplane_texfmt, oneplane_texfmt,
                 GL_UNSIGNED_BYTE
             };
-            tc->texs[1] = (struct opengl_tex_cfg) {
+            imp->texs[1] = (struct vlc_gl_tex_cfg) {
                 { 1, 2 }, { 1, 2 }, twoplanes_texfmt, twoplanes_texfmt,
                 GL_UNSIGNED_BYTE
             };
@@ -187,11 +190,11 @@ tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
              || GetTexFormatSize(tc, tex_target, twoplanes_texfmt,
                                  twoplanes16_texfmt, GL_UNSIGNED_SHORT) != 16)
                 return VLC_EGENERIC;
-            tc->texs[0] = (struct opengl_tex_cfg) {
+            imp->texs[0] = (struct vlc_gl_tex_cfg) {
                 { 1, 1 }, { 1, 1 }, oneplane16_texfmt, oneplane_texfmt,
                 GL_UNSIGNED_SHORT
             };
-            tc->texs[1] = (struct opengl_tex_cfg) {
+            imp->texs[1] = (struct vlc_gl_tex_cfg) {
                 { 1, 2 }, { 1, 2 }, twoplanes16_texfmt, twoplanes_texfmt,
                 GL_UNSIGNED_SHORT
             };
@@ -213,8 +216,8 @@ tc_yuv_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
     else if (desc->plane_count == 1)
     {
         /* Y1 U Y2 V fits in R G B A */
-        tc->tex_count = 1;
-        tc->texs[0] = (struct opengl_tex_cfg) {
+        imp->tex_count = 1;
+        imp->texs[0] = (struct vlc_gl_tex_cfg) {
             { 1, 2 }, { 1, 1 }, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
         };
 
@@ -302,12 +305,13 @@ tc_rgb_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
                  vlc_fourcc_t chroma)
 {
     (void) tex_target;
+    struct vlc_gl_importer *imp = &tc->importer;
 
     switch (chroma)
     {
         case VLC_CODEC_RGB32:
         case VLC_CODEC_RGBA:
-            tc->texs[0] = (struct opengl_tex_cfg) {
+            imp->texs[0] = (struct vlc_gl_tex_cfg) {
                 { 1, 1 }, { 1, 1 }, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
             };
             break;
@@ -315,7 +319,7 @@ tc_rgb_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
             if (GetTexFormatSize(tc, tex_target, GL_BGRA, GL_RGBA,
                                  GL_UNSIGNED_BYTE) != 32)
                 return VLC_EGENERIC;
-            tc->texs[0] = (struct opengl_tex_cfg) {
+            imp->texs[0] = (struct vlc_gl_tex_cfg) {
                 { 1, 1 }, { 1, 1 }, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE
             };
             break;
@@ -323,13 +327,15 @@ tc_rgb_base_init(opengl_tex_converter_t *tc, GLenum tex_target,
         default:
             return VLC_EGENERIC;
     }
-    tc->tex_count = 1;
+    imp->tex_count = 1;
     return VLC_SUCCESS;
 }
 
 static int
 tc_base_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
 {
+    struct vlc_gl_importer *imp = &tc->importer;
+
     if (tc->yuv_color)
     {
         tc->uloc.Coefficients = tc->vt->GetUniformLocation(program,
@@ -338,14 +344,14 @@ tc_base_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
             return VLC_EGENERIC;
     }
 
-    for (unsigned int i = 0; i < tc->tex_count; ++i)
+    for (unsigned int i = 0; i < imp->tex_count; ++i)
     {
         char name[sizeof("TextureX")];
         snprintf(name, sizeof(name), "Texture%1u", i);
         tc->uloc.Texture[i] = tc->vt->GetUniformLocation(program, name);
         if (tc->uloc.Texture[i] == -1)
             return VLC_EGENERIC;
-        if (tc->tex_target == GL_TEXTURE_RECTANGLE)
+        if (imp->tex_target == GL_TEXTURE_RECTANGLE)
         {
             snprintf(name, sizeof(name), "TexSize%1u", i);
             tc->uloc.TexSize[i] = tc->vt->GetUniformLocation(program, name);
@@ -375,18 +381,19 @@ tc_base_prepare_shader(const opengl_tex_converter_t *tc,
                        float alpha)
 {
     (void) tex_width; (void) tex_height;
+    const struct vlc_gl_importer *imp = &tc->importer;
 
     if (tc->yuv_color)
         tc->vt->Uniform4fv(tc->uloc.Coefficients, 4, tc->yuv_coefficients);
 
-    for (unsigned i = 0; i < tc->tex_count; ++i)
+    for (unsigned i = 0; i < imp->tex_count; ++i)
         tc->vt->Uniform1i(tc->uloc.Texture[i], i);
 
     tc->vt->Uniform4f(tc->uloc.FillColor, 1.0f, 1.0f, 1.0f, alpha);
 
-    if (tc->tex_target == GL_TEXTURE_RECTANGLE)
+    if (imp->tex_target == GL_TEXTURE_RECTANGLE)
     {
-        for (unsigned i = 0; i < tc->tex_count; ++i)
+        for (unsigned i = 0; i < imp->tex_count; ++i)
             tc->vt->Uniform2f(tc->uloc.TexSize[i], tex_width[i],
                                tex_height[i]);
     }
@@ -444,9 +451,11 @@ tc_xyz12_prepare_shader(const opengl_tex_converter_t *tc,
 static GLuint
 xyz12_shader_init(opengl_tex_converter_t *tc)
 {
-    tc->tex_count = 1;
-    tc->tex_target = GL_TEXTURE_2D;
-    tc->texs[0] = (struct opengl_tex_cfg) {
+    struct vlc_gl_importer *imp = &tc->importer;
+
+    imp->tex_count = 1;
+    imp->tex_target = GL_TEXTURE_2D;
+    imp->texs[0] = (struct vlc_gl_tex_cfg) {
         { 1, 1 }, { 1, 1 }, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT
     };
 
@@ -500,6 +509,8 @@ GLuint
 opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
                                  vlc_fourcc_t chroma, video_color_space_t yuv_space)
 {
+    struct vlc_gl_importer *imp = &tc->importer;
+
     const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL, };
     const bool is_yuv = vlc_fourcc_IsYUV(chroma);
     bool yuv_swap_uv = false;
@@ -557,7 +568,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
 
     ADDF("%s", tc->glsl_precision_header);
 
-    for (unsigned i = 0; i < tc->tex_count; ++i)
+    for (unsigned i = 0; i < imp->tex_count; ++i)
         ADDF("uniform %s Texture%u;\n"
              "varying vec2 TexCoord%u;\n", sampler, i, i);
 
@@ -582,7 +593,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
         dst_space.transfer = var_InheritInteger(tc->gl, "target-trc");
 
         pl_shader_color_map(sh, &color_params,
-                vlc_placebo_ColorSpace(&tc->fmt),
+                vlc_placebo_ColorSpace(&imp->fmt),
                 dst_space, NULL, false);
 
         struct pl_shader_obj *dither_state = NULL;
@@ -632,8 +643,8 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
         ADD(res->glsl);
     }
 #else
-    if (tc->fmt.transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
-        tc->fmt.primaries == COLOR_PRIMARIES_BT2020)
+    if (imp->fmt.transfer == TRANSFER_FUNC_SMPTE_ST2084 ||
+        imp->fmt.primaries == COLOR_PRIMARIES_BT2020)
     {
         // no warning for HLG because it's more or less backwards-compatible
         msg_Warn(tc->gl, "VLC needs to be built with support for libplacebo "
@@ -643,7 +654,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
 
     if (tex_target == GL_TEXTURE_RECTANGLE)
     {
-        for (unsigned i = 0; i < tc->tex_count; ++i)
+        for (unsigned i = 0; i < imp->tex_count; ++i)
             ADDF("uniform vec2 TexSize%u;\n", i);
     }
 
@@ -656,13 +667,13 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
 
     if (tex_target == GL_TEXTURE_RECTANGLE)
     {
-        for (unsigned i = 0; i < tc->tex_count; ++i)
+        for (unsigned i = 0; i < imp->tex_count; ++i)
             ADDF(" vec2 TexCoordRect%u = vec2(TexCoord%u.x * TexSize%u.x, "
                  "TexCoord%u.y * TexSize%u.y);\n", i, i, i, i, i);
     }
 
     unsigned color_idx = 0;
-    for (unsigned i = 0; i < tc->tex_count; ++i)
+    for (unsigned i = 0; i < imp->tex_count; ++i)
     {
         const char *swizzle = swizzle_per_tex[i];
         if (swizzle)
@@ -743,7 +754,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
                 (const char *)&chroma, yuv_space, ms.ptr);
     free(ms.ptr);
 
-    tc->tex_target = tex_target;
+    imp->tex_target = tex_target;
 
     tc->pf_fetch_locations = tc_base_fetch_locations;
     tc->pf_prepare_shader = tc_base_prepare_shader;
diff --git a/modules/video_output/opengl/importer.h b/modules/video_output/opengl/importer.h
new file mode 100644
index 0000000000..d59f425acc
--- /dev/null
+++ b/modules/video_output/opengl/importer.h
@@ -0,0 +1,150 @@
+/*****************************************************************************
+ * importer.h
+ *****************************************************************************
+ * Copyright (C) 2019 Videolabs
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifndef VLC_GL_IMPORTER_H
+#define VLC_GL_IMPORTER_H
+
+#include <vlc_common.h>
+#include <vlc_opengl.h>
+#include <vlc_picture.h>
+#include <vlc_picture_pool.h>
+
+#include "gl_common.h"
+
+struct vlc_gl_importer;
+
+struct vlc_gl_importer_ops {
+    /**
+     * Callback to allocate data for bound textures
+     *
+     * This function pointer can be NULL. Software converters should call
+     * glTexImage2D() to allocate textures data (it will be deallocated by the
+     * caller when calling glDeleteTextures()). Won't be called if
+     * handle_texs_gen is true.
+     *
+     * \param importer the OpenGL importer
+     * \param textures array of textures to bind (one per plane)
+     * \param tex_width array of tex width (one per plane)
+     * \param tex_height array of tex height (one per plane)
+     * \return VLC_SUCCESS or a VLC error
+     */
+    int
+    (*allocate_textures)(const struct vlc_gl_importer *importer,
+                         GLuint textures[], const GLsizei tex_width[],
+                         const GLsizei tex_height[]);
+
+    /**
+     * Callback to update a picture
+     *
+     * This function pointer cannot be NULL. The implementation should upload
+     * every planes of the picture.
+     *
+     * \param importer the OpenGL importer
+     * \param textures array of textures to bind (one per plane)
+     * \param tex_width array of tex width (one per plane)
+     * \param tex_height array of tex height (one per plane)
+     * \param pic picture to update
+     * \param plane_offset offsets of each picture planes to read data from
+     * (one per plane, can be NULL)
+     * \return VLC_SUCCESS or a VLC error
+     */
+    int
+    (*update_textures)(const struct vlc_gl_importer *importer,
+                       GLuint textures[], const GLsizei tex_width[],
+                       const GLsizei tex_height[], picture_t *pic,
+                       const size_t plane_offsets[]);
+
+    /**
+     * Callback to allocate a picture pool
+     *
+     * This function pointer *can* be NULL. If NULL, A generic pool with
+     * pictures allocated from the video_format_t will be used.
+     *
+     * \param importer the OpenGL importer
+     * \param requested_count number of pictures to allocate
+     * \return the picture pool or NULL in case of error
+     */
+     picture_pool_t *
+     (*get_pool)(const struct vlc_gl_importer *importer,
+                 unsigned requested_count);
+
+    /**
+     * Callback to retrieve the transform matrix to apply to texture coordinates
+     *
+     * This function pointer can be NULL. If it is set, it may return NULL.
+     *
+     * Otherwise, it must return a 4x4 matrix, as an array of 16 floats in
+     * column-major order.
+     *
+     * This transform matrix maps 2D homogeneous texture coordinates of the
+     * form (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the
+     * texture coordinate that should be used to sample that location from the
+     * texture.
+     *
+     * The returned pointer is owned by the converter module, and must not be
+     * freed before the module is closed.
+     *
+     * \param importer the OpenGL importer
+     * \return a 4x4 transformatoin matrix (possibly NULL)
+     */
+    const float *
+    (*get_transform_matrix)(const struct vlc_gl_importer *importer);
+};
+
+struct vlc_gl_importer {
+    vlc_gl_t *gl;
+    const opengl_vtable_t *vt;
+    GLenum tex_target;
+
+    /* True if the current API is OpenGL ES, set by the caller */
+    bool is_gles;
+
+    /* Available gl extensions (from GL_EXTENSIONS) */
+    const char *glexts;
+
+    /* Can only be changed from the module open function */
+    video_format_t fmt;
+
+    /* Pointer to decoder video context, set by the caller (can be NULL) */
+    vlc_video_context *vctx;
+
+    /* Set to true if textures are generated from pf_update() */
+    bool handle_texs_gen;
+
+    /* Initialized by the importer */
+    struct vlc_gl_tex_cfg {
+        /*
+         * Texture scale factor, cannot be 0.
+         * In 4:2:0, 1/1 for the Y texture and 1/2 for the UV texture(s)
+         */
+        vlc_rational_t w;
+        vlc_rational_t h;
+
+        GLint internal;
+        GLenum format;
+        GLenum type;
+    } texs[PICTURE_PLANE_MAX];
+    unsigned tex_count;
+
+    void *priv;
+    const struct vlc_gl_importer_ops *ops;
+};
+
+#endif
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index a8cca1131b..533b554e6c 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -346,7 +346,7 @@ static GLuint BuildVertexShader(const opengl_tex_converter_t *tc,
     tc->vt->ShaderSource(shader, 1, (const char **) &code, NULL);
     if (tc->b_dump_shaders)
         msg_Dbg(tc->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
-                (const char *)&tc->fmt.i_chroma, code);
+                (const char *)&tc->importer.fmt.i_chroma, code);
     tc->vt->CompileShader(shader);
     free(code);
     return shader;
@@ -357,31 +357,33 @@ GenTextures(const opengl_tex_converter_t *tc,
             const GLsizei *tex_width, const GLsizei *tex_height,
             GLuint *textures)
 {
-    tc->vt->GenTextures(tc->tex_count, textures);
+    const struct vlc_gl_importer *imp = &tc->importer;
 
-    for (unsigned i = 0; i < tc->tex_count; i++)
+    tc->vt->GenTextures(imp->tex_count, textures);
+
+    for (unsigned i = 0; i < imp->tex_count; i++)
     {
-        tc->vt->BindTexture(tc->tex_target, textures[i]);
+        tc->vt->BindTexture(imp->tex_target, textures[i]);
 
 #if !defined(USE_OPENGL_ES2)
         /* Set the texture parameters */
-        tc->vt->TexParameterf(tc->tex_target, GL_TEXTURE_PRIORITY, 1.0);
+        tc->vt->TexParameterf(imp->tex_target, GL_TEXTURE_PRIORITY, 1.0);
         tc->vt->TexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 #endif
 
-        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        tc->vt->TexParameteri(imp->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        tc->vt->TexParameteri(imp->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        tc->vt->TexParameteri(imp->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        tc->vt->TexParameteri(imp->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     }
 
-    if (tc->pf_allocate_textures != NULL)
+    if (imp->ops->allocate_textures != NULL)
     {
-        int ret = tc->pf_allocate_textures(tc, textures, tex_width, tex_height);
+        int ret = imp->ops->allocate_textures(&tc->importer, textures, tex_width, tex_height);
         if (ret != VLC_SUCCESS)
         {
-            tc->vt->DeleteTextures(tc->tex_count, textures);
-            memset(textures, 0, tc->tex_count * sizeof(GLuint));
+            tc->vt->DeleteTextures(imp->tex_count, textures);
+            memset(textures, 0, imp->tex_count * sizeof(GLuint));
             return ret;
         }
     }
@@ -391,16 +393,18 @@ GenTextures(const opengl_tex_converter_t *tc,
 static void
 DelTextures(const opengl_tex_converter_t *tc, GLuint *textures)
 {
-    tc->vt->DeleteTextures(tc->tex_count, textures);
-    memset(textures, 0, tc->tex_count * sizeof(GLuint));
+    const struct vlc_gl_importer *imp = &tc->importer;
+    tc->vt->DeleteTextures(imp->tex_count, textures);
+    memset(textures, 0, imp->tex_count * sizeof(GLuint));
 }
 
 static int
 opengl_link_program(struct prgm *prgm)
 {
     opengl_tex_converter_t *tc = prgm->tc;
+    struct vlc_gl_importer *imp = &tc->importer;
 
-    GLuint vertex_shader = BuildVertexShader(tc, tc->tex_count);
+    GLuint vertex_shader = BuildVertexShader(tc, imp->tex_count);
     GLuint shaders[] = { tc->fshader, vertex_shader };
 
     /* Check shaders messages */
@@ -474,11 +478,11 @@ opengl_link_program(struct prgm *prgm)
     GET_ALOC(VertexPosition, "VertexPosition");
     GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
     /* MultiTexCoord 1 and 2 can be optimized out if not used */
-    if (prgm->tc->tex_count > 1)
+    if (imp->tex_count > 1)
         GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
     else
         prgm->aloc.MultiTexCoord[1] = -1;
-    if (prgm->tc->tex_count > 2)
+    if (imp->tex_count > 2)
         GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
     else
         prgm->aloc.MultiTexCoord[2] = -1;
@@ -505,9 +509,10 @@ static void
 opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
 {
     opengl_tex_converter_t *tc = prgm->tc;
+    struct vlc_gl_importer *imp = &tc->importer;
     if (tc->p_module != NULL)
         module_unneed(tc, tc->p_module);
-    else if (tc->priv != NULL)
+    else if (imp->priv != NULL)
         opengl_tex_converter_generic_deinit(tc);
     if (prgm->id != 0)
         vgl->vt.DeleteProgram(prgm->id);
@@ -532,22 +537,28 @@ opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
     if (tc == NULL)
         return VLC_ENOMEM;
 
+    struct vlc_gl_importer *imp = &tc->importer;
+
     tc->gl = vgl->gl;
     tc->vt = &vgl->vt;
     tc->b_dump_shaders = b_dump_shaders;
     tc->pf_fragment_shader_init = opengl_fragment_shader_init_impl;
-    tc->pf_get_transform_matrix = NULL;
-    tc->glexts = glexts;
 #if defined(USE_OPENGL_ES2)
-    tc->is_gles = true;
+    imp->is_gles = true;
     tc->glsl_version = 100;
     tc->glsl_precision_header = "precision highp float;\n";
 #else
-    tc->is_gles = false;
+    imp->is_gles = false;
     tc->glsl_version = 120;
     tc->glsl_precision_header = "";
 #endif
-    tc->fmt = *fmt;
+
+    imp->ops = NULL;
+    imp->glexts = glexts;
+    imp->fmt = *fmt;
+
+    imp->gl = tc->gl;
+    imp->vt = tc->vt;
 
 #ifdef HAVE_LIBPLACEBO
     // Create the main libplacebo context
@@ -569,13 +580,13 @@ opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
     int ret;
     if (subpics)
     {
-        tc->fmt.i_chroma = VLC_CODEC_RGB32;
+        imp->fmt.i_chroma = VLC_CODEC_RGB32;
         /* Normal orientation and no projection for subtitles */
-        tc->fmt.orientation = ORIENT_NORMAL;
-        tc->fmt.projection_mode = PROJECTION_MODE_RECTANGULAR;
-        tc->fmt.primaries = COLOR_PRIMARIES_UNDEF;
-        tc->fmt.transfer = TRANSFER_FUNC_UNDEF;
-        tc->fmt.space = COLOR_SPACE_UNDEF;
+        imp->fmt.orientation = ORIENT_NORMAL;
+        imp->fmt.projection_mode = PROJECTION_MODE_RECTANGULAR;
+        imp->fmt.primaries = COLOR_PRIMARIES_UNDEF;
+        imp->fmt.transfer = TRANSFER_FUNC_UNDEF;
+        imp->fmt.space = COLOR_SPACE_UNDEF;
 
         ret = opengl_tex_converter_generic_init(tc, false);
     }
@@ -592,7 +603,7 @@ opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
         if (desc->plane_count == 0)
         {
             /* Opaque chroma: load a module to handle it */
-            tc->vctx = context;
+            imp->vctx = context;
             tc->p_module = module_need_var(tc, "glconv", "glconv");
         }
 
@@ -612,8 +623,11 @@ opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
         return VLC_EGENERIC;
     }
 
-    assert(tc->fshader != 0 && tc->tex_target != 0 && tc->tex_count > 0 &&
-           tc->pf_update != NULL && tc->pf_fetch_locations != NULL &&
+    assert(tc->fshader != 0 &&
+           imp->tex_target != 0 &&
+           imp->tex_count > 0 &&
+           imp->ops->update_textures != NULL &&
+           tc->pf_fetch_locations != NULL &&
            tc->pf_prepare_shader != NULL);
 
     prgm->tc = tc;
@@ -625,9 +639,9 @@ opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
         return VLC_EGENERIC;
     }
 
-    getOrientationTransformMatrix(tc->fmt.orientation,
+    getOrientationTransformMatrix(imp->fmt.orientation,
                                   prgm->var.OrientationMatrix);
-    getViewpointMatrixes(vgl, tc->fmt.projection_mode, prgm);
+    getViewpointMatrixes(vgl, imp->fmt.projection_mode, prgm);
 
     return VLC_SUCCESS;
 }
@@ -838,18 +852,19 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
         return NULL;
     }
     GL_ASSERT_NOERROR();
+
+    const struct vlc_gl_importer *imp = &vgl->prgm->tc->importer;
     /* Update the fmt to main program one */
-    vgl->fmt = vgl->prgm->tc->fmt;
+    vgl->fmt = imp->fmt;
     /* The orientation is handled by the orientation matrix */
     vgl->fmt.orientation = fmt->orientation;
 
     /* Texture size */
-    const opengl_tex_converter_t *tc = vgl->prgm->tc;
-    for (unsigned j = 0; j < tc->tex_count; j++) {
-        const GLsizei w = vgl->fmt.i_visible_width  * tc->texs[j].w.num
-                        / tc->texs[j].w.den;
-        const GLsizei h = vgl->fmt.i_visible_height * tc->texs[j].h.num
-                        / tc->texs[j].h.den;
+    for (unsigned j = 0; j < imp->tex_count; j++) {
+        const GLsizei w = vgl->fmt.i_visible_width  * imp->texs[j].w.num
+                        / imp->texs[j].w.den;
+        const GLsizei h = vgl->fmt.i_visible_height * imp->texs[j].h.num
+                        / imp->texs[j].h.den;
         if (vgl->supports_npot) {
             vgl->tex_width[j]  = w;
             vgl->tex_height[j] = h;
@@ -860,9 +875,9 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     }
 
     /* Allocates our textures */
-    assert(!vgl->sub_prgm->tc->handle_texs_gen);
+    assert(!vgl->sub_prgm->tc->importer.handle_texs_gen);
 
-    if (!vgl->prgm->tc->handle_texs_gen)
+    if (!imp->handle_texs_gen)
     {
         ret = GenTextures(vgl->prgm->tc, vgl->tex_width, vgl->tex_height,
                           vgl->texture);
@@ -883,7 +898,7 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
 
     vgl->vt.GenBuffers(1, &vgl->vertex_buffer_object);
     vgl->vt.GenBuffers(1, &vgl->index_buffer_object);
-    vgl->vt.GenBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
+    vgl->vt.GenBuffers(imp->tex_count, vgl->texture_buffer_object);
 
     /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
     int subpicture_buffer_object_count = 8;
@@ -924,8 +939,9 @@ void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
     vgl->vt.Finish();
     vgl->vt.Flush();
 
-    const size_t main_tex_count = vgl->prgm->tc->tex_count;
-    const bool main_del_texs = !vgl->prgm->tc->handle_texs_gen;
+    const struct vlc_gl_importer *imp = &vgl->prgm->tc->importer;
+    const size_t main_tex_count = imp->tex_count;
+    const bool main_del_texs = !imp->handle_texs_gen;
 
     if (vgl->pool)
         picture_pool_Release(vgl->pool);
@@ -1031,7 +1047,8 @@ void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
 bool vout_display_opengl_HasPool(const vout_display_opengl_t *vgl)
 {
     opengl_tex_converter_t *tc = vgl->prgm->tc;
-    return tc->pf_get_pool != NULL;
+    struct vlc_gl_importer *imp = &tc->importer;
+    return imp->ops->get_pool != NULL;
 }
 
 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
@@ -1042,10 +1059,11 @@ picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned
         return vgl->pool;
 
     opengl_tex_converter_t *tc = vgl->prgm->tc;
+    const struct vlc_gl_importer *imp = &tc->importer;
     requested_count = __MIN(VLCGL_PICTURE_MAX, requested_count);
     /* Allocate with tex converter pool callback if it exists */
-    assert(tc->pf_get_pool != NULL);
-    vgl->pool = tc->pf_get_pool(tc, requested_count);
+    assert(imp->ops->get_pool != NULL);
+    vgl->pool = imp->ops->get_pool(imp, requested_count);
     if (!vgl->pool)
         goto error;
     return vgl->pool;
@@ -1061,10 +1079,11 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
     GL_ASSERT_NOERROR();
 
     opengl_tex_converter_t *tc = vgl->prgm->tc;
+    struct vlc_gl_importer *imp = &tc->importer;
 
     /* Update the texture */
-    int ret = tc->pf_update(tc, vgl->texture, vgl->tex_width, vgl->tex_height,
-                            picture, NULL);
+    int ret = imp->ops->update_textures(imp, vgl->texture, vgl->tex_width, vgl->tex_height,
+                                        picture, NULL);
     if (ret != VLC_SUCCESS)
         return ret;
 
@@ -1075,6 +1094,7 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
     vgl->region       = NULL;
 
     tc = vgl->sub_prgm->tc;
+    imp = &tc->importer;
     if (subpicture) {
 
         int count = 0;
@@ -1132,8 +1152,9 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
             /* Use the visible pitch of the region */
             r->p_picture->p[0].i_visible_pitch = r->fmt.i_visible_width
                                                * r->p_picture->p[0].i_pixel_pitch;
-            ret = tc->pf_update(tc, &glr->texture, &glr->width, &glr->height,
-                                r->p_picture, &pixels_offset);
+            ret = imp->ops->update_textures(imp, &glr->texture,
+                                            &glr->width, &glr->height,
+                                            r->p_picture, &pixels_offset);
         }
     }
     for (int i = 0; i < last_count; i++) {
@@ -1420,6 +1441,8 @@ static int SetupCoords(vout_display_opengl_t *vgl,
                        const float *left, const float *top,
                        const float *right, const float *bottom)
 {
+    const struct vlc_gl_importer *imp = &vgl->prgm->tc->importer;
+
     GLfloat *vertexCoord, *textureCoord;
     GLushort *indices;
     unsigned nbVertices, nbIndices;
@@ -1428,19 +1451,19 @@ static int SetupCoords(vout_display_opengl_t *vgl,
     switch (vgl->fmt.projection_mode)
     {
     case PROJECTION_MODE_RECTANGULAR:
-        i_ret = BuildRectangle(vgl->prgm->tc->tex_count,
+        i_ret = BuildRectangle(imp->tex_count,
                                &vertexCoord, &textureCoord, &nbVertices,
                                &indices, &nbIndices,
                                left, top, right, bottom);
         break;
     case PROJECTION_MODE_EQUIRECTANGULAR:
-        i_ret = BuildSphere(vgl->prgm->tc->tex_count,
+        i_ret = BuildSphere(imp->tex_count,
                             &vertexCoord, &textureCoord, &nbVertices,
                             &indices, &nbIndices,
                             left, top, right, bottom);
         break;
     case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
-        i_ret = BuildCube(vgl->prgm->tc->tex_count,
+        i_ret = BuildCube(imp->tex_count,
                           (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_width,
                           (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_height,
                           &vertexCoord, &textureCoord, &nbVertices,
@@ -1455,7 +1478,7 @@ static int SetupCoords(vout_display_opengl_t *vgl,
     if (i_ret != VLC_SUCCESS)
         return i_ret;
 
-    for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++)
+    for (unsigned j = 0; j < imp->tex_count; j++)
     {
         vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
         vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
@@ -1482,12 +1505,13 @@ static int SetupCoords(vout_display_opengl_t *vgl,
 static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
 {
     opengl_tex_converter_t *tc = prgm->tc;
+    const struct vlc_gl_importer *imp = &tc->importer;
     tc->pf_prepare_shader(tc, vgl->tex_width, vgl->tex_height, 1.0f);
 
-    for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++) {
+    for (unsigned j = 0; j < imp->tex_count; j++) {
         assert(vgl->texture[j] != 0);
         vgl->vt.ActiveTexture(GL_TEXTURE0+j);
-        vgl->vt.BindTexture(tc->tex_target, vgl->texture[j]);
+        vgl->vt.BindTexture(imp->tex_target, vgl->texture[j]);
 
         vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
 
@@ -1502,8 +1526,9 @@ static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
     vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
     vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
 
-    const GLfloat *tm = tc->pf_get_transform_matrix
-                      ? tc->pf_get_transform_matrix(tc) : NULL;
+    const GLfloat *tm = NULL;
+    if (imp->ops && imp->ops->get_transform_matrix)
+        tm = imp->ops->get_transform_matrix(imp);
     if (!tm)
         tm = identity;
 
@@ -1544,6 +1569,8 @@ static void TextureCropForStereo(vout_display_opengl_t *vgl,
                                  float *left, float *top,
                                  float *right, float *bottom)
 {
+    const struct vlc_gl_importer *imp = &vgl->prgm->tc->importer;
+
     float stereoCoefs[2];
     float stereoOffsets[2];
 
@@ -1553,7 +1580,7 @@ static void TextureCropForStereo(vout_display_opengl_t *vgl,
         // Display only the left eye.
         stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
         stereoOffsets[0] = 0; stereoOffsets[1] = 0;
-        GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
+        GetTextureCropParamsForStereo(imp->tex_count,
                                       stereoCoefs, stereoOffsets,
                                       left, top, right, bottom);
         break;
@@ -1561,7 +1588,7 @@ static void TextureCropForStereo(vout_display_opengl_t *vgl,
         // Display only the left eye.
         stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
         stereoOffsets[0] = 0; stereoOffsets[1] = 0;
-        GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
+        GetTextureCropParamsForStereo(imp->tex_count,
                                       stereoCoefs, stereoOffsets,
                                       left, top, right, bottom);
         break;
@@ -1592,11 +1619,12 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
         float right[PICTURE_PLANE_MAX];
         float bottom[PICTURE_PLANE_MAX];
         const opengl_tex_converter_t *tc = vgl->prgm->tc;
-        for (unsigned j = 0; j < tc->tex_count; j++)
+        const struct vlc_gl_importer *imp = &tc->importer;
+        for (unsigned j = 0; j < tc->importer.tex_count; j++)
         {
-            float scale_w = (float)tc->texs[j].w.num / tc->texs[j].w.den
+            float scale_w = (float)imp->texs[j].w.num / imp->texs[j].w.den
                           / vgl->tex_width[j];
-            float scale_h = (float)tc->texs[j].h.num / tc->texs[j].h.den
+            float scale_h = (float)imp->texs[j].h.num / imp->texs[j].h.den
                           / vgl->tex_height[j];
 
             /* Warning: if NPOT is not supported a larger texture is
@@ -1633,6 +1661,7 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
     struct prgm *prgm = vgl->sub_prgm;
     GLuint program = prgm->id;
     opengl_tex_converter_t *tc = prgm->tc;
+    const struct vlc_gl_importer *imp = &tc->importer;
     vgl->vt.UseProgram(program);
 
     vgl->vt.Enable(GL_BLEND);
@@ -1672,7 +1701,7 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
         };
 
         assert(glr->texture != 0);
-        vgl->vt.BindTexture(tc->tex_target, glr->texture);
+        vgl->vt.BindTexture(imp->tex_target, glr->texture);
 
         tc->pf_prepare_shader(tc, &glr->width, &glr->height, glr->alpha);
 
-- 
2.24.1



More information about the vlc-devel mailing list