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

An Introduction to Video Compression in C/C++ now available at Amazon

@Copyright by Fore June, 2006

Video Compression

1 2 3 4 5 6 7 8
  1. DCT and IDCT for Video Frames

    In the previous few sections, we have discussed DCT and IDCT and their practical implementations. We also presented in Section 8 the complete codes to convert raw RGB data to 8x8 YCbCr sample blocks and vice-versa. Now, lets go one step further. We apply DCT to each of the 8x8 sample blocks and save the DCT coefficients in the file, sample_video.dct. Our program should be able to reverse the steps by reading in the DCT coefficients from sample_video.dct, applying IDCT to recover the YCbCr sample blocks, and converting them to RGB data for display.

    Lets incorporate the DCT in the function encode() of encode.cpp presented in Section 8. Since we have already developed the 8x8 DCT and IDCT functions in Section 14, this step becomes simple. We rename the function save_ybrblocks() of encode.cpp as save_dctcoefs() to reflect the changes of saving DCT coefficients rather than YCbCr sample blocks; also, in the function, whenever we obtain an 8x8 sample block, we apply dct() of dct_viedo.cpp presented in Section 14 to obtain the coefficients which are then saved in the file sample_video.dct. Incorporating these functionalities is straight forward. The modified encode.cpp is listed below.

    /*
      encode.cpp
      Contains functions to convert an RGB frame to YCbCr to DCT coeffs and to save the converted data.
      See 'http://www.webkinesia.com/games/vcompress4.php'
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include "common.h"
    #include "rgb_ybr.h"
    #include "dct_video.h"
    
    extern short width;		//image width
    extern short height;		//image height
    
    //save one DCT block; this is just for testing and learning. Eventually, we won't need this
    void save_dct_block ( int *Y, FILE *fpo )
    {
      short temp[64];
      for ( int i = 0; i < 64; ++i )
        temp[i] = ( short ) Y[i];	//convert to short  ( 2 bytes );
      fwrite ( temp, 2, 64, fpo );	//save the block
    }
    
    //save DCT coeffs for one  macroblock.
    void save_dctcoefs( YCbCr_MACRO *ycbcr_macro, FILE *fpo )
    {
      short block, i, j, k, *py;
      int X[64], Y[64];			//for DCT transform
     
      //save DCT coefs for four 8x8 Y sample blocks
      for ( block = 0; block < 4; block++ ) {
        if ( block < 2 )
          py = ( short * ) &ycbcr_macro->Y + 8*block;	//points to beginning of block
        else
          py = (short *)&ycbcr_macro->Y + 128 + 8*(block-2);//points to beginning of block
        k = 0;
        for ( i = 0; i < 8; i++ ) {		//one sample-block
          if ( i > 0 ) py += 16;		//advance py by 16 ( length of one row )
          for ( j = 0; j < 8; j++ ) {
    	X[k++] = (int) *( py + j );
          }
        }
        dct ( X, Y );			//perform DCT for the sample block
        save_dct_block ( Y, fpo );		//save DCT coefs for Y block
      }
      
      //save one 8x8 Cb block
      k = 0;
      for ( i = 0; i < 8; ++i ) {
        for ( j = 0; j < 8; ++j ) {
          X[k] = (int) ycbcr_macro->Cb[k];
          k++;
        }
      }
      dct ( X, Y );				//perform DCT for the sample block
      save_dct_block ( Y, fpo );		//save DCT coefs for Cb block
    
      //save one 8x8 Cr block
      k = 0; 
      for ( i = 0; i < 8; ++i ) {
        for ( j = 0; j < 8; ++j ) {
          X[k] = (int) ycbcr_macro->Cr[k];
          k++;
        }
      } 
      dct ( X, Y );				//perform DCT for the sample block
      save_dct_block ( Y, fpo );		//save DCT coefs for Cr block
    }
    
    /*
      Convert RGB to YCbCr to DCT coefs and save the converted data.
      width, height are global variables of image.
    */
    void encode ( char *image, FILE *fpo )
    {
      short row, col, i, j, r;
      RGB macro16x16[256];          //16x16 pixel macroblock; assume 24-bit for each RGB pixel
      YCbCr_MACRO ycbcr_macro;      //macroblock for YCbCr samples
      RGB *p;			//pointer to an RGB pixel
      static int nframe = 0;
      for ( row = 0; row < height; row += 16 ) {
        for ( col = 0; col < width; col += 16 ) {
          p = ( RGB *) image  +  ( row * width + col );	//points to beginning of macroblock
          r = 0;						//note pointer arithmetic
          for ( i = 0; i < 16; ++i ) {
    	for ( j = 0; j < 16; ++j ) {
    	  macro16x16[r++] = (RGB) *p++;
    	}
    	p += ( width - 16 );	//points to next row within macroblock ( note pointer arithmetic )
          }
          macroblock2ycbcr ( macro16x16,  &ycbcr_macro );	//convert from RGB to YCbCr
          save_dctcoefs( &ycbcr_macro, fpo );		//save one YCbCr macroblock
        } //for col
      } //for row
    }
    

    We also rename the file typlayer.cpp of Section 8 to dctplayer.cpp and modify it to include IDCT operations. Actually, there's not much that we need to change in this file. The only major change is in the function get_ybrblocks(). We add a new function called get_dct_block() to read in one block ( 64 samples ) of DCT coefficients from the file sample_video.dct. This function get_ybrblocks() still returns a macroblock which consists of four 8x8 Y-samples, one 8x8 Cb-samples, and one 8x8 Cr-samples; it first calls get_dct_block() to obtain a block of 8x8 DCT coefficients and then call idct() to convert the DCT coefficients to a block of YCbCr samples. The modified code is listed below.

    int get_dct_block ( int Y[] )           //read in one DCT block
    {
      short temp[64];
    
      if ( !fread ( temp, 2, 64, fpi ) )
         return 0;
    
      for ( int i = 0; i < 64; ++i )
        Y[i] = ( int ) temp[i];
    
      return 1;
    }
    /*
      Get DCT data from file pointed by fpi; convert DCT coefficients to YCbcr and put the
      four 8x8 Y sample blocks, one 8x8 Cb sample block and one 8x8 Cr sample block into a
      struct of YCbCr_MACRO.
      Return: number of bytes read from file and put in YCbCr_MACRO struct.
    */
    int get_ybrblocks( YCbCr_MACRO *ycbcr_macro )
    {
      short r, row, col, i, j, k, n, block, *py;
      short c;
      int   Y[64], X[64];           //for IDCT
      //short Y[64], X[64];
    
      n = 0;
      //read data from file, perform IDCT and put them in four 8x8 Y sample blocks
      for ( block = 0; block < 4; block++ ) {
      if ( !get_dct_block ( Y ) )   //read in one DCT block
          return 0;
        idct ( Y, X );              //perform IDCT, output in X
        k = 0;
        if ( block < 2 )
          py = ( short * ) &ycbcr_macro->Y + 8*block;               //points to beginning of block
        else
          py = (short *)&ycbcr_macro->Y + 128 + 8*(block-2);        //points to beginning of block
        for ( i = 0; i < 8; i++ ) {         //one sample-block
          if ( i > 0 ) py += 16;            //advance py by 16 ( length of one row of macroblock )
          for ( j = 0; j < 8; j++ ) {
            *(py+j) = X[k++];               //put sample value in macroblock
             n++;
          } //for j
        } //for i
      } //for block
    
      if ( !get_dct_block ( Y ) )           //read in one DCT block
        return 0;
      idct (Y, X );                         //perform IDCT, output in X
      k = 0;
      for ( i = 0; i < 8; ++i ) {
        for ( j = 0; j < 8; ++j ) {
          ycbcr_macro->Cb[k] = X[k];        //put Cb sample value in macro block
          k++;
          n++;
        }
      }
      //now do that for 8x8 Cr block
      if ( !get_dct_block ( Y ) )           //read in one DCT block
        return 0;
      idct (Y, X );                         //perform IDCT, output in X
      k = 0;
      for ( i = 0; i < 8; ++i ) {
        for ( j = 0; j < 8; ++j ) {
          ycbcr_macro->Cr[k] = X[k];        //put Cr sample value in macro block
          k++;
          n++;
        }
      }
      return n;                             //number of bytes read
    }
    

    For your convenience, we list below all the programs required to generate the executable dctplayer. A Makefile and sample_video.raw are provided. Again, none of the files is zipped. So when you click on sample_video.raw, simply save it with filename sample_video.raw without any .zip extension. Other files can be copied using copy-and-paste method. Put all the files in one directory and execute make to obtain the executable dctplayer. You can run it by the command,

    ./dctplayer which will read the data from sample_video.raw, play the video and convert the data to blocks of DCT coefficients, which will be saved in the file sample_video.dct. If you want to play from the saved DCT coefficients in sample_video.dct, you can run the command ./dctplayer d which will read in DCT coefficients from sample_video.dct, apply IDCT to convert them to YCbCr sample blocks, then convert the YCbCr sample blocks to RGB data and play the RGB video. common.h
    rgb_ybr.h
    dct_video.h
    encode.h
    dctplayer.cpp
    encode.cpp
    dct_video.cpp
    rgb_ybr.cpp
    Makefile
    sample_video.raw

    <<Prev   Next >>