| 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 |
Video Compression
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
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,