[MPlayer-dev-eng] OpenGL

malc malc at pulsesoft.com
Wed Sep 6 16:28:39 CEST 2006


Hi,

Currently OpenGL video output with hardware yuv2rgb conversion
requires 3 texture units. Following is the version that works
with 2 TUs and a back-buffer (i.e. works on GeForce2MX here).
Perhaps someone could integrate it into existing framework.

/*  Clamping idea/initial combiner implementation - Fabian Giesen/ryg
    Requires 2 staged register combiner, 2 texture units and a back buffer
    24 bit frame buffer is required(5 bits of precision for U+V is not enough)
    to build OpenGL, GLUT and GLEW are needed
    build: gcc -o yuv yuv.c -lGL -lGLEW -lglut
    run: ffmpeg -i moo.mpg -s WIDTHxHEIGHT -f rawvideo - | yuv WIDTH HEIGHT
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

enum {Y,U,V,T,NB_TEXS};
typedef struct {
    unsigned int size;
    unsigned int w, h;
    char *data;
    struct plane {
        unsigned int w, h, p2w, p2h, offset, size;
        GLenum unit;
        GLuint id;
        GLfloat coords[4][2];
    } planes[4];
} GLState;

static GLState *global_state;
static GLint quad[] = {-1, 1, 1, 1, 1, -1, -1, -1};
static GLfloat yuv2rgb[2][4] = {
    {0.500000f, 0.413650f, 0.944700f, 0.f},
    {0.851850f, 0.320550f, 0.500000f, 1.f},
};

/* http://aggregate.org/MAGIC/ */
static unsigned int nlpo2 (unsigned int x)
{
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    return x + 1;
}

static void nvrc2tu2_combine_UV (void)
{
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
                       GL_TEXTURE0_ARB, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
                       GL_CONSTANT_COLOR0_NV, GL_EXPAND_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
                       GL_TEXTURE1_ARB, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
                       GL_CONSTANT_COLOR1_NV, GL_EXPAND_NORMAL_NV, GL_RGB);

    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
                       GL_SPARE0_NV, GL_SIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
                       GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
                       GL_ZERO, GL_HALF_BIAS_NEGATE_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV,
                       GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerParameteriNV (GL_NUM_GENERAL_COMBINERS_NV, 2);
}

static void nvrc2tu2_combine_final (void)
{
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
                       GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
                       GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
                       GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
                       GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerParameteriNV (GL_NUM_GENERAL_COMBINERS_NV, 1);
}

static void tex_params (void)
{
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}

static GLState *init (GLsizei w, GLsizei h, int xshift, int yshift)
{
    GLState *s;
    size_t i;
    GLuint texids[NB_TEXS];
    struct plane *plane;

    s = malloc (sizeof (*s));
    if (!s) {
        perror ("malloc");
        exit (EXIT_FAILURE);
    }

    s->w = w;
    s->h = h;

    s->planes[Y].w = w;
    s->planes[Y].h = h;
    s->planes[Y].size = w * h;
    s->planes[Y].offset = 0;

    s->planes[U] = s->planes[0];
    s->planes[U].w >>= xshift;
    s->planes[U].h >>= yshift;
    s->planes[U].size >>= 2;
    s->planes[U].offset = s->planes[Y].size;

    s->planes[V] = s->planes[1];
    s->planes[V].offset += s->planes[U].size;

    s->size = s->planes[Y].size + (s->planes[U].size << 1);
    s->data = malloc (s->size);
    if (!s->data) {
        perror ("malloc");
        exit (EXIT_FAILURE);
    }

    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glGenTextures (NB_TEXS, texids);

    for (i = 0; i < 3; ++i) {
        GLfloat tw, th;
        unsigned int ww, hh;
        GLenum units[3] = {GL_TEXTURE0_ARB, GL_TEXTURE0_ARB, GL_TEXTURE1_ARB};

        plane = &s->planes[i];
        ww = plane->w;
        hh = plane->h;
        plane->unit = i[units];
        plane->id = texids[i];
        plane->p2w = (ww & (ww - 1)) ? nlpo2 (ww) : ww;
        plane->p2h = (hh & (hh - 1)) ? nlpo2 (hh) : hh;
        tw = (double) ww / plane->p2w;
        th = (double) hh / plane->p2h;

        plane->coords[0][0] = 0.0; plane->coords[0][1] = 0.0;
        plane->coords[1][0] = tw;  plane->coords[1][1] = 0.0;
        plane->coords[2][0] = tw;  plane->coords[2][1] = th;
        plane->coords[3][0] = 0.0; plane->coords[3][1] = th;

        glActiveTextureARB (plane->unit);
        glEnable (GL_TEXTURE_2D);
        glBindTexture (GL_TEXTURE_2D, plane->id);
        tex_params ();
        glTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE8,
                      plane->p2w, plane->p2h, 0, GL_LUMINANCE,
                      GL_UNSIGNED_BYTE, NULL);
    }

    plane = &s->planes[T];
    *plane = s->planes[U];
    plane->id = texids[T];
    plane->unit = GL_TEXTURE1_ARB;

    glBindTexture (GL_TEXTURE_2D, plane->id);
    tex_params ();
    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8,
                  plane->p2w, plane->p2h, 0, GL_RGB,
                  GL_INT, NULL);

    glEnable (GL_REGISTER_COMBINERS_NV);
    glCombinerParameterfvNV (GL_CONSTANT_COLOR0_NV, yuv2rgb[0]);
    glCombinerParameterfvNV (GL_CONSTANT_COLOR1_NV, yuv2rgb[1]);
    return s;
}

