[MPlayer-dev-eng] [PATCH] yuv support for vo_gl

Reimar Döffinger Reimar.Doeffinger at stud.uni-karlsruhe.de
Fri Aug 19 22:29:17 CEST 2005


Hi,
here is a patch (against latest CVS) I'd like you to test. I does RGB->YUV
conversion via register combiners.
Two disadvantages: 1) It is not completely correct (the image is
brighter than with software scaler)
2) It only works for nVidia cards (I think) and crashes with everything
else.
For me it was slower than software conversion when using -dr, probably
because the support for luminance textures is lousy.
Please report your experiences.

Greetings,
Reimar Döffinger
-------------- next part --------------
Index: libvo/gl_common.c
===================================================================
RCS file: /cvsroot/mplayer/main/libvo/gl_common.c,v
retrieving revision 1.16
diff -u -r1.16 gl_common.c
--- libvo/gl_common.c	19 Aug 2005 12:09:44 -0000	1.16
+++ libvo/gl_common.c	19 Aug 2005 18:59:19 -0000
@@ -365,6 +365,62 @@
 }
 
 /**
+ * \brief Setup register combiners for YUV to RGB conversion.
+ */
+void glSetupYUVCombiners() {
+  GLfloat ucoef[] = { 0, -0.344, 1.770, 0 };
+  GLfloat vcoef[] = { 1.403, -0.714, 0, 0 };
+  int i;
+  glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &i);
+  if (i < 2)
+    mp_msg(MSGT_VO, MSGL_WARN,
+           "[gl] 2 general combiners needed for YUV support (found %i)\n", i);
+  glGetIntegerv (GL_MAX_TEXTURE_UNITS, &i);
+  if (i < 3)
+    mp_msg(MSGT_VO, MSGL_FATAL,
+           "[gl] 3 texture units needed for YUV support (found %i)\n", i);
+  // coefficient (probably) must be in [0,1] range, so here comes the trick
+  // first put them in [-0.5, 0.5] range, then add 0.5
+  // can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments
+  // for CombinerInput and CombinerOutput
+  for (i = 0; i < 4; i++) {
+    ucoef[i] = ucoef[i] * 0.25 + 0.5;
+    vcoef[i] = vcoef[i] * 0.25 + 0.5;
+  }
+  CombinerParameterfv(GL_CONSTANT_COLOR0_NV, ucoef);
+  CombinerParameterfv(GL_CONSTANT_COLOR1_NV, vcoef);
+
+  // UV first, like this green component can't overflow
+  CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
+                GL_TEXTURE1, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+  CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
+                GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+  CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
+                GL_TEXTURE2, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+  CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
+                GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
+  CombinerOutput(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
+                 GL_SPARE0_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE,
+                 GL_FALSE, GL_FALSE);
+
+  // stage 2
+  CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV,
+                GL_SIGNED_IDENTITY_NV, GL_RGB);
+  CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO,
+                 GL_UNSIGNED_INVERT_NV, GL_RGB);
+  CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
+                GL_TEXTURE0, GL_SIGNED_IDENTITY_NV, GL_RGB);
+  CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO,
+                GL_UNSIGNED_INVERT_NV, GL_RGB);
+  CombinerOutput(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
+                 GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE,
+                 GL_FALSE, GL_FALSE);
+
+  // leave final combiner stage in default mode
+  CombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV, 2);
+}
+
+/**
  * \brief draw a texture part at given 2D coordinates
  * \param x screen top coordinate
  * \param y screen left coordinate
@@ -380,18 +436,36 @@
  */
 void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h,
                GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
-               int sx, int sy, int rect_tex) {
+               int sx, int sy, int rect_tex, int is_yv12) {
+  GLfloat tx2 = tx / 2, ty2 = ty / 2, tw2 = tw / 2, th2 = th / 2;
   if (!rect_tex) {
     tx /= sx; ty /= sy; tw /= sx; th /= sy;
+    tx2 = tx, ty2 = ty, tw2 = tw, th2 = th;
   }
   glBegin(GL_QUADS);
   glTexCoord2f(tx, ty);
+  if (is_yv12) {
+    MultiTexCoord2f(GL_TEXTURE1, tx2, ty2);
+    MultiTexCoord2f(GL_TEXTURE2, tx2, ty2);
+  }
   glVertex2f(x, y);
   glTexCoord2f(tx, ty + th);
+  if (is_yv12) {
+    MultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2);
+    MultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2);
+  }
   glVertex2f(x, y + h);
   glTexCoord2f(tx + tw, ty + th);
