[MPlayer-dev-eng] Re: OpenGL

malc malc at pulsesoft.com
Sat Sep 16 00:38:23 CEST 2006


Hi,

Following is another version which consumes only one texture unit,
does not claim BACK/AUXn buffers and does not require any particular
framebuffer depth. Some work (pack) uses CPU and is not the fastest
thing in the world, nevertheless performance (due to PBO compatible
texture format) is not so bad on this geforce2mx.

It would be nice to know who came up with 1/4 range idea, so i can
give a proper credit.

/*  Clamping idea - Fabian Giesen/ryg
    Requires 2 staged register combiner and one texture unit
    works only on 420 planar
    to build OpenGL, GLUT and GLEW are needed
    build: gcc -o yuvi yuvi.c -lGL -lGLEW -lglut
    run: ffmpeg -i moo.mpg -s WIDTHxHEIGHT -f rawvideo - | yuvi WIDTH HEIGHT
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

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

#define USE_PBUFFER
enum {NB_TEXS=1};
enum {Y,U,V};
typedef struct {
    unsigned int size;
    unsigned int w, h, p2w, p2h;
    GLuint id;
    unsigned char *data;
    void *bgra;
    GLfloat coords[4][2];
    GLenum format, internal_format, type;
    struct plane {
        unsigned int offset, size;
    } planes[3];
    GLuint pbuffer;
} 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 pack420 (GLState *s)
{
    unsigned int x, y;
    unsigned char *py = s->data + s->planes[Y].offset;
    unsigned char *pu = s->data + s->planes[U].offset;
    unsigned char *pv = s->data + s->planes[V].offset;
    unsigned int hw = s->w >> 1;
    unsigned int hh = s->h >> 1;
    unsigned long long *bgra;

    bgra = s->pbuffer
        ? glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY)
        : s->bgra
        ;

    for (y = 0; y < hh; ++y) {
        unsigned int i;
        unsigned char *u1, *v1, *y1;

        for (i = 0; i < 2; ++i) {
            for (u1 = pu, v1 = pv, y1 = py, x = 0; x < hw; ++x) {
                unsigned char u = *u1++;
                unsigned char v = *v1++;
                unsigned int uvv0 = (u << 24) | (v << 16) | (v << 8);
                unsigned long long bgrabgra = uvv0 | *y1++;

                *bgra++ = bgrabgra | (unsigned long long) (uvv0 | *y1++) << 32;
            }
            py += s->w;
        }

        pu += hw;
        pv += hw;
    }
    if (s->pbuffer) {
        glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB);
    }
}

static void (*pack) (GLState *) = pack420;

static void setup_combiners (GLState *s)
{
    glEnable (GL_REGISTER_COMBINERS_NV);
    glCombinerParameterfvNV (GL_CONSTANT_COLOR0_NV, yuv2rgb[0]);
    glCombinerParameterfvNV (GL_CONSTANT_COLOR1_NV, yuv2rgb[1]);

    glCombinerInputNV (GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV,
                       GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);

    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
                       GL_TEXTURE0_ARB, GL_HALF_BIAS_NORMAL_NV, GL_ALPHA);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
                       GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
                       GL_TEXTURE0_ARB, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
    glCombinerInputNV (GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
                       GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
    glCombinerOutputNV (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);

    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
                       GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    glCombinerInputNV (GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
                       GL_SPARE0_NV, GL_SIGNED_IDENTITY_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 tex_params (void)
{
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    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 (unsigned int w, unsigned int h, int xshift, int yshift)
{
    GLState *s;
    unsigned int i;
    GLuint texids[NB_TEXS];
    struct plane *plane;
    GLfloat tw, th;

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

    s->format = GL_BGRA;
    s->internal_format = GL_RGBA;
    s->type = GL_UNSIGNED_INT_8_8_8_8_REV;

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

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

    s->planes[U] = s->planes[Y];
    s->planes[U].size >>= (xshift + yshift);
    s->planes[U].offset = s->planes[Y].size;

    s->planes[V] = s->planes[U];
    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);
    }

#ifndef USE_PBUFFER
    s->bgra = malloc (w * 4 * h);
    if (!s->bgra) {
        perror ("malloc temp");
        exit (EXIT_FAILURE);
    }
#else
    s->bgra = NULL;
#endif

    glGenTextures (1, texids);
    s->id = texids[0];

    s->p2w = (w & (w - 1)) ? nlpo2 (w) : w;
    s->p2h = (h & (h - 1)) ? nlpo2 (h) : h;
    tw = (double) w / s->p2w;
    th = (double) h / s->p2h;

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

    glEnable (GL_TEXTURE_2D);
    glBindTexture (GL_TEXTURE_2D, s->id);
    tex_params ();
    glTexImage2D (GL_TEXTURE_2D, 0, s->internal_format,
                  s->p2w, s->p2h, 0, s->format,
                  s->type, NULL);

#ifdef USE_PBUFFER
    glGenBuffers (1, &s->pbuffer);
    glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, s->pbuffer);
    glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, w * h * 4, NULL,
                     GL_STREAM_DRAW);
#else
    s->pbuffer = 0;
#endif
    setup_combiners (s);
    return s;
}

static void upload (GLState *s)
{
    static const GLint als[4] = {4, 1, 2, 1};

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

    pack (s);

    glBindTexture (GL_TEXTURE_2D, s->id);
    glPixelStorei (GL_UNPACK_ALIGNMENT, als[s->w & 3]);
    glPixelStorei (GL_UNPACK_ROW_LENGTH, s->w );
    glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0,
                     s->w, s->h,
                     s->format, s->type, s->bgra);
}

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

    glBegin (GL_QUADS);
    {
        for (i = 0; i < 4; ++i) {
            glTexCoord2fv (s->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: yuvi width height < rawvideo.i420\n");
        fprintf (stderr,
                 "   OR: ffmpeg -i moo.mpg -f rawvideo -s WxH - | yuvi W H\n");
        exit (EXIT_FAILURE);
    }

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

    glutInitDisplayMode (GLUT_SINGLE);
    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