static void upload (GLState *s)
{
    size_t i;
    char *ptr;

    if (fread (s->data, s->size, 1, stdin) - 1) {
        if (feof (stdin)) {
            exit (EXIT_SUCCESS);
        }
        perror ("fread");
        exit (EXIT_FAILURE);
    }

    ptr = s->data;
    for (i = 0; i < 3; ++i) {
        GLint als[4] = {4, 1, 2, 1};
        struct plane *plane = &s->planes[i];

        glBindTexture (GL_TEXTURE_2D, plane->id);
        glPixelStorei (GL_UNPACK_ALIGNMENT, als[plane->offset & 3]);
        if (plane->p2w != plane->w) {
            glPixelStorei (GL_UNPACK_ROW_LENGTH, plane->w);
        }
        glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
                         plane->w, plane->h,
                         GL_LUMINANCE, GL_UNSIGNED_BYTE,
                         ptr);
        ptr += plane->size;
    }
}

static void draw (GLState *s)
{
    int i;

    glPushMatrix ();
    glLoadIdentity ();
    glRotatef (180.0, 1.0, 0.0, 0.0);
    glViewport (0, 0, s->planes[T].w, s->planes[T].h);
    glDrawBuffer (GL_BACK);

    nvrc2tu2_combine_UV ();

    glActiveTextureARB (s->planes[U].unit);
    glBindTexture (GL_TEXTURE_2D, s->planes[U].id);

    glActiveTextureARB (s->planes[V].unit);
    glBindTexture (GL_TEXTURE_2D, s->planes[V].id);

    glBegin (GL_QUADS);
    {
        for (i = 0; i < 4; ++i) {
            glMultiTexCoord2fvARB (s->planes[U].unit, s->planes[1].coords[i]);
            glMultiTexCoord2fvARB (s->planes[V].unit, s->planes[2].coords[i]);
            glVertex2iv (quad + i * 2);
        }
    }
    glEnd ();

    glReadBuffer (GL_BACK);
    glActiveTextureARB (s->planes[T].unit);
    glBindTexture (GL_TEXTURE_2D, s->planes[T].id);
    glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 0, 0,
                         s->planes[T].w, s->planes[T].h);

    glPopMatrix ();
    glViewport (0, 0,
                glutGet (GLUT_WINDOW_WIDTH), glutGet (GLUT_WINDOW_HEIGHT));
    glDrawBuffer (GL_FRONT);

    glActiveTextureARB (s->planes[Y].unit);
    glBindTexture (GL_TEXTURE_2D, s->planes[Y].id);

    nvrc2tu2_combine_final ();

    glBegin (GL_QUADS);
    {
        for (i = 0; i < 4; ++i) {
            glMultiTexCoord2fvARB (s->planes[Y].unit, s->planes[Y].coords[i]);
            glMultiTexCoord2fvARB (s->planes[T].unit, s->planes[T].coords[i]);
            glVertex2iv (quad + i * 2);
        }
    }
    glEnd ();
}

static void keyboard_cb (unsigned char c, int huh, int doh)
{
    switch (c) {
        case 27:
        case 'q':
            exit (EXIT_SUCCESS);
    }
}

static void idle_cb (void)
{
    GLState *s = global_state;

    upload (s);
    draw (s);
    glutPostRedisplay ();
}

static void reshape_cb (int w, int h)
{
    glViewport (0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
}

int main (int argc, char **argv)
{
    int w, h;

    glutInit (&argc, argv);

    if (argc < 3) {
        fprintf (stderr, "usage: yuv width height < rawvideo.i420\n");
        fprintf (stderr,
                 "   OR: ffmpeg -i moo.mpg -f rawvideo -s WxH - | yuv W H\n");
        exit (EXIT_FAILURE);
    }

    w = atoi (argv[1]);
    h = atoi (argv[2]);

    glutInitDisplayMode (GLUT_DOUBLE);
    glutInitWindowSize (w, h);
    glutCreateWindow ("YUV2RGB");
    glewInit ();

    global_state = init (w, h, 1, 1);

    glutReshapeFunc (reshape_cb);
    glutKeyboardFunc (keyboard_cb);
    glutIdleFunc (idle_cb);
    glutMainLoop ();
    exit (EXIT_SUCCESS);
}





More information about the MPlayer-dev-eng mailing list