Jump to content
  • Advertisement
Sign in to follow this  
lunix

OpenGL AVI texture mapper

This topic is 4440 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im new to these forums however I spent a hair pulling weekend trying to get compressed AVI's to play in windows. For some reason I couldnt get the NeHe tutorial to work on XVID AVI's. In the interest of saving other people this pain and frustration I would like to post my AVI player as a sort of tutorial/sample code. Would this be the correct forum? It is written as a C++ class and uses the VFW interface. The other thing is that the hearder file is 6K and the class is 8K. Would this be too much to post in a CODE block?

Share this post


Link to post
Share on other sites
Advertisement
What the heck. If this is the wrong forum I appoligize.

GLAvi.h

[source lang=cpp]
/*******************************************************************************
$Id:$

Date: $DATE$

Author: Neil Brideau <corn@shaw.ca>

Project: Playing Compressed Avis in OpenGL WIN32

Description: Tutorial Demo Code.

$Log:$

*******************************************************************************/


#ifndef _GL_AVI_H_INCLUDED_
#define _GL_AVI_H_INCLUDED_


// Includes ==================================================================/
#include <stdio.h>
#include <windows.h> // Windows
#include <vfw.h> // Video For Windows
#include <GL/gl.h> // OpenGL

// Libraries =================================================================/
#pragma comment(lib, "vfw32.lib")
#pragma comment(lib, "opengl32.lib")

/**
@class GLAvi
Grabs compressed avi frames and places them in a texture
using windows VFW interface. This is sample code.

In the interest of simplicity the output format of the
texture is limited to 1024x1024 at 24bits.

I spent a whole weekend trying to play XVID videos in OpenGL
for reasons still unknown to me I could not get NeHe's tutorial
to work on compressed avi's. This method ended up working for
me so I thought I would share.

Usage:

GLfloat coords[4];
GLAvi * movie = NULL;

// Make movie object and check initialization
// Also get the suggested texture coordinates
bool Initialize(void) {
movie = new GLAvi();
if (!movie->Init("Sacrifice.avi"))
return false;
movie->GetTexCrds(&coords[0]);
return true;
}

// Draw a textured quad with current movie frame
// using the suggested texture coordinates
bool Draw(GLvoid) {
movie->GetNextFrame(GetTickCount());
glBegin(GL_QUADS);
glTexCoord2f(coords[0], coords[2]); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(coords[1], coords[2]); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(coords[1], coords[3]); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(coords[0], coords[3]); glVertex3f(-1.0f, 1.0f, 1.0f);
glEnd();
return true;
}

*/

class GLAvi {
/** @name Construktorz and Destruktorz
@{ */

public:
/** Default constructor */
GLAvi(void);
/** Default destructor */
~GLAvi(void);

/** @} */
/** @name Public interface methods
@{ */

public:
/**
Initialize the VFW interface.

This method intializes vfw and generates a texture

@param filename The filename of the avi we are to play
@returns success... hopefully :) */

bool Init(const char * filename);

/**
Updates the texture with a new frame if necessary
based on the FPS. Leaves texture bound

@returns true on success or false if video is done. */

bool GetNextFrame(GLuint ticks);

/** @} */
/** @name Accessor Methods
Allows one to modify certain parameters or aquire certain
parameters
*/

public:
/**
Used to set frames per second
Turns into ticks per frame internally. This
is set automagically by Init().

@param fps Frames per second */

void SetFPS(float fps);

/**
Seeks X seconds from the start of the avi
Approximately

@param seconds # seconds */

void SeekTime(int seconds);

/**
Changes current frame to frame X
@param Frame # to use */

void SeekFrame(int frame);

/**
Returns the last frame # in the avi
@returns Last frame number*/

unsigned int GetLastFrame(void);

/** Returns width of avi */
int GetWidth(void);

/** Returns height of avi */
int GetHeight(void);

/** Returns texture object */
GLuint GetTex(void);

/** Texture coordinate suggestion
Methods to suggest texture coordinates.
If you want to display the avi on a quad these can help
X right , X left, Y Top, Y Bottom */

void GetTexCrds(GLfloat * crds);

/** @} */
private:
/** Internal reset mechanism */
void Reset(void);


/** @name Private member variables
@{ */

private:
bool m_isinit;

// Time syncronization routines
int m_tpf; // Ticks per frame
GLuint m_firsttick; // Tick we started at
GLuint m_lasttick; // Tick stamp of last frame
unsigned int m_currframe; // Current frame we are displaying
unsigned int m_lastframe; // EOF

private:
// Output format and texture data
GLuint m_tex; // Texture object
int m_bpp; // Bytes per pixel output format 3 (24 bit)
int m_dim; // Texture dimensions (Limiting to 1024x1024)
unsigned char * m_texframe; // Pointer to our frame as a texture
unsigned char * m_avidata; // Pointer to compressed avi frame

private:
// Windows VFW and AVIFILE data
HIC m_hic; // Handle to our decompressor
PAVISTREAM m_stream; // Handle to our avi stream

LPBITMAPINFOHEADER m_outformat;// Contains decompressor output format
LPBITMAPINFOHEADER m_informat; // Contains decompressor input format

};
#endif

