/* * Convert basic RGB pixmap into YUV420 format (e.g. used by theora_encoder). * * Only works properly when the RGB has even number of rows and columns because * 2x2 cells get colour calculated into a single colour. * * It only outputs one frame, need to run once for each frame being converted. */ #include #include #include int fps = 30; /* * The chroma data is always half resolution in both X and Y directions, * thus 1/4 of the bulk of the full data. This is the most primitive compression * method used by MPEG and OGG/THEORA formats and originally comes from television * broadcast which did the same thing in analog. */ int chroma_packdown( unsigned char *data, unsigned int width, unsigned int height ) { unsigned int x, y; unsigned char *w; w = data; /* This is where the output gets written to */ for( y = 0; y < height; y += 2 ) { for( x = 0; x < width; x += 2 ) /* Visit columns in pairs */ { unsigned int t; t = data[ x ] + 1; t += data[ x + 1 ]; t += data[ width + x ]; t += data[ width + x + 1 ]; t /= 4; /* Average a block of four cells */ if( t > 255 ) t = 255; *w++ = t; /* We depend on the output running behind the input */ // fprintf( stderr, "x=%d y=%d t=%d\n", x, y, t ); } data += width + width; /* Skip two rows at a time */ } } int main( int argc, char *argv[]) { struct pam inpam; tuple *tuplerow; unsigned int row; unsigned int grand_total = 0; unsigned char *tmp_Y; unsigned char *tmp_Cb; unsigned char *tmp_Cr; unsigned char *p_Y; unsigned char *p_Cb; unsigned char *p_Cr; pnm_init( &argc, argv ); pnm_readpaminit( stdin, &inpam, PAM_STRUCT_SIZE( tuple_type )); tuplerow = pnm_allocpamrow( &inpam ); if( inpam.width & 0x01 ) { fprintf( stderr, "ERROR: Must be even width (not %u)\n", inpam.width ); exit( -1 ); } if( inpam.height & 0x01 ) { fprintf( stderr, "ERROR: Must be even height (not %u)\n", inpam.height ); exit( -2 ); } if( inpam.depth != 3 ) { fprintf( stderr, "ERROR: Only RGB data is supported\n" ); exit( -3 ); } p_Y = tmp_Y = malloc( inpam.width * inpam.height ); p_Cb = tmp_Cb = malloc( inpam.width * inpam.height ); p_Cr = tmp_Cr = malloc( inpam.width * inpam.height ); if( p_Y == 0 || p_Cb == 0 || p_Cr == 0 ) { fprintf( stderr, "ERROR: Out of memory\n" ); exit( -4 ); } // Only want one header for the whole stream, not handled by this program. // printf( "YUV4MPEG2 W%d H%d F%d:1 Ip A1:1 C422\n", inpam.width, inpam.height, fps ); for( row = 0; row < inpam.height; row++ ) { unsigned int column; pnm_readpamrow( &inpam, tuplerow ); for( column = 0; column < inpam.width; column++ ) { /* * We only support 3 planes... must be R, G, B */ int R = tuplerow[ column ][ 0 ]; int G = tuplerow[ column ][ 1 ]; int B = tuplerow[ column ][ 2 ]; double tmp; tmp = floor( 16.5 + ( 65.738 * R + 129.057 * G + 25.064 * B ) / 256 ); if( tmp < 0 ) { tmp = 0; } if( tmp > 255 ) { tmp = 255; } p_Y[ column ] = tmp; tmp = floor( 128.5 + ( -37.945 * R - 74.494 * G + 112.439 * B ) / 256 ); if( tmp < 0 ) { tmp = 0; } if( tmp > 255 ) { tmp = 255; } // fprintf( stderr, "row=%d col=%d R=%d G=%d B=%d Cb=%f\n", row, column, R, G, B, tmp ); p_Cb[ column ] = tmp; tmp = floor( 128.5 + ( 112.439 * R - 94.154 * G - 18.285 * B ) / 256 ); if( tmp < 0 ) { tmp = 0; } if( tmp > 255 ) { tmp = 255; } p_Cr[ column ] = tmp; } p_Y += inpam.width; p_Cb += inpam.width; p_Cr += inpam.width; } pnm_freepamrow( tuplerow ); chroma_packdown( tmp_Cb, inpam.width, inpam.height ); chroma_packdown( tmp_Cr, inpam.width, inpam.height ); fprintf( stderr, "Write frame: %u x %u (%u %u %u)\n", inpam.width, inpam.height, inpam.width * inpam.height, inpam.width * inpam.height / 4, inpam.width * inpam.height / 4 ); fflush( stderr ); printf( "FRAME\n" ); fwrite( tmp_Y, 1, inpam.width * inpam.height, stdout ); fwrite( tmp_Cb, 1, inpam.width * inpam.height / 4, stdout ); fwrite( tmp_Cr, 1, inpam.width * inpam.height / 4, stdout ); fflush( stdout ); return( 0 ); }