Video OpenGL texture with libvlc (on SDL_Surface)

Hi there,

I want a video texture on a OpenGL quad. I found this tutorial to read video on a SDL_Surface : http://wiki.videolan.org/LibVLC_SampleCode_SDL
It works fine.

I just want to add a OpenGL quad and to use the video as a texture. But the only thing I get is a white square.
Here’s my code :

#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>

#include <SDL/SDL.h>
#include <SDL/SDL_mutex.h>
#include <GL/glu.h>

#include <vlc/vlc.h>

#define WIDTH 640
#define HEIGHT 480
#define VIDEO_FILE "/home/user/path/movie.avi"


struct ctx {
	SDL_Surface *surf;
	SDL_mutex *mutex;
};

static void catchEx (libvlc_exception_t *ex) {
	if(libvlc_exception_raised(ex)) {
		fprintf(stderr, "Exception libvlc : %s
", libvlc_exception_get_message(ex));
		exit(EXIT_FAILURE);
	}
	libvlc_exception_clear(ex);
}

static void lock(struct ctx *ctx, void **pp_ret) {
	SDL_LockMutex(ctx->mutex);
	SDL_LockSurface(ctx->surf);
	*pp_ret = ctx->surf->pixels;
}

static void unlock(struct ctx *ctx) {
	SDL_UnlockSurface(ctx->surf);
	SDL_UnlockMutex(ctx->mutex);
}



int main(int argc, char *argv[])
{
    char clock[64], cunlock[64], cdata[64];
    char width[32], height[32], pitch[32];
    libvlc_exception_t ex;
    libvlc_instance_t *libvlc;
    libvlc_media_t *m;
    libvlc_media_player_t *mp;
    char const *vlc_argv[] =
    {
        "--quiet",	
        "--ignore-config",		
        "--vout", "vmem",
        "--vmem-width", width,
        "--vmem-height", height,
        "--vmem-pitch", pitch,
        "--vmem-chroma", "RV16",
        "--vmem-lock", clock,
        "--vmem-unlock", cunlock,
        "--vmem-data", cdata
    };
    int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);

    SDL_Surface *screen;
    SDL_Event event;
    int done = 0, action = 0;

    struct ctx ctx;

    /*
     *  Init libSDL
     */
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) == -1)
    {
        printf("cannot initialize SDL
");
        return EXIT_FAILURE;
    }

    ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, 16, 0xf800, 0x07e0, 0x001f, 0);

    ctx.mutex = SDL_CreateMutex();

    int options = SDL_ANYFORMAT | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_OPENGL;

    screen = SDL_SetVideoMode(1280, 800, 0, options);
    if(!screen)
    {
        printf("cannot set video mode
");
        return EXIT_FAILURE;
    }



    /* Get a texture id */
    GLuint image;
    glGenTextures(1, &image);


    /*
     *  Init libVLC
     */
    sprintf(clock, "%lld", (long long int)(intptr_t)lock);
    sprintf(cunlock, "%lld", (long long int)(intptr_t)unlock);
    sprintf(cdata, "%lld", (long long int)(intptr_t)&ctx);
    sprintf(width, "%i", WIDTH);
    sprintf(height, "%i", HEIGHT);
    sprintf(pitch, "%i", WIDTH * 2);

    libvlc_exception_init(&ex);

    libvlc = libvlc_new(vlc_argc, vlc_argv, &ex);
    catchEx(&ex);

    m = libvlc_media_new(libvlc, VIDEO_FILE, &ex);
    catchEx(&ex);

    mp = libvlc_media_player_new_from_media(m, &ex);
    catchEx(&ex);

    libvlc_media_release(m);
    libvlc_media_player_play(mp, &ex);
    catchEx(&ex);
	
    while(!done) {
        action = 0;

        /* Keys: enter (fullscreen), space (pause), escape (quit) */
        while( SDL_PollEvent( &event ) )
        {
            switch(event.type)
            {
            case SDL_QUIT:
                done = 1;
                break;
            case SDL_KEYDOWN:
                action = event.key.keysym.sym;
                break;
            }
        }

        switch(action)
        {
        case SDLK_ESCAPE:
            done = 1;
            break;
        }


        /* Blitting the surface does not prevent it from being locked and
         * written to by another thread, so we use this additional mutex. */
        SDL_LockMutex(ctx.mutex);
        //SDL_BlitSurface(ctx.surf, NULL, screen, NULL);


        glBindTexture(GL_TEXTURE_2D, image);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ctx.surf->w, ctx.surf->h, 0, GL_RGB, GL_UNSIGNED_BYTE, ctx.surf->pixels);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

		glBegin(GL_QUADS);
			glTexCoord2d(0, 1);
			glVertex2f(0, 0.9);
			glTexCoord2d(1, 1);
			glVertex2f(0.9, 0.9);
			glTexCoord2d(1, 0);
			glVertex2f(0.9, -0.9);
			glTexCoord2d(0, 0);
			glVertex2f(0, -0.9);
		glEnd();

        SDL_UnlockMutex(ctx.mutex);

        //SDL_Flip(screen);
	glFlush();
        SDL_GL_SwapBuffers();
        SDL_Delay(10);

    }

    /*
     * Stop stream and clean up libVLC
     */
    libvlc_media_player_stop(mp, &ex);
    catchEx(&ex);


    libvlc_media_player_release(mp);
    libvlc_release(libvlc);

    /*
     * Close window and clean up libSDL
     */
    SDL_DestroyMutex(ctx.mutex);
    SDL_FreeSurface(ctx.surf);

    SDL_Quit();

    return 0;
}