Share this post


Link to post
Share on other sites
GLAvi.cpp

[source lang=cpp]
/*******************************************************************************
$Id:$

Date: $DATE$

Author: Neil Brideau <corn@shaw.ca>

Project: Playing Compressed Avis in OpenGL WIN32

Description: Tutorial Demo Code.

$Log:$

*******************************************************************************/




#include "GLAvi.h"

GLAvi::GLAvi(void) {
m_isinit = false;
m_firsttick = 0;
m_lasttick = 0;
m_currframe = 0;
m_tpf = 0;
m_tex = 0;
m_bpp = 3; // 24 bits
m_dim = 1024; // 1024 x 1024 texture
m_stream = NULL;
m_avidata = NULL;
m_texframe = NULL;
m_informat = NULL;
m_outformat = NULL;

// TODO Better memory alloc in Init
m_avidata = new unsigned char[m_dim * m_dim * m_bpp];
if (!m_avidata) {
printf("ERROR! MEMALLOC m_avidata \n");
return;
}

// TODO Better memory alloc in Init
m_texframe = new unsigned char[m_dim * m_dim * m_bpp];
if (!m_texframe) {
printf("ERROR! MEMALLOC m_texframe\n");
return;
}
memset(m_texframe, 0, m_dim * m_dim * m_bpp);
memset(m_avidata, 0, m_dim * m_dim * m_bpp);

AVIFileInit();
return;
}

GLAvi::~GLAvi(void) {
Reset();
AVIFileExit();
delete [] m_avidata;
delete [] m_texframe;
return;
}

void GLAvi::Reset(void) {
// Reset vars
m_isinit = false;
m_firsttick = 0;
m_lasttick = 0;
m_currframe = 0;
m_tpf = 0;

// Delete format structs
delete [] m_informat;
delete [] m_outformat;

// Delete / release resources
if (m_tex) glDeleteTextures(1, &m_tex);
if (m_stream) AVIStreamRelease(m_stream);
if (m_hic) ICDecompressEnd(m_hic);

// Reset pointer and handles
m_tex = 0;
m_stream = NULL;
m_hic = NULL;
m_informat = NULL;
m_outformat = NULL;
return;
}

void GLAvi::SetFPS(float fps) {
// Ticks per frame. Not the most accurate
// but good enough for rock and roll
m_tpf = (int)(1.0f / (fps / 1000.0f));
//m_tpf *= 2;
return;
}

void GLAvi::SeekTime(int seconds) {
m_currframe = seconds * 1000 / m_tpf;
return;
}

void GLAvi::SeekFrame(int frame) {
m_currframe = frame;
return;
}

unsigned int GLAvi::GetLastFrame(void) {
return m_currframe;
}

int GLAvi::GetWidth(void) {
if (!m_isinit) return 0;
return m_informat->biWidth;
}

int GLAvi::GetHeight(void) {
if (!m_isinit) return 0;
return m_informat->biHeight;
}

GLuint GLAvi::GetTex(void) {
return m_tex;
}

void GLAvi::GetTexCrds(GLfloat * crds) {
// For suggested texture coordinates TODO FIXME not done!
// X Left
crds[0] = 0.0f;
// X Right
crds[1] = 1.0f - ((float)(m_dim - m_informat->biWidth) / (float)m_dim);
// Y Top
crds[2] = -0.125f;
// Y Bottom
crds[3] = 1.125f - ((float)(m_dim - m_informat->biHeight) / (float)m_dim);
return;
}

