Sample program for converting RGB to YCbCr and vice versa.
/*
typlayer.cpp
Hard-coded program to illustrate the conversion from RGB to 4:2:0 YCbCr and vice vers.
Assume that our video is 320x240 8-bit data.
It converts raw RGB data of "sample_video.raw" to YCbCr data saved in "sample_video.ycb" and plays the video.
Or if an argument "y" is provided, it reads YCbCr data from "sample_video.ycb", converts the data to RGB
and plays the video.
See 'http://www.webkinesia.com/games/vcompress.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"
short ycb_flag = 0; //0: read RGB file, convert to YCbCr; 1: read YCbCr file 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;
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 ( !ycb_flag ) //convert RGB data to YCbCr and save it
encode (buf[head%4],fpo); //encode and save there
//if ycb_flag == 1, we just play the data in buf[]
screen->pixels = buf[head%4];
SDL_UpdateRect ( screen, 0, 0, 0, 0 ); //update whole screen
head++;
SDL_Delay ( 40 ); //play ~ 20 fps, should use timer to calculate exact delay
} //while
return 0;
}
/*
Get YCbCr data from file pointed by fpi. 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;
n = 0;
//read data from file and put them in 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
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++ ) {
if ( ( c = getc ( fpi )) == EOF) //read one byte
break;
*( py + j ) = c; //put it in YCbCr_MACRO struct
n++;
} //for j
} //for i
} //for block
//now do that for 8x8 Cb block
k = 0;
for ( i = 0; i < 8; ++i ) {
for ( j = 0; j < 8; ++j ) {
if ( ( c = getc ( fpi )) == EOF )
break;
if ( c > 127 ) c -= 256; //convert from 8-bit unsigned to sign
ycbcr_macro->Cb[k++] = c;
n++;
}
}
//now do that for 8x8 Cr block
k = 0;
for ( i = 0; i < 8; ++i ) {
for ( j = 0; j < 8; ++j ) {
if ( ( c = getc ( fpi )) == EOF )
break;
if ( c > 127 ) c -= 256; //convert from 8-bit unsigned to sing
ycbcr_macro->Cr[k++] = c;
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];
// n = fread ( buf[tail%4], frameSize, 1, fp );
if ( !ycb_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], "y" ) == 0 ) ) { //play from YCbCr file
ycb_flag = 1;
fpi = fopen ( "sample_video.ybr", "rb" ); //input of YCbCr data
if ( fpi == NULL ) {
printf("\nError opening file \"sample_video.ybr\" for input\n" );
return 1;
}
} else { //play from RGB file and convert to YCbCr
ycb_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.ybr", "wb" ); //output of YCbCr data
if ( fpo == NULL ) {
printf("\nError opening file \"sample_video.ybr\" 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;
}