Sample program for converting RGB to YCbCr to DCT coefficients and vice versa.
/*
dctplayer.cpp
Hard-coded program to illustrate the conversion from RGB to 4:2:0 YCbCr then to DCT coefficients
and vice versa.
Assume that our video is 320x240 8-bit data.
It converts raw RGB data of "sample_video.raw" to DCT coefficients to be saved in "sample_video.dct"
and plays the video.
Or if an argument "d" is provided, it reads DCT coefficients from "sample_video.dct", applies
IDCT to recover the YCbCr sample blocks, converts YCbCr to RGB data
and plays the video.
See 'http://www.webkinesia.com/games/vcompress4.php'
*/
#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 "common.h"
#include "encode.h"
#include "rgb_ybr.h"
#include "dct_video.h"
short dct_flag = 0; //0: read RGB file, perform YCbCr and DCT transforms, and play video;
//1: read DCT coefficients, reverse conversions to play video
FILE *fpi, *fpo;
short width = 320; //image width
short height = 240; //image height
unsigned long frameSize; //size of one frame
unsigned long head=0, tail=0; //for synchronization of producer and consumer threads
bool quit = false; //quit program if set to true
char *buf[4]; //ring buffer to hold four frames of data
//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 = 50; //frame time in ms, assume fps = 20
while ( !quit || head < tail ) {
if ( head >= tail ) { //buffer empty (data not available yet )
SDL_Delay ( 30 ); //sleep for 30 ms
continue;
}
//consumes the data
if ( !dct_flag ) //convert RGB data to YCbCr and save it
encode (buf[head%4],fpo); //encode and save there
//if dct_flag == 1, we just play the data in buf[]
screen->pixels = buf[head%4];
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;
}
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
}
/*
Convert a YCbCr frame to RGB frame.
*/
int decode_ybrFrame ( char *image )
{
short r, row, col, i, j, k, block, *py;
int n;
RGB *p, macro16x16[256];
YCbCr_MACRO ycbcr_macro;
for ( row = 0; row < height; row += 16 ) {
for ( col = 0; col < width; col += 16 ) {
n = get_ybrblocks( &ycbcr_macro );
if ( n <= 0 ) return n;
ycbcr2macroblock( &ycbcr_macro, macro16x16 );
p = ( RGB *) image + ( row * width + col ); //points to beginning of macroblock
r = 0;
for ( i = 0; i < 16; ++i ) {
for ( j = 0; j < 16; ++j ) {
*p++ = macro16x16[r++];
}
p += ( width - 16 ); //points to next row within macroblock
}
} //for col
} //for row
return n;
}
int decode_frame ( char *image )
{
int n;
n = fread ( image, 1, frameSize, fpi );
return n;
}
//Producer
int decoder ( void *data )
{
int n;
char *image;
while ( !quit ) {
if ( tail >= head + 4 ) { //buffer full
SDL_Delay ( 30 ); //sleep for 30 ms ( not the best method, better use
//semaphore, let consumer wake you up
continue;
}
//produce data
image = buf[tail%4];
if ( !dct_flag )
n = decode_frame ( image );//decoded data put in image[]
else
n = decode_ybrFrame ( image );
if ( n <= 0 )
quit = true;
else
tail++;
} //while
return 0;
}
int main( int argc, char *argv[] )
{
SDL_Surface *screen;
frameSize = width * height * 3;
SDL_Thread *producer, *consumer;
SDL_Event event;
int status;
char *key;
if ( argc > 1 && ( strcmp ( argv[1], "d" ) == 0 ) ) { //play from DCT coef file
dct_flag = 1;
fpi = fopen ( "sample_video.dct", "rb" ); //input of DCT data
if ( fpi == NULL ) {
printf("\nError opening file \"sample_video.dct\" for input\n" );
return 1;
}
} else { //play from RGB file and convert to YCbCr
dct_flag = 0;
fpi = fopen ( "sample_video.raw", "rb" ); //input of RGB data
if ( fpi == NULL ) {
printf("\nError opening file \"sample_video.raw\" for input\n" );
return 1;
}
fpo = fopen ( "sample_video.dct", "wb" ); //output of DCT coeff data
if ( fpo == NULL ) {
printf("\nError opening file \"sample_video.dct\" for output\n" );
return 1;
}
}
//initialize video system
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
exit(1);
}
//ensure SDL_Quit is called when the program exits
atexit(SDL_Quit);
//set video mode of 320 x 240 with 24-bit pixels
screen = SDL_SetVideoMode(width, height, 24, SDL_SWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, "Unable to set 320x240 video: %s\n", SDL_GetError());
exit(1);
}
for ( int i = 0; i < 4; ++i ) {
buf[i] = ( char * ) malloc ( frameSize );
assert ( buf[i] );
}
head = tail = 0;
consumer = SDL_CreateThread ( player, screen );
producer = SDL_CreateThread ( decoder, ( void * ) "decdoing" );
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
}
SDL_WaitThread ( consumer, NULL ); //wait for child threads to finish
SDL_WaitThread ( producer, NULL );
return 0;
}