So if someone has an idea to make this code working… :]

Thanks in advance.

Regards.

PS : g++ -otest main.o -lvlc -lSDL_image -lSDL -lGL

I don’t see a call to glEnable(GL_TEXTURE_2D).

Texturing is disabled by default.

The other thing that might be a problem on old hardware is the texture dimensions.

Old hardware might generate an error for your glTexImage2D instead of loading the texture if your texture dimensions aren’t powers of 2. (e.g. 512x256 OK, 720x525 not OK). Check with glGetError.

Any recent hardware should be able to handle non-power-of-two textures though.

Hi there,

Ooops! It was a mistake… :eek: Added!
I have recent (less than a year) hardware.

I improved the code and this is what I get : http://img208.imageshack.us/img208/7343/facei.png (animated, of course). As you can see there’s a little problem…

Here’s my code :

#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>

#include <SDL/SDL.h>
#include <SDL/SDL_mutex.h>
#include <GL/glu.h>

#include <vlc/vlc.h>

#define WIDTH 1024
#define HEIGHT 768
#define VIDEO_FILE "/home/user/path/movie.avi"


struct ctx {
	SDL_Surface *surf;
	SDL_mutex *mutex;
};

static void catchEx (libvlc_exception_t *ex) {
	if(libvlc_exception_raised(ex)) {
		fprintf(stderr, "Exception libvlc : %s
", libvlc_exception_get_message(ex));
		exit(EXIT_FAILURE);
	}
	libvlc_exception_clear(ex);
}

static void lock(struct ctx *ctx, void **pp_ret) {
	SDL_LockMutex(ctx->mutex);
	SDL_LockSurface(ctx->surf);
	*pp_ret = ctx->surf->pixels;
}

static void unlock(struct ctx *ctx) {
	SDL_UnlockSurface(ctx->surf);
	SDL_UnlockMutex(ctx->mutex);
}



int loadTexture(SDL_Surface *sdlimage) {
	GLuint retval;
	void *raw;
	int w, h, i, j, bpp;
	Uint8 *srcPixel, *dstPixel;
	Uint32 truePixel;
	GLenum errorCode;

	if (sdlimage->format->BytesPerPixel < 2) {
		printf("Bad image -- not true color!
");
		SDL_FreeSurface(sdlimage);
		return 0;
	}

	w = sdlimage->w;
	h = sdlimage->h;

	raw = (void *) malloc(w * h * 4);
	dstPixel = (Uint8 *) raw;

	bpp = sdlimage->format->BytesPerPixel;

	for (i = h - 1; i >= 0; i--) {
		for (j = 0; j < w; j++) {
			srcPixel = (Uint8 *) sdlimage->pixels + i * sdlimage->pitch + j	* bpp;
			switch (bpp) {
			case 1:
				truePixel = *srcPixel;
				break;

			case 2:
				truePixel = *(Uint16 *) srcPixel;
				break;

			case 3:
				if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
					truePixel = srcPixel[0] << 16 | srcPixel[1] << 8 | srcPixel[2];
				} else {
					truePixel = srcPixel[0] | srcPixel[1] << 8 | srcPixel[2] << 16;
				}
				break;

			case 4:
				truePixel = *(Uint32 *) srcPixel;
				break;

			default:
				printf("Image bpp of %d unusable
", bpp);
				SDL_UnlockSurface(sdlimage);
				SDL_FreeSurface(sdlimage);
				free(raw);
				return 0;
			}

			SDL_GetRGBA(truePixel, sdlimage->format, &(dstPixel[0]), &(dstPixel[1]), &(dstPixel[2]), &(dstPixel[3]));
			dstPixel++;
			dstPixel++;
			dstPixel++;
			dstPixel++;
		}
	}

	while (glGetError()) {
		;
	}

	glBindTexture(GL_TEXTURE_2D, retval);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


	errorCode = glGetError();
	if (errorCode != 0) {
		if (errorCode == GL_OUT_OF_MEMORY) {
			printf("Out of texture memory!
");
		}

		glDeleteTextures(1, &retval);
		free(raw);
		return 0;
	}

	gluBuild2DMipmaps(GL_TEXTURE_2D, 4, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (Uint8 *) raw);

	errorCode = glGetError();
	if (errorCode != 0) {
		if (errorCode == GL_OUT_OF_MEMORY) {
			printf("Out of texture memory!
");
		}

		glDeleteTextures(1, &retval);
		free(raw);
		return 0;
	}

	return retval;
}




int main(int argc, char *argv[])
{
    char clock[64], cunlock[64], cdata[64];
    char width[32], height[32], pitch[32];
    libvlc_exception_t ex;
    libvlc_instance_t *libvlc;
    libvlc_media_t *m;
    libvlc_media_player_t *mp;
    char const *vlc_argv[] = {
        "--quiet",
        "--ignore-config",
        "--vout", "vmem",
        "--vmem-width", width,
        "--vmem-height", height,
        "--vmem-pitch", pitch,
        "--vmem-chroma", "RV32",
        "--vmem-lock", clock,
        "--vmem-unlock", cunlock,
        "--vmem-data", cdata
    };
    int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);

    SDL_Event event;
    int done = 0, action = 0;

    struct ctx ctx;

    /*
     *  Init libSDL
     */
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) == -1) {
        printf("cannot initialize SDL
");
        exit(EXIT_FAILURE);
    }

    ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, 32, 0xf800, 0x07e0, 0x001f, 0);

    ctx.mutex = SDL_CreateMutex();

    if( ! SDL_SetVideoMode(1280, 800, 0, SDL_ANYFORMAT | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_OPENGL) ) {
        printf("cannot set video mode
");
        exit(EXIT_FAILURE);
    }

    glEnable(GL_TEXTURE_2D);
    GLuint image;
    glGenTextures(1, &image);




    /*
     *  Init libVLC
     */
    sprintf(clock, "%lld", (long long int)(intptr_t)lock);
    sprintf(cunlock, "%lld", (long long int)(intptr_t)unlock);
    sprintf(cdata, "%lld", (long long int)(intptr_t)&ctx);
    sprintf(width, "%i", WIDTH);
    sprintf(height, "%i", HEIGHT);
    sprintf(pitch, "%i", WIDTH * 4);

    libvlc_exception_init(&ex);

	libvlc = libvlc_new(vlc_argc, vlc_argv, &ex);
	catchEx(&ex);

	m = libvlc_media_new(libvlc, VIDEO_FILE, &ex);
	catchEx(&ex);

	mp = libvlc_media_player_new_from_media(m, &ex);
	catchEx(&ex);

	libvlc_media_release(m);
	libvlc_media_player_play(mp, &ex);
	catchEx(&ex);

    while(!done) {
        action = 0;

        /* Keys: enter (fullscreen), space (pause), escape (quit) */
        while( SDL_PollEvent( &event ) ) {
            switch(event.type)
            {
            case SDL_QUIT:
                done = 1;
                break;
            case SDL_KEYDOWN:
                action = event.key.keysym.sym;
                break;
            }
        }

        switch(action) {
        case SDLK_ESCAPE:
            done = 1;
            break;
        }



		glClear(GL_COLOR_BUFFER_BIT);

        /* Blitting the surface does not prevent it from being locked and
         * written to by another thread, so we use this additional mutex. */
        SDL_LockMutex(ctx.mutex);
        image = loadTexture(ctx.surf);
        SDL_UnlockMutex(ctx.mutex);

		glBegin(GL_QUADS);
			glTexCoord2d(0, 1);
			glVertex2f(0, 0.9);
			glTexCoord2d(1, 1);
			glVertex2f(0.9, 0.9);
			glTexCoord2d(1, 0);
			glVertex2f(0.9, -0.9);
			glTexCoord2d(0, 0);
			glVertex2f(0, -0.9);
		glEnd();

		glFlush();
        SDL_GL_SwapBuffers();
        SDL_Delay(10);

    }

    glDeleteTextures(1, &image);


    /*
     * Stop stream and clean up libVLC
     */
    libvlc_media_player_stop(mp, &ex);
    catchEx(&ex);


    libvlc_media_player_release(mp);
    libvlc_release(libvlc);

    /*
     * Close window and clean up libSDL
     */
    SDL_DestroyMutex(ctx.mutex);
    SDL_FreeSurface(ctx.surf);

    SDL_Quit();

    exit(EXIT_SUCCESS);
}

Strange! O_o When I set the vmem-chroma to “RV16” I get : http://img406.imageshack.us/img406/885/halfface.png Perfect but It use only the half part of my quad.

If you have an idea… Thanks! :smiley:

Regards.