Sample program of playing raw video from an AVI file.
/*
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;
}