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

Raw Video Player

  1. Introduction

    In more advanced games, to enhance the atmosphere of gaming you may need to play a video clip to tell players a story related to the game or to illustrate some game features. The video clip could be obtained from shooting some real sceneries or from computer graphics animation. In this Chapter, we shall discuss how to play a video using data saved in the raw format of a file. We shall discuss the handling of data with compression in a later Chapter.

  2. A Simple Raw Video Player

    Actually, to play a raw video clip ( one without compression ) using SDL may be a lot simpler than you thought. All you need to do is to read in the raw data from a file and use SDL_Flip() or SDL_UpdateRect() to blit them on the screen in a controlled time period. For the sake of illustrating the principles and avoiding the burden of handling overheads, let us for instance consider that we already have a raw data file called "sample_video.raw" that saves the raw data of a video clip. Suppose we already know the width and height of the image of each frame. Let us assume that the image size is 320 x 240 and we want to play the video at a rate of 20 fps ( frames per second ). We also assume that an image is saved in RGB format with each color represented by an eight bit number (i.e. 24 bits / pixel ).

    The following program, player.cpp shows how to play such a video clip. It is particularly simple ( everything has been hard-coded for clarity and simplicity to illustrate the main concepts ). It reads one frame of video data from the file sample_video.raw into a buffer at one time. It waits for 50 ms before using SDL_UpdateRect() to blit the data on the screen. The 50 ms delay gives us a frame rate of 20 fps ( 1/20 s = 50 ms ); of course this is a very rough estimate as the operation of reading data also consumes time and a more practical delay should be less than 50 ms in order to achieve a frame rate of 20 fps. Interested readers may click here to obtain a sample file of sample_video.raw' to test the program. ( Simply ignore the .zip extension and save the file with file name "sample_video.raw'. )

  3. A Multi-threaded Raw Video Player

    Though the above program, player.cpp can play raw video data, it does not handle data in an effective way. It sits in a main loop to read in the video data and blit them to the screen. In practice, a player has to handle various tasks besides blitting data on the screen. It may have to decode the data or to process the audio; programs like player.cpp tangle all the tasks together and one needs to worry about the coordination between various tasks. A better way to do this is to use multi-threads that we have discussed in an earlier chapter to synchronize the tasks. In this way, playing data ( sending data to screen ) can be cleanly separated from other tasks. The following section describes how to develop a multi-threaded program named tplayer.cpp to play video data.

    In its simplest form, besides the main(), our multi-threaded player needs two threads, one for sending data to the screen and one for 'decoding' data ( at this moment, 'decoding' is simply reading reading from the file ); suppose we name these threads player() and decoder() respectively. This becomes a classical producer-consumer problem. Here, decoder() is the producer which produces resources ( video data ) and player() is the consumer which consumes resources ( video data ).

    Producer-Consumer Problem

    In the producer-consumer problem, to allow producer and consumer threads to run concurrently, we must have available a buffer of items that can be filled by the producer and emptied by the consumer. A producer can produce zero or more items while the consumer is consuming an item; the number of items that can be produced or consumed depend on the production rate as well as the consumption rate and other factors that may influence the production and consumption operations. The producer and consumer must be synchronized, so that the consumer does not try to consume an item that has not yet been produced and the producer suspends production when the buffer is full as there will not be any space to hold the produced item. Therefore, the producer ( decoder() ) must wait when the buffer is full and the consumer ( player() ) must wait when the buffer is empty.

    Typically, when the buffer is empty, the consumer goes to sleep and when the producer has finished producing an item and put it in the buffer, it is responsible to wake up the consumer; on the other hand, when the buffer is full, the producer goes to sleep and the consumer is responsible to wake up the producer after it has consumed an item. These can be implemented using the SDL semaphores that we have discussed in Chapter SDL Threads. However, to slightly simplify our implementation of tplayer.cpp, each thread is responsible to wake up by itself after sleeping ( SDL_Delay() ) for a certain period time and check the buffer condition again. In our implementation, we create a buffer that can hold four frames of video data:

    This buffer buf acts as a circular queue. We maintain two pointers, head and tail. Data are entered into the queue at the 'rear' where the tail points at and are deleted ( consumed ) at the front where the head points at. Each time, an item is produced, tail is advanced by 1 and when an item is produced, head is advanced. When head equals tail, indicating that the buffer is empty, the player() thread must wait:

    When tail is 4 ahead of head, it implies that the buffer is full and the decoder() must wait:

    Modulo 4 is taken on head and tail to point to the actual buffer location.

    Another improvement made in tplayer.cpp over player.cpp is that it uses SDL_GetTicks() to give a much better estimate of the time delay between two frames:

    The complete code of tplayer.cpp is listed below. Again, you can try it with sample_video.raw'. You may quit the program by pressing the 'ESC' key.