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

Games and SDL
SDL Installation
SDL for Embedded
SDL API
SDL Events
 SDL Graphics
SDL Threads
Thread Example
SDL Animation
SDL Sound
 Raw Video Player
Video Formats
Video Compression
 Game Trees
About The Author
@Copyright by Fore June, 2006

Video File Formats

1 2
  1. Playing Raw .avi File

    Now, combining what we have learned about AVI file format and the method of playing raw video data discussed in the previous chapter, we can easily write a simple player that plays an uncompressed .avi video file. All we need to do is to have a thread to read the data from an .avi file, extracting the raw video data frame by frame and another thread to play the data ( blit them to the screen ). The only catch is that SDL defines the upper left corner of the screen to be the origin while AVI defines the left lower corner. To make the display consistent, we have to flip a frame of the .avi data up side down. The following piece of code does the job:

    /*
      image[] holds one frame of data
      frameSize is the number of bytes of data in one frame
    */
    void up_down_flip ( char *image, int frameSize, int imageWidth, int imageHeight )
    {
      int  number_of_bytes_in_one_row  = frameSize / imageHeight;
      char *one_row;			//holds one row of data
      one_row = ( char * ) malloc ( number_of_bytes_in_one_row );
      for ( int i = 0; i < imageHeight/2; ++i ) {
        memcpy ( one_row, image+one_row_bytes*i, number_of_bytes_in_one_row );   //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), number_of_bytes_in_one_row );
        //copy the saved one_row to bottom half
        memcpy (  image + one_row_bytes*( imageHeight-i-1), one_row, number_of_bytes_in_one_row_bytes );
      }
      free ( one_row );		//free the memory
    }
    

    The following program, avplayer.cpp is a complete program that can play a .avi file with raw video data. Again, you need to compile with with avilib.c using a command like,

    /*
      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
    */
    #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;
    }
    
    

    For your convenience, we again list below all the links to the files required to compile this program:

  2. avplayer.cpp
  3. avilib.c
  4. avilib.h
  5. mpeg4ip.h
  6. <<Prev