Linux Game Programming for PC & Embedded Systems using SDL
Presented by
Fore June
Author of Windows Fan, Linux Fan

Sample program of playing raw video from an AVI file.

avplayer.cpp
mpeg4ip.h
avilib.h
avilib.c



avplayer.cpp:
/*
  avplayer.cpp
  compile by: <b>g++ -o avplayer avplayer.cpp avilib.c -I/usr/include -L/usr/local/lib -lSDL</b>
  execute by: ./avplayer avi_file

  See
	http://www.webkinesia.com/games/videoformats2.php
*/
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "avilib.h"

using namespace std;

//shared variables
bool quit = false;
char *buf[4];
unsigned long head = 0,  tail = 0;
unsigned int frameSize;		//total data of one frame in bytes
short fps;			//frames per second

//Consumer
int player ( void *scr )
{
  SDL_Surface *screen = ( SDL_Surface * ) scr;
  Uint32 prev_time, current_time;
  short frame_time;

  current_time = SDL_GetTicks();//ms since library starts
  prev_time = SDL_GetTicks();   //ms since library starts
  frame_time = 1000 / fps;	//frame time in ms
  while ( !quit ) {
    if ( head == tail ) {	//buffer empty (data not available yet )
      SDL_Delay ( 30 );		//sleep for 30 ms
      continue;
    }
    //consumes the data
    screen->pixels = buf[head%4];
    current_time = SDL_GetTicks();        //ms since library starts
    if ( current_time - prev_time < frame_time )  
      SDL_Delay ( frame_time - ( current_time - prev_time ) );
    prev_time = current_time;

    SDL_UpdateRect ( screen, 0, 0, 0, 0 );      //update whole screen
    head++;
  } //while
  return 0;
}

//turn the image upside down to make it consistent with SDL format
void up_down_flip ( char *image, avi_t *avi )
{
  short one_row_bytes = frameSize / avi->height;
  char *one_row;			//holds one row of data

  one_row = ( char * ) malloc ( one_row_bytes );

  for ( short i = 0; i < avi->height/2; ++i ) {
    memcpy ( one_row, image+one_row_bytes*i, one_row_bytes );	//copy i-th row of image to one_row
    //copy one row from bottom half of image to upper half
    memcpy ( image+one_row_bytes*i, image+one_row_bytes*(avi->height-i-1), one_row_bytes );
    //copy the saved one_row to bottom half
    memcpy (  image + one_row_bytes*( avi->height-i-1), one_row, one_row_bytes );
  }
  free ( one_row );
}

//Producer
int decoder ( void *avip )
{
  long n;
  avi_t *avi = ( avi_t * ) avip;
  int videoFramesRead = 0, numBytes;
  unsigned long short_frames_len = 4;
  unsigned long emptyFramesRead = 0;

  while ( !quit ) {
     if ( tail >= head + 4 ) {	//buffer full, sleep a while
	SDL_Delay ( 30 );	
	continue;
     }
     //produce data
    if ( ( numBytes = AVI_read_frame( avi, (char *)buf[tail%4] ) ) < 0 ) 
	break;
    videoFramesRead++;
    //eliminate short frames
    if ( numBytes <= short_frames_len ) {
      emptyFramesRead++;
      continue;
    }
    //avi->video_frames holds number of video frames in file
    if ( videoFramesRead >= avi->video_frames ) numBytes = -1;

     if ( numBytes < 0 )
	quit = true; 
     else {
      	up_down_flip (buf[tail%4],avi);	//flip the data to make SDL and AVI format consistent
	tail++;
     }
  } //while
  return 0;
}


int main( int argc, char *argv[] )
{   
  SDL_Surface *screen;
  SDL_Thread *producer, *consumer;
  SDL_Event event;
  int status;
  char *key;
  short bytes_per_pixel;
  avi_t *avi = NULL;            //points to opened avi file
  char infilename[30];
 
  if ( argc < 2 ) {
    printf("\nUsage: avplayer infile\n");
    exit ( 1 );
  }
  strcpy ( infilename, argv[1] );
 
  avi = AVI_open_input_file( infilename, 1 );
  if (avi == NULL) {
    fprintf(stderr, "error %s: %s\n", infilename, AVI_strerror());
    exit( 2 );
  }
  
  //very rough guess if avi file is compressed
  if ( ( strlen( avi->compressor ) > 0 ) && !strstr ( avi->compressor, "DIB" ) ) {
    fprintf(stderr, "avi file has been compressed, format not supported yet!\n");
    exit ( 3 );
  }                                                                                     
  fps = ( short ) AVI_video_frame_rate( avi ); //frames per second
  frameSize =  avi->video_index[0].len;
  bytes_per_pixel = frameSize / ( avi->width * avi->height );
  //move file pointer pointing to beginning of video data
  if (AVI_seek_start( avi )) {
     fprintf(stderr, "bad seek 0: %s\n", AVI_strerror());
     exit( 4 );
  }
  if (AVI_set_video_position( avi, 0, NULL)) {
     fprintf(stderr, "bad seek 1: %s\n", AVI_strerror());
     exit( 5 );
  }

  //initialize video system
  if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
        fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
        exit(6);
  }
  //ensure SDL_Quit is called when the program exits
  //atexit(SDL_Quit);		//conflict with freeing dynamically allocated memory
   
  //set video mode 
  screen = SDL_SetVideoMode(avi->width, avi->height, bytes_per_pixel * 8, SDL_SWSURFACE);
  if ( screen == NULL ) {
        fprintf(stderr, "Unable to set %dx%d video: %s\n", avi->width, avi->height, SDL_GetError());
        exit(7);
  }
  //create buffers to hold at most 4 frames of video data
  for ( int i = 0; i < 4; ++i ) {
    buf[i] = ( char * ) malloc ( frameSize );
    assert ( buf[i] );
  }
  //thread to play data 
  consumer = SDL_CreateThread ( player, screen );
  //thread to fetch data
  producer = SDL_CreateThread ( decoder, ( void * ) avi );
  while (!quit)
  {
    status = SDL_WaitEvent(&event);     //wait indefinitely for an event to occur
                                        //event will be removed from event queue
    if ( !status ) {                    //Error has occured while waiting
        printf("SDL_WaitEvent error: %s\n", SDL_GetError());
        quit = true;
        return false;
    }
    switch (event.type) {               //check the event type
      case SDL_KEYDOWN:                 //if a key has been pressed
        key = SDL_GetKeyName(event.key.keysym.sym);
        printf("The %s key was pressed!\n", key );
        if ( event.key.keysym.sym == SDLK_ESCAPE )      //quit if 'ESC' pressed
          quit = true;
        else if ( key[0] == 'q'  )      //quit if 'q'  pressed
          quit = true;                  //same as "if ( event.key.keysym.sym == SDLK_q )"
    }
    SDL_Delay ( 100 );			//give up some CPU time
  }

  //wait for child threads to finish before exit
  SDL_WaitThread ( consumer, NULL );
  SDL_WaitThread ( producer, NULL );
  //free memory
  for ( int i = 0; i < 4; ++i )
    if ( buf[i] != NULL )
      free ( buf[i] );
  printf("Video play successful!\n");
  return 0;
}