+  if (is_yv12) {
+    MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2);
+    MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2);
+  }
   glVertex2f(x + w, y + h);
   glTexCoord2f(tx + tw, ty);
+  if (is_yv12) {
+    MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2);
+    MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2);
+  }
   glVertex2f(x + w, y);
   glEnd();
 }
Index: libvo/gl_common.h
===================================================================
RCS file: /cvsroot/mplayer/main/libvo/gl_common.h,v
retrieving revision 1.13
diff -u -r1.13 gl_common.h
--- libvo/gl_common.h	19 Aug 2005 12:09:44 -0000	1.13
+++ libvo/gl_common.h	19 Aug 2005 18:59:19 -0000
@@ -17,6 +17,66 @@
 #include "x11_common.h"
 #endif
 
+#ifndef GL_MAX_GENERAL_COMBINERS_NV
+#define GL_MAX_GENERAL_COMBINERS_NV 0x854D
+#endif
+#ifndef GL_NUM_GENERAL_COMBINERS_NV
+#define GL_NUM_GENERAL_COMBINERS_NV 0x854E
+#endif
+#ifndef GL_CONSTANT_COLOR0_NV
+#define GL_CONSTANT_COLOR0_NV 0x852A
+#endif
+#ifndef GL_CONSTANT_COLOR1_NV
+#define GL_CONSTANT_COLOR1_NV 0x852B
+#endif
+#ifndef GL_COMBINER0_NV
+#define GL_COMBINER0_NV 0x8550
+#endif
+#ifndef GL_COMBINER1_NV
+#define GL_COMBINER1_NV 0x8551
+#endif
+#ifndef GL_VARIABLE_A_NV
+#define GL_VARIABLE_A_NV 0x8523
+#endif
+#ifndef GL_VARIABLE_B_NV
+#define GL_VARIABLE_B_NV 0x8524
+#endif
+#ifndef GL_VARIABLE_C_NV
+#define GL_VARIABLE_C_NV 0x8525
+#endif
+#ifndef GL_VARIABLE_D_NV
+#define GL_VARIABLE_D_NV 0x8526
+#endif
+#ifndef GL_UNSIGNED_INVERT_NV
+#define GL_UNSIGNED_INVERT_NV 0x8537
+#endif
+#ifndef GL_HALF_BIAS_NORMAL_NV
+#define GL_HALF_BIAS_NORMAL_NV 0x853A
+#endif
+#ifndef GL_SIGNED_IDENTITY_NV
+#define GL_SIGNED_IDENTITY_NV 0x853C
+#endif
+#ifndef GL_SCALE_BY_FOUR_NV
+#define GL_SCALE_BY_FOUR_NV 0x853F
+#endif
+#ifndef GL_DISCARD_NV
+#define GL_DISCARD_NV 0x8530
+#endif
+#ifndef GL_SPARE0_NV
+#define GL_SPARE0_NV 0x852E
+#endif
+#ifndef GL_MAX_TEXTURE_UNITS
+#define GL_MAX_TEXTURE_UNITS 0x84E2
+#endif
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+#ifndef GL_TEXTURE2
+#define GL_TEXTURE2 0x84C2
+#endif
 #ifndef GL_TEXTURE_RECTANGLE
 #define GL_TEXTURE_RECTANGLE 0x84F5
 #endif
@@ -64,6 +124,7 @@
 int glFindFormat(uint32_t format, uint32_t *bpp, GLenum *gl_texfmt,
                   GLenum *gl_format, GLenum *gl_type);
 int glFmt2bpp(GLenum format, GLenum type);