bool GLAvi::Init(const char * filename) {
AVISTREAMINFO avi_info; // AVI stream information. Used for fps
LONG size; // Size of AVI format header struct
Reset();

// Open our avi stream
if (AVIStreamOpenFromFile(&m_stream, filename, streamtypeVIDEO, 0, OF_READ, NULL)) {
printf("[GLAvi::Init] AVIStreamOpenFromFile returned an error code\n");
return false;
}

// Get some information from avi stream
if (AVIStreamInfo(m_stream, &avi_info, sizeof(avi_info))) {
printf("[GLAvi::Init] AVIStreamInfo returned an error code\n");
return false;
}

// Set our frames per second
SetFPS(((float)avi_info.dwRate / (float)avi_info.dwScale));

// Get our format header size with macro
if (AVIStreamFormatSize(m_stream, 0, &size)) {
printf("[GLAvi::Init] AVIStreamFormatSize returned an error code\n");
return false;
}

// Setup our format structs
m_informat = (LPBITMAPINFOHEADER)new unsigned char[size];
m_outformat = (LPBITMAPINFOHEADER)new unsigned char[size];
if (!m_informat || !m_outformat) {
printf("[GLAvi::Init] ERROR MEMALLOC m_format\n");
return false;
}
memset(m_outformat, 0, size);
memset(m_informat, 0, size);

// Read in our input format
if (AVIStreamReadFormat(m_stream, 0, m_informat, &size)) {
printf("[GLAvi::Init] AVIStreamReadFormat returned and error code\n");
return false;
}

// Since we dont want 2048x2048 textures we fail on large inputs
if (m_informat->biWidth > 1024) {
printf("[GLAvi::Init] Input size to large\n");
return false;
}

// Setup our output format
m_outformat->biSize = size; // Sizof self (BITMAPINFOHEADER)
m_outformat->biWidth = m_informat->biWidth; // Set output size to input size for now
m_outformat->biHeight = m_informat->biHeight; // Set output size to input size for now
m_outformat->biPlanes = 1; // RGB 1 plane
m_outformat->biBitCount = m_bpp * 8; // Byts per pixel * 8

// Determine EOF
m_lastframe = AVIStreamLength(m_stream);

// Now use ICLocate to find an appropriate decompressor
m_hic = ICLocate(ICTYPE_VIDEO, avi_info.fccHandler, m_informat, m_outformat, ICMODE_DECOMPRESS);
if (!m_hic) {
printf("[GLAvi::Init] ICLocate did not return a decompressor\n");
return false;
}

// Determine if decompressor will work on AVI stream
if (ICDecompressQuery(m_hic, m_informat, m_outformat) != ICERR_OK) {
printf("[GLAvi::Init] ICDecompressQuery failed cannot decompress format\n");
return false;
}

// Start our decompressor
if (ICDecompressBegin(m_hic, m_informat, m_outformat) != ICERR_OK) {
printf("[GLAvi::Init] ICDecompressBegin failed");
return false;
}


// Will become our frames texture
glGenTextures(1, &m_tex);
glBindTexture(GL_TEXTURE_2D, m_tex);

// Build 1024 X 1024 24 bit texture. The decoder returns frames BGR so we use
// The BL_BGR_EXT extension as our format
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_dim, m_dim, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, m_texframe);

// Really nice quality filtering
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

m_isinit = true;
return true;
}

/*
*/

bool GLAvi::GetNextFrame(GLuint ticks) {
if (!m_isinit) return false;
if (m_currframe > m_lastframe) return false;

// Auto bind texture for our caller :)
glBindTexture(GL_TEXTURE_2D, m_tex);

// See if we need to update out texture
if ((ticks - m_lasttick) > m_tpf) {
m_lasttick = ticks;
m_currframe++;

// Use AVI stream to read one frame into buffer;
if (AVIStreamRead(m_stream, m_currframe, 1, m_avidata, m_dim * m_dim * m_bpp, NULL, NULL)) {
printf("[GLAvi::GetNextFrame] AVIStreamRead returned an error");
return false;
}

// Decompress frame into our texture buffer
if (ICDecompress(m_hic, 0, m_informat, m_avidata, m_outformat, m_texframe) != ICERR_OK) {
printf("[GLAvi::GetNextFrame] ICDecompress returned an error");
return false;
}

// Update only the portion of our texture that changed
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_informat->biWidth, m_informat->biHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, m_texframe);
}

return true;
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!