[vlc-devel] [PATCH] vout: opengl: add NV12/NV12 support

Thomas Guillem thomas at gllm.fr
Thu Dec 1 14:15:52 CET 2016


---
 modules/video_output/opengl.c | 152 +++++++++++++++++++++++++++++++++---------
 1 file changed, 121 insertions(+), 31 deletions(-)

diff --git a/modules/video_output/opengl.c b/modules/video_output/opengl.c
index 2b994bd..7ae7a92 100644
--- a/modules/video_output/opengl.c
+++ b/modules/video_output/opengl.c
@@ -104,6 +104,9 @@
 #ifndef GL_R16
 #define GL_R16 0
 #endif
+#ifndef GL_RG
+#define GL_RG 0
+#endif
 
 #define SPHERE_RADIUS 1.f
 
@@ -131,10 +134,11 @@ struct vout_display_opengl_t {
 
     video_format_t fmt;
     const vlc_chroma_description_t *chroma;
+    unsigned samples_per_pixel[PICTURE_PLANE_MAX];
 
     int        tex_target;
-    int        tex_format;
-    int        tex_internal;
+    int        tex_format[PICTURE_PLANE_MAX];
+    int        tex_internal[PICTURE_PLANE_MAX];
     int        tex_type;
 
     int        tex_width[PICTURE_PLANE_MAX];
@@ -322,7 +326,7 @@ static void BuildYUVFragmentShader(vout_display_opengl_t *vgl,
     };
 
     /* Basic linear YUV -> RGB conversion using bilinear interpolation */
-    const char *template_glsl_yuv =
+    static const char *template_glsl_yuv3 =
         "#version " GLSL_VERSION "\n"
         PRECISION
         "uniform sampler2D Texture0;"
@@ -350,14 +354,54 @@ static void BuildYUVFragmentShader(vout_display_opengl_t *vgl,
         " result = (z * Coefficient[2]) + result;"
         " gl_FragColor = result;"
         "}";
-    bool swap_uv = fmt->i_chroma == VLC_CODEC_YV12 ||
-                   fmt->i_chroma == VLC_CODEC_YV9;
+    static const char *template_glsl_yuv2 =
+        "#version " GLSL_VERSION "\n"
+        PRECISION
+        "uniform sampler2D Texture0;"
+        "uniform sampler2D Texture1;"
+        "uniform vec4      Coefficient[4];"
+        "varying vec4      TexCoord0,TexCoord1;"
+
+        "void main(void) {"
+        " vec4 x,y,z,result;"
+
+        " float val0 = texture2D(Texture0, TexCoord0.st).x;"
+        " float val1 = texture2D(Texture1, TexCoord1.st).x;"
+        /* The second sample on the second texture is either on 'g' (for GL_RG)
+         * or on 'a' (for GL_LUMINANCE_ALPHA) */
+        " float val2 = texture2D(Texture1, TexCoord1.st).%c;"
+        " x  = vec4(val0, val0, val0, 1);"
+        " %c = vec4(val1, val1, val1, 1);"
+        " %c = vec4(val2, val2, val2, 1l);"
+
+        " result = x * Coefficient[0] + Coefficient[3];"
+        " result = (y * Coefficient[1]) + result;"
+        " result = (z * Coefficient[2]) + result;"
+        " gl_FragColor = result;"
+        "}";
 
     char *code;
-    if (asprintf(&code, template_glsl_yuv,
-                 swap_uv ? 'z' : 'y',
-                 swap_uv ? 'y' : 'z') < 0)
-        return;
+    if (vgl->chroma->plane_count == 3)
+    {
+        bool swap_uv = fmt->i_chroma == VLC_CODEC_YV12 ||
+                       fmt->i_chroma == VLC_CODEC_YV9;
+        if (asprintf(&code, template_glsl_yuv3,
+                     swap_uv ? 'z' : 'y',
+                     swap_uv ? 'y' : 'z') < 0)
+            return;
+    }
+    else
+    {
+        assert(vgl->tex_format[0] == GL_RED || vgl->tex_format[0] == GL_LUMINANCE);
+        assert(vgl->tex_format[1] == GL_RG || vgl->tex_format[1] == GL_LUMINANCE_ALPHA);
+
+        bool swap_uv = fmt->i_chroma == VLC_CODEC_NV21;
+        if (asprintf(&code, template_glsl_yuv2,
+                     vgl->tex_format[1] == GL_RG ? 'g' : 'a',
+                     swap_uv ? 'z' : 'y',
+                     swap_uv ? 'y' : 'z') < 0)
+            return;
+    }
 
     for (int i = 0; i < 4; i++) {
         float correction = i < 3 ? yuv_range_correction : 1.f;
@@ -485,9 +529,11 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     const bool oglv3 = strverscmp((const char *)ogl_version, "3.0") >= 0;
     const int yuv_plane_texformat = oglv3 ? GL_RED : GL_LUMINANCE;
     const int yuv_plane_texformat_16 = oglv3 ? GL_R16 : GL_LUMINANCE16;
+    const int yuv_plane_texformat_2samples = oglv3 ? GL_RG : GL_LUMINANCE_ALPHA;
 #else
     bool supports_shaders = false;
     const int yuv_plane_texformat = GL_LUMINANCE;
+    const int yuv_plane_texformat_2samples = GL_LUMINANCE_ALPHA;
 #endif
 
 #if USE_OPENGL_ES == 2
@@ -596,10 +642,11 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->fmt.i_gmask  = 0x0000ff00;
     vgl->fmt.i_bmask  = 0x00ff0000;
 #   endif
-    vgl->tex_target   = GL_TEXTURE_2D;
-    vgl->tex_format   = GL_RGBA;
-    vgl->tex_internal = GL_RGBA;
-    vgl->tex_type     = GL_UNSIGNED_BYTE;
+    vgl->tex_target      = GL_TEXTURE_2D;
+    vgl->tex_format[0]   = GL_RGBA;
+    vgl->tex_internal[0] = GL_RGBA;
+    vgl->samples_per_pixel[0] = 1;
+    vgl->tex_type        = GL_UNSIGNED_BYTE;
     /* Use YUV if possible and needed */
     bool need_fs_yuv = false;
     bool need_fs_xyz = false;
@@ -607,16 +654,41 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     bool need_vs = fmt->projection_mode != PROJECTION_MODE_RECTANGULAR;
     float yuv_range_correction = 1.0;
 
-    if (max_texture_units >= 3 && supports_shaders && vlc_fourcc_IsYUV(fmt->i_chroma)) {
+    if (max_texture_units >= 2 && supports_shaders && vlc_fourcc_IsYUV(fmt->i_chroma)) {
         const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
         while (*list) {
             const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
-            if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
+            if (!dsc)
+                continue;
+            if ((unsigned) max_texture_units >= dsc->plane_count
+                && dsc->plane_count <= 3 && dsc->pixel_size == 1) {
+                /* Accept yuv format with 2 or 3 planes if there are enough
+                 * textures */
+
+                if (dsc->plane_count == 2)
+                {
+                    if (*list != VLC_CODEC_NV12 && *list != VLC_CODEC_NV21)
+                        continue;
+                    vgl->samples_per_pixel[0] = 1;
+                    vgl->samples_per_pixel[1] = 2;
+                    vgl->tex_format[0]   = yuv_plane_texformat;
+                    vgl->tex_internal[0] = yuv_plane_texformat;
+                    vgl->tex_format[1]   = yuv_plane_texformat_2samples;
+                    vgl->tex_internal[1] = yuv_plane_texformat_2samples;
+                }
+                else
+                {
+                    for (unsigned i = 0; i < dsc->plane_count; ++i)
+                    {
+                        vgl->tex_format[i]   = yuv_plane_texformat;
+                        vgl->tex_internal[i] = yuv_plane_texformat;
+                        vgl->samples_per_pixel[i] = 1;
+                    }
+                }
+
                 need_fs_yuv       = true;
                 vgl->fmt          = *fmt;
                 vgl->fmt.i_chroma = *list;
-                vgl->tex_format   = yuv_plane_texformat;
-                vgl->tex_internal = yuv_plane_texformat;
                 vgl->tex_type     = GL_UNSIGNED_BYTE;
                 yuv_range_correction = 1.0;
                 break;
@@ -629,9 +701,13 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
                 need_fs_yuv       = true;
                 vgl->fmt          = *fmt;
                 vgl->fmt.i_chroma = *list;
-                vgl->tex_format   = yuv_plane_texformat;
-                vgl->tex_internal = yuv_plane_texformat_16;
                 vgl->tex_type     = GL_UNSIGNED_SHORT;
+                for (unsigned i = 0; i < dsc->plane_count; ++i)
+                {
+                    vgl->tex_format[i]   = yuv_plane_texformat;
+                    vgl->tex_internal[i] = yuv_plane_texformat_16;
+                    vgl->samples_per_pixel[i] = 1;
+                }
                 yuv_range_correction = (float)((1 << 16) - 1) / ((1 << dsc->pixel_bits) - 1);
                 break;
 #endif
@@ -639,23 +715,26 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
             list++;
         }
     }
-
     if (fmt->i_chroma == VLC_CODEC_XYZ12) {
         need_fs_xyz       = true;
         vgl->fmt          = *fmt;
         vgl->fmt.i_chroma = VLC_CODEC_XYZ12;
-        vgl->tex_format   = GL_RGB;
-        vgl->tex_internal = GL_RGB;
         vgl->tex_type     = GL_UNSIGNED_SHORT;
+        vgl->tex_format[0]           = GL_RGB;
+        vgl->tex_internal[0]         = GL_RGB;
+        vgl->samples_per_pixel[0]    = 1;
     }
+
     vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
     assert(vgl->chroma != NULL);
     vgl->use_multitexture = vgl->chroma->plane_count > 1;
 
     /* Texture size */
     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
-        int w = vgl->fmt.i_visible_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
-        int h = vgl->fmt.i_visible_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
+        int w = vgl->fmt.i_visible_width * vgl->chroma->p[j].w.num
+              / vgl->chroma->p[j].w.den / vgl->samples_per_pixel[j];
+        int h = vgl->fmt.i_visible_height * vgl->chroma->p[j].h.num
+              / vgl->chroma->p[j].h.den;
         if (vgl->supports_npot) {
             vgl->tex_width[j]  = w;
             vgl->tex_height[j] = h;
@@ -937,9 +1016,9 @@ picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned
             glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
             /* Call glTexImage2D only once, and use glTexSubImage2D later */
-            glTexImage2D(vgl->tex_target, 0,
-                         vgl->tex_internal, vgl->tex_width[j], vgl->tex_height[j],
-                         0, vgl->tex_format, vgl->tex_type, NULL);
+            glTexImage2D(vgl->tex_target, 0, vgl->tex_internal[j],
+                         vgl->tex_width[j], vgl->tex_height[j], 0,
+                         vgl->tex_format[j], vgl->tex_type, NULL);
         }
     }
 
@@ -1034,8 +1113,13 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
 
         Upload(vgl, picture->format.i_visible_width, vgl->fmt.i_visible_height,
                vgl->fmt.i_width, vgl->fmt.i_height,
-               vgl->chroma->p[j].w.num, vgl->chroma->p[j].w.den, vgl->chroma->p[j].h.num, vgl->chroma->p[j].h.den,
-               picture->p[j].i_pitch, picture->p[j].i_pixel_pitch, 0, picture->p[j].p_pixels, vgl->tex_target, vgl->tex_format, vgl->tex_type);
+               vgl->chroma->p[j].w.num,
+               vgl->chroma->p[j].w.den * vgl->samples_per_pixel[j],
+               vgl->chroma->p[j].h.num, vgl->chroma->p[j].h.den,
+               picture->p[j].i_pitch,
+               picture->p[j].i_pixel_pitch * vgl->samples_per_pixel[j], 0,
+               picture->p[j].p_pixels, vgl->tex_target, vgl->tex_format[j],
+               vgl->tex_type);
     }
 
     int         last_count = vgl->region_count;
@@ -1617,6 +1701,10 @@ static void DrawWithShaders(vout_display_opengl_t *vgl,
             vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture0"), 0);
             vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture1"), 1);
             vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture2"), 2);
+        } else if (vgl->chroma->plane_count == 2) {
+            vgl->Uniform4fv(vgl->GetUniformLocation(vgl->program[0], "Coefficient"), 4, vgl->local_value);
+            vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture0"), 0);
+            vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture1"), 1);
         }
         else if (vgl->chroma->plane_count == 1) {
             vgl->Uniform1i(vgl->GetUniformLocation(vgl->program[0], "Texture0"), 0);
@@ -1747,8 +1835,10 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
         float scale_w, scale_h;
 
         if (vgl->tex_target == GL_TEXTURE_2D) {
-            scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
-            scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
+            scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den
+                    / vgl->samples_per_pixel[j] / vgl->tex_width[j];
+            scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den
+                    / vgl->tex_height[j];
 
         } else {
             scale_w = 1.0;
-- 
2.10.2



More information about the vlc-devel mailing list