+void glSetupYUVCombiners();
 void glCreateClearTex(GLenum target, GLenum fmt, GLint filter,
                       int w, int h, char val);
 void glUploadTex(GLenum target, GLenum format, GLenum type,
@@ -71,7 +132,7 @@
                  int x, int y, int w, int h, int slice);
 void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h,
                GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
-               int sx, int sy, int rect_tex);
+               int sx, int sy, int rect_tex, int is_yv12);
 
 //! could not set new window, will continue drawing into the old one.
 #define SET_WINDOW_FAILED -1
Index: libvo/vo_gl.c
===================================================================
RCS file: /cvsroot/mplayer/main/libvo/vo_gl.c,v
retrieving revision 1.89
diff -u -r1.89 vo_gl.c
--- libvo/vo_gl.c	19 Aug 2005 12:23:34 -0000	1.89
+++ libvo/vo_gl.c	19 Aug 2005 18:59:34 -0000
@@ -56,10 +56,12 @@
 static int osdtexCnt = 0;
 
 static int use_aspect;
+static int use_yuv;
 static int use_rectangle;
 static int err_shown;
 static uint32_t image_width;
 static uint32_t image_height;
+static uint32_t image_format;
 static int many_fmts;
 static int use_glFinish;
 static int swap_interval;
@@ -132,6 +132,7 @@
  * \brief Initialize a (new or reused) OpenGL context.
  */
 static int initGl(uint32_t d_width, uint32_t d_height) {
+  GLuint uvtexs[2];
   texSize(image_width, image_height, &texture_width, &texture_height);
 
   glDisable(GL_BLEND); 
@@ -143,6 +144,20 @@
   mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n",
           texture_width, texture_height);
 
+  if (image_format == IMGFMT_YV12) {
+    glSetupYUVCombiners();
+    glGenTextures(2, uvtexs);
+    ActiveTexture(GL_TEXTURE1);
+    BindTexture(gl_target, uvtexs[0]);
+    glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
+                     texture_width / 2, texture_height / 2, 128);
+    ActiveTexture(GL_TEXTURE2);
+    BindTexture(gl_target, uvtexs[1]);
+    glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
+                     texture_width / 2, texture_height / 2, 128);
+    ActiveTexture(GL_TEXTURE0);
+    BindTexture(gl_target, 0);
+  }
   glCreateClearTex(gl_target, gl_texfmt, GL_LINEAR,
                    texture_width, texture_height, 0);
 
@@ -167,7 +182,13 @@
 	int tmp;
 	image_height = height;
 	image_width = width;
+	image_format = format;
 	glFindFormat(format, &tmp, &gl_texfmt, &gl_format, &gl_type);
+	if (format == IMGFMT_YV12) {
+	  gl_texfmt = GL_LUMINANCE8;
+	  gl_format = GL_LUMINANCE;
+	  gl_type = GL_UNSIGNED_BYTE;
+	}
 
 	int_pause = 0;
 
@@ -350,12 +371,12 @@
   // render alpha
   glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
   BindTexture(gl_target, osdatex[osdtexCnt]);
-  glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1);
+  glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0);
 #endif
   // render OSD
   glBlendFunc (GL_ONE, GL_ONE);
   BindTexture(gl_target, osdtex[osdtexCnt]);
-  glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1);
+  glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0);
   glEndList();
 
   osdtexCnt++;
@@ -389,9 +410,26 @@
 //  glBindTexture(GL_TEXTURE_2D, texture_id);
 
   glColor3f(1,1,1);
