- /*
avi_to_raw.cpp Need to compile together with MPEG4IP avilib.c Compile: g++ -o avi_to_raw avi_to_raw.cpp avilib.c Execute: ./avi_to_raw infile [video_output_file audio_output_file]
http://www.webkinesia.com/games/index.php */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include "avilib.h"
using namespace std;
int main( int argc, char *argv[] ) { FILE *fpo = NULL; avi_t *avi = NULL; //points to opened avi file char infilename[30], outfilename[30], *pc; double videoFrameRate; unsigned numVideoFrames; unsigned long fileDuration; long numBytes = 0, totalBytes = 0; unsigned long videoFramesRead = 0; unsigned long emptyFramesRead = 0; unsigned long short_frames_len = 4;
if ( argc < 2 ) { printf("\nUsage: avi_to_raw infile [outfile1 outfile2]\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 ); } videoFrameRate = AVI_video_frame_rate( avi ); //frame per second numVideoFrames = AVI_video_frames( avi ); fileDuration = ( unsigned long ) ceil( numVideoFrames / videoFrameRate);
printf("\n---------------------------------------------------------"); printf("\nVideo Frame Rate:\t%5.0f \tfps", videoFrameRate ); printf("\nNumber of Frames:\t%5d", numVideoFrames ); printf("\nFile Duration: \t%5ld \tsec", fileDuration ); printf("\nVideo width: \t%5d \tpixels", avi->width ); printf("\nVideo height: \t%5d \tpixels", avi->height ); printf("\nCompressor type: \t%5s", avi->compressor ); printf("\nAudio channels: \t%5d", avi->a_chans ); printf("\nAudio sample rate:\t%5d \tHz", avi->a_rate ); printf("\nBits per audio sample:\t%5d", avi->a_bits );
printf("\nVideo starts at: \t%5ld ( 0x%lx )", avi->movi_start, avi->movi_start ); printf("\n---------------------------------------------------------\n"); //extract video if ( argc < 3 ) { //construct raw video output file name strcpy ( outfilename, infilename ); pc = strrchr ( outfilename, '.' ); //pc points to '.' if ( pc != NULL ) *pc = 0; strcat ( outfilename, ".rawv" ); //extenstion .rawv } else strcpy ( outfilename, argv[2] ); if ( ( fpo = fopen ( outfilename, "wb" ) ) == NULL ) { fprintf(stderr, "error opening file %s for output\n", outfilename ); exit ( 3 ); } // get a buffer large enough to handle a frame of raw SDTV u_char *buf = ( u_char * ) malloc ( 768 * 576 * 4 ); if ( buf == NULL ) { fprintf(stderr, "Error allocating memory\n"); exit ( 4 ); } //move file pointer pointing to beginning of video data if (AVI_seek_start( avi )) { fprintf(stderr, "bad seek 0: %s\n", AVI_strerror()); exit( 5 ); } if (AVI_set_video_position( avi, 0, NULL)) { fprintf(stderr, "bad seek 1: %s\n", AVI_strerror()); exit( 6 ); }
videoFramesRead = 0; while ( ( numBytes = AVI_read_frame( avi, (char *)buf ) ) > -1 ) { totalBytes += numBytes; videoFramesRead++; printf("frame %d - length %u total %u\r", videoFramesRead, numBytes, totalBytes); //eliminate short frames if ( numBytes > short_frames_len ) { if ( fwrite ( buf, 1, numBytes, fpo ) != numBytes ) { fprintf ( stderr, "Error writing %s\n", outfilename ); break; } } else { emptyFramesRead++; } if ( videoFramesRead >= numVideoFrames ) break; } //while // read error if (numBytes < 0) fprintf(stderr, "read error\n" ); if (emptyFramesRead) { fprintf(stderr, "warning: %u zero length frames ignored\n", emptyFramesRead); }
fclose ( fpo ); //done with video
if ( avi->a_chans == 0 ) { printf("\nno audio channel\n" ); exit ( 7 ); } //extract audio if ( argc < 4 ) { //construct raw audio output file name strcpy ( outfilename, infilename ); pc = strrchr ( outfilename, '.' ); //pc points to '.' if ( pc != NULL ) *pc = 0; strcat ( outfilename, ".rawa" ); //extenstion .rawa } else strcpy ( outfilename, argv[3] ); if ( ( fpo = fopen ( outfilename, "wb" ) ) == NULL ) { fprintf(stderr, "error opening file %s for output\n", outfilename ); exit ( 9 ); }
unsigned audioFrames = 0; totalBytes = 0; if (AVI_seek_start( avi )) { fprintf(stderr, "Audio bad seek 2: %s\n", AVI_strerror()); exit( 10 ); } if (AVI_set_audio_position( avi, 0)) { fprintf(stderr, "Audio bad seek 3: %s\n", AVI_strerror()); exit( 11 ); } while ((numBytes = AVI_read_audio( avi, (char *)buf, 8192)) > 0) { totalBytes += numBytes; if (fwrite ( buf, 1, numBytes, fpo ) != numBytes) { fprintf(stderr, "error writing %s\n", outfilename ); break; } audioFrames++; printf("frame %d - length %u total %u\r", audioFrames, numBytes, totalBytes); } printf("\n"); fclose ( fpo );
return 0; }
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,
g++ -o avplayer avplayer.cpp avilib.c -I/usr/include -L/usr/local/lib -lSDL
/*
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;
}
|
|