On this tutorial we will explain how to make une simple audio/video player using the waave library. The completed source code can be found here. But to follow the player's structure we will start with a SDL base program. To compile the source do :
We start by setting a global variable to store the input file's name. It will be given by the user in first command argument.
/* input file */ char* filename;
Remain to parse given arguments :
int main(int argc, char** argv) { if(argc != 2){ printf("usage : %s vidfilename\n", argv[0]); return 0; } /* parse arg */ filename = argv[1];
In the waave engine the video streams need to be associated with a streaming object that describe the methods to display the video frames. So we need two global variables : one for the stream handle and one for the corresponding streaming object.
/* playing stream */ WVStream* stream = NULL; WVStreamingObject* streamObj = NULL;
After the SDL window was build we start initializing the waave engine with audio and video support and load the input file :
/**************/ /* init waave */ /**************/ /* start waave and load file */ WV_waaveInit(WAAVE_INIT_AUDIO|WAAVE_INIT_VIDEO); stream = WV_getStream(filename);
To know if we need to build a streaming object we check if the stream contain video data. If it is the case we construct a standard streaming object provided with the library that will stream the video frame on the screen surface. Don't forget to associate it with the stream.
/* build a streaming object for the screen surface */ int streamType = WV_getStreamType(stream); if(streamType == WV_STREAM_TYPE_VIDEO || streamType == WV_STREAM_TYPE_AUDIOVIDEO){ streamObj = WV_getStreamOverlayObj(screen, NULL); WV_setStreamingMethod(stream, streamObj); }
We can now load the stream in the waave engine.
/* load the stream */ WV_loadStream(stream);
All the video refreshing operation need to be done by the main thread. So the waave engine doesn't do it for you but just send a WV_REFRESH_EVENT when displaying a video frame is needed. To accomplish it you just need to call the WV_refreshVideoFrame() function, all the stream specific operations are specified into the streaming object. So when we process events :
/* sdl events */ void processEvents(void) { SDL_Event event; while( SDL_PollEvent( &event ) ) { switch( event.type ) { case SDL_KEYDOWN: keyPressed( &event.key.keysym ); break; case SDL_VIDEORESIZE: if(!fullscreenFlag){ winWidth = event.resize.w; winHeight = event.resize.h; screen = SDL_SetVideoMode(winWidth, winHeight, bpp, flags); if(streamObj) WV_resetStreamOverlayOutput(streamObj, screen, NULL); } break; case WV_REFRESH_EVENT: WV_refreshVideoFrame(&event); break;
Now if we send the play command to the stream the video can be displayed ! We put the command just before entering in the main loop.
/* at start the stream is paused */ /* so we launch playback */ WV_playStream(stream); /*****************/ /* display loop */ /*****************/ while( 1 ) { processEvents( ); } return(0);
We have now a working video player. You can try to compile it now. To continue will we add the classic user commands : play, pause ...
At this time the opened stream, the streaming object and the waave engine itself are not closed when exiting. So we write a simple function that carrefully free each of them :
/* close sequence */ void closePlayer(void) { /* close the opened stream. not mandatory */ if(stream) WV_closeStream(stream); /* close the streaming object */ if(streamObj) WV_freeStreamOverlayObj(streamObj); /* close the waave engine */ WV_waaveClose(); /* close sdl */ SDL_Quit(); exit(0); }
So we change each program exit. First the SDL_QUIT event :
case SDL_QUIT: closePlayer(); break;
Next when the user press the escape key :
case SDLK_ESCAPE: closePlayer(); //close break;
When the SDL window is resized, a new surface is created with the SDL_SetVideoMode() function. So our streaming object become outdated and we need to reset the new target surface. This is done in one step with WV_resetStreamOverlayOutput(). The role of the fullscreen flag we be explained below.
case SDL_VIDEORESIZE: if(!fullscreenFlag){ winWidth = event.resize.w; winHeight = event.resize.h; screen = SDL_SetVideoMode(winWidth, winHeight, bpp, flags); if(streamObj) WV_resetStreamOverlayOutput(streamObj, screen, NULL); } break;
We check the presence of the streaming object in the case we play an audio stream. To make the fullscreen command we apply the same method. We add a global variable to store the fullscreen status :
/* window surface */ int flags; SDL_Surface* screen; int winWidth; int winHeight; int fullscreenFlag = 0;
Next we toogle fullscreen when the user press the "f" key. The screenWidth and screenHeight variables give the screen size and the winWidthQ and winHeight keep the SDL window size.
case SDLK_f: if(!fullscreenFlag){ //toggle fullscreen fullscreenFlag = 1; SDL_ShowCursor(SDL_DISABLE); screen = SDL_SetVideoMode(screenWidth, screenHeight, bpp, flags ^ SDL_FULLSCREEN); if(streamObj) WV_resetStreamOverlayOutput(streamObj, screen, NULL); } else{ fullscreenFlag = 0; SDL_ShowCursor(SDL_ENABLE); screen = SDL_SetVideoMode(winWidth, winHeight, bpp, flags); if(streamObj) WV_resetStreamOverlayOutput(streamObj, screen, NULL); } break;
We start by making a simple play/pause command when the user press the space/backspace keys. We just have to call the corresponding waave functions :
case SDLK_SPACE: WV_playStream(stream); //play break; case SDLK_BACKSPACE: WV_pauseStream(stream); //pause break;
We do the same thing to encrease/decrease the stream volume with the +/- keys of the numeric pad :
case SDLK_KP_PLUS: WV_shiftDBVolume(stream, +1); //encrease volume break; case SDLK_KP_MINUS: WV_shiftDBVolume(stream, -1); //decrease volume break;
To seek in the stream will use four commands. Left and right to do a 10 seconds seek and up and down to do a 60second seek.
/********/ /* seek */ /********/ uint32_t seekShift; //will be converted in milliseconds int seekDirection; case SDLK_RIGHT: seekShift = 10; seekDirection = WV_SEEK_FORWARD; goto do_seek; case SDLK_LEFT: seekShift = 10; seekDirection = WV_SEEK_BACKWARD; goto do_seek; case SDLK_UP: seekShift = 60; seekDirection = WV_SEEK_FORWARD; goto do_seek; case SDLK_DOWN: seekShift = 60; seekDirection = WV_SEEK_BACKWARD; goto do_seek; do_seek: seekShift *= 1000; WV_rseekStream(stream, seekShift, seekDirection); break;
When the stream end is reached the waave engien send a WV_EOF_EVENT sdl event. The to close the player when playback is terminated we just have to call our closePlayer() function when we receive the event.
case WV_EOF_EVENT: closePlayer(); break;
Everything works ! Now you can make a player user interface, draw a playback progress bar, make a volume trigger ...