+  if (image_format == IMGFMT_YV12) {
+    ActiveTexture(GL_TEXTURE1);
+    glEnable(gl_target);
+    ActiveTexture(GL_TEXTURE2);
+    glEnable(gl_target);
+    ActiveTexture(GL_TEXTURE0);
+    glEnable(GL_REGISTER_COMBINERS_NV);
+  }
   glDrawTex(0, 0, texture_width, texture_height,
             0, 0, texture_width, texture_height,
-            texture_width, texture_height, use_rectangle == 1);
+            texture_width, texture_height,
+            use_rectangle == 1, image_format == IMGFMT_YV12);
+  if (image_format == IMGFMT_YV12) {
+    glDisable (GL_REGISTER_COMBINERS_NV);
+    ActiveTexture(GL_TEXTURE2);
+    glDisable(gl_target);
+    ActiveTexture(GL_TEXTURE1);
+    glDisable(gl_target);
+    ActiveTexture(GL_TEXTURE0);
+  }
 
   if (osdtexCnt > 0) {
     // set special rendering parameters
@@ -427,6 +465,17 @@
 //static inline uint32_t draw_slice_x11(uint8_t *src[], uint32_t slice_num)
 static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y)
 {
+  glUploadTex(gl_target, gl_format, gl_type, src[0], stride[0],
+              x, y, w, h, slice_height);
+  if (image_format == IMGFMT_YV12) {
+    ActiveTexture(GL_TEXTURE1);
+    glUploadTex(gl_target, gl_format, gl_type, src[1], stride[1],
+                x / 2, y / 2, w / 2, h / 2, slice_height);
+    ActiveTexture(GL_TEXTURE2);
+    glUploadTex(gl_target, gl_format, gl_type, src[2], stride[2],
+                x / 2, y / 2, w / 2, h / 2, slice_height);
+    ActiveTexture(GL_TEXTURE0);
+  }
 	return 0;
 }
 
@@ -460,6 +509,15 @@
     err_shown = 1;
     return VO_FALSE;
   }
+  if (mpi->imgfmt == IMGFMT_YV12) {
+    // YV12
+    mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE;
+    mpi->stride[0] = mpi->width;
+    mpi->planes[1] = mpi->planes[0] + mpi->stride[0] * mpi->height;
+    mpi->stride[1] = mpi->width >> 1;
+    mpi->planes[2] = mpi->planes[1] + mpi->stride[1] * (mpi->height >> 1);
+    mpi->stride[2] = mpi->width >> 1;
+  }
   mpi->flags |= MP_IMGFLAG_DIRECT;
   return VO_TRUE;
 }
@@ -477,6 +535,17 @@
   }
   glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[0],
               mpi->x, mpi->y, mpi->w, mpi->h, slice);
+  if (mpi->imgfmt == IMGFMT_YV12) {
+    data += mpi->planes[1] - mpi->planes[0];
+    ActiveTexture(GL_TEXTURE1);
+    glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[1],
+                mpi->x / 2, mpi->y / 2, mpi->w / 2, mpi->h / 2, slice);
+    data += mpi->planes[2] - mpi->planes[1];
+    ActiveTexture(GL_TEXTURE2);
+    glUploadTex(gl_target, gl_format, gl_type, data, mpi->stride[2],
+                mpi->x / 2, mpi->y / 2, mpi->w / 2, mpi->h / 2, slice);
+    ActiveTexture(GL_TEXTURE0);
+  }
   if (mpi->flags & MP_IMGFLAG_DIRECT)
     BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
   return VO_TRUE;
@@ -497,6 +566,8 @@
       caps |= VFCAP_OSD;
     if ((format == IMGFMT_RGB24) || (format == IMGFMT_RGBA))
         return caps;
+    if (use_yuv && format == IMGFMT_YV12)
+        return caps;
     if (many_fmts &&
          glFindFormat(format, NULL, NULL, NULL, NULL))
         return caps;
@@ -523,6 +594,7 @@
   {"aspect",       OPT_ARG_BOOL, &use_aspect,   NULL},
   {"slice-height", OPT_ARG_INT,  &slice_height, (opt_test_f)int_non_neg},
   {"rectangle",    OPT_ARG_INT,  &use_rectangle,(opt_test_f)int_non_neg},
+  {"yuv",          OPT_ARG_INT,  &use_yuv,      (opt_test_f)int_non_neg},
   {"glfinish",     OPT_ARG_BOOL, &use_glFinish, NULL},
   {"swapinterval", OPT_ARG_INT,  &swap_interval,NULL},
   {NULL}
@@ -535,6 +607,7 @@
     use_osd = 1;
     scaled_osd = 0;
     use_aspect = 1;
+    use_yuv = 0;
     use_rectangle = 0;
     use_glFinish = 0;
     swap_interval = 1;


More information about the MPlayer-dev-eng mailing list