Added I420 (YUV 4:2:0) conversion option to avi recorder.
This commit is contained in:
parent
b9f6bf281f
commit
ce554b5e7b
|
@ -21,6 +21,7 @@ static int abufTail = 0;
|
|||
static int abufSize = 0;
|
||||
static uint32_t *rawVideoBuf = NULL;
|
||||
static int16_t *rawAudioBuf = NULL;
|
||||
static int videoFormat = 1;
|
||||
//**************************************************************************************
|
||||
|
||||
static void convertRgb_32_to_24( const unsigned char *src, unsigned char *dest, int w, int h, int nPix )
|
||||
|
@ -47,6 +48,104 @@ static void convertRgb_32_to_24( const unsigned char *src, unsigned char *dest,
|
|||
}
|
||||
}
|
||||
//**************************************************************************************
|
||||
/* For RGB2YUV: */
|
||||
|
||||
static const int RGB2YUV_SHIFT = 15; /* highest value where [RGB][YUV] fit in signed short */
|
||||
|
||||
static const int RY = 8414; // ((int)(( 65.738/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int RV = 14392; // ((int)((112.439/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int RU = -4856; // ((int)((-37.945/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
|
||||
static const int GY = 16519; // ((int)((129.057/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int GV = -12051;// ((int)((-94.154/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int GU = -9534; // ((int)((-74.494/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
|
||||
static const int BY = 3208; // ((int)(( 25.064/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int BV = -2339; // ((int)((-18.285/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
static const int BU = 14392; // ((int)((112.439/256.0)*(1<<RGB2YUV_SHIFT)+0.5));
|
||||
|
||||
static const int Y_ADD = 16;
|
||||
static const int U_ADD = 128;
|
||||
static const int V_ADD = 128;
|
||||
|
||||
template<int PixStride>
|
||||
void Convert_4byte_To_I420Frame(const void* data, unsigned char* dest, unsigned npixels, unsigned width)
|
||||
{
|
||||
const unsigned char* src = (const unsigned char*) data;
|
||||
unsigned height = npixels / width;
|
||||
|
||||
unsigned pos = 0;
|
||||
unsigned ypos = 0;
|
||||
unsigned vpos = npixels;
|
||||
unsigned upos = vpos + npixels / 4;
|
||||
unsigned stride = width*PixStride;
|
||||
|
||||
/*fprintf(stderr, "npixels=%u, width=%u, height=%u, ypos=%u,upos=%u,vpos=%u",
|
||||
npixels,width,height, ypos,upos,vpos);*/
|
||||
|
||||
/* This function is based on code from x264 svn version 711 */
|
||||
/* TODO: Apply MMX optimization for 24-bit pixels */
|
||||
|
||||
for(unsigned y=0; y<height; y += 2)
|
||||
{
|
||||
for(unsigned x=0; x<width; x += 2)
|
||||
{
|
||||
//#ifdef __MMX__
|
||||
// if(PixStride == 4)
|
||||
// {
|
||||
// c64_MMX p0_1; p0_1.Get(&src[pos]); // two 32-bit pixels (4*8)
|
||||
// c64_MMX p2_3; p2_3.Get(&src[pos+stride]); // two 32-bit pixels
|
||||
|
||||
// pos += PixStride*2;
|
||||
//
|
||||
// Convert_I420_MMX_Common(p0_1, p2_3,
|
||||
// dest+ypos,
|
||||
// dest+ypos+width,
|
||||
// dest+upos++,
|
||||
// dest+vpos++);
|
||||
// }
|
||||
// else
|
||||
//#endif
|
||||
{
|
||||
int c[3], rgb[3][4];
|
||||
|
||||
/* luma */
|
||||
for(int n=0; n<3; ++n) c[n] = rgb[n][0] = src[pos + n];
|
||||
for(int n=0; n<3; ++n) c[n] += rgb[n][1] = src[pos + n + stride];
|
||||
pos += PixStride;
|
||||
|
||||
for(int n=0; n<3; ++n) c[n] += rgb[n][2] = src[pos + n];
|
||||
for(int n=0; n<3; ++n) c[n] += rgb[n][3] = src[pos + n + stride];
|
||||
pos += PixStride;
|
||||
|
||||
unsigned destpos[4] = { ypos, ypos+width, ypos+1, ypos+width+1 };
|
||||
for(int n=0; n<4; ++n)
|
||||
{
|
||||
dest[destpos[n]]
|
||||
= Y_ADD + ((RY * rgb[0][n]
|
||||
+ GY * rgb[1][n]
|
||||
+ BY * rgb[2][n]
|
||||
) >> RGB2YUV_SHIFT); // y
|
||||
}
|
||||
|
||||
dest[upos++] = (U_ADD + ((RU * c[0] + GU * c[1] + BU * c[2]) >> (RGB2YUV_SHIFT+2)) );
|
||||
dest[vpos++] = (V_ADD + ((RV * c[0] + GV * c[1] + BV * c[2]) >> (RGB2YUV_SHIFT+2)) );
|
||||
}
|
||||
|
||||
ypos += 2;
|
||||
}
|
||||
pos += stride;
|
||||
ypos += width;
|
||||
}
|
||||
|
||||
/*fprintf(stderr, ",yr=%u,ur=%u,vr=%u\n",
|
||||
ypos,upos,vpos);*/
|
||||
|
||||
//#ifdef __MMX__
|
||||
// MMX_clear();
|
||||
//#endif
|
||||
}
|
||||
//**************************************************************************************
|
||||
int aviRecordOpenFile( const char *filepath, int format, int width, int height )
|
||||
{
|
||||
char fourcc[8];
|
||||
|
@ -65,6 +164,11 @@ int aviRecordOpenFile( const char *filepath, int format, int width, int height )
|
|||
|
||||
memset( fourcc, 0, sizeof(fourcc) );
|
||||
|
||||
if ( videoFormat )
|
||||
{
|
||||
strcpy( fourcc, "I420");
|
||||
}
|
||||
|
||||
gwavi = new gwavi_t();
|
||||
|
||||
if ( gwavi->open( "/tmp/test.avi", nes_shm->video.ncol, nes_shm->video.nrow, fourcc, fps, &audioConfig ) )
|
||||
|
@ -227,9 +331,17 @@ void AviRecordDiskThread_t::run(void)
|
|||
|
||||
if ( numPixelsReady >= numPixels )
|
||||
{
|
||||
convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24,
|
||||
width, height, numPixels );
|
||||
gwavi->add_frame( rgb24, numPixels*3 );
|
||||
if ( videoFormat )
|
||||
{
|
||||
Convert_4byte_To_I420Frame<4>(videoOut,rgb24,numPixels,width);
|
||||
gwavi->add_frame( rgb24, (numPixels*3)/2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
convertRgb_32_to_24( (const unsigned char*)videoOut, rgb24,
|
||||
width, height, numPixels );
|
||||
gwavi->add_frame( rgb24, numPixels*3 );
|
||||
}
|
||||
|
||||
numPixelsReady = 0;
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ gwavi_t::gwavi_t(void)
|
|||
offsets_start = 0;
|
||||
offsets = 0;
|
||||
offset_count = 0;
|
||||
bits_per_pixel = 24;
|
||||
}
|
||||
|
||||
gwavi_t::~gwavi_t(void)
|
||||
|
@ -91,9 +92,16 @@ int
|
|||
gwavi_t::open(const char *filename, unsigned int width, unsigned int height,
|
||||
const char *fourcc, unsigned int fps, struct gwavi_audio_t *audio)
|
||||
{
|
||||
int size = 0;
|
||||
memset( this->fourcc, 0, sizeof(this->fourcc) );
|
||||
strcpy( this->fourcc, fourcc );
|
||||
|
||||
if (check_fourcc(fourcc) != 0)
|
||||
{
|
||||
(void)fprintf(stderr, "WARNING: given fourcc does not seem to "
|
||||
"be valid: %s\n", fourcc);
|
||||
}
|
||||
|
||||
if (fps < 1)
|
||||
{
|
||||
return -1;
|
||||
|
@ -118,18 +126,34 @@ gwavi_t::open(const char *filename, unsigned int width, unsigned int height,
|
|||
avi_header.data_streams = 1;
|
||||
}
|
||||
|
||||
if ( strcmp( fourcc, "I420" ) == 0 )
|
||||
{ // I420 YUV 4:2:0
|
||||
bits_per_pixel = 12;
|
||||
}
|
||||
else
|
||||
{ // Plain RGB24
|
||||
bits_per_pixel = 24;
|
||||
}
|
||||
size = (width * height * bits_per_pixel);
|
||||
|
||||
if ( (size % 8) != 0 )
|
||||
{
|
||||
printf("Warning: Video Buffer Size not on an 8 bit boundary: %ix%i:%i\n", width, height, bits_per_pixel);
|
||||
}
|
||||
size = size / 8;
|
||||
|
||||
/* this field gets updated when calling gwavi_close() */
|
||||
avi_header.number_of_frames = 0;
|
||||
avi_header.width = width;
|
||||
avi_header.height = height;
|
||||
avi_header.buffer_size = (width * height * 3);
|
||||
avi_header.buffer_size = size;
|
||||
|
||||
/* set stream header */
|
||||
(void)strcpy(stream_header_v.data_type, "vids");
|
||||
(void)memcpy(stream_header_v.codec, fourcc, 4);
|
||||
stream_header_v.time_scale = 1;
|
||||
stream_header_v.data_rate = fps;
|
||||
stream_header_v.buffer_size = (width * height * 3);
|
||||
stream_header_v.buffer_size = size;
|
||||
stream_header_v.data_length = 0;
|
||||
|
||||
/* set stream format */
|
||||
|
@ -137,13 +161,13 @@ gwavi_t::open(const char *filename, unsigned int width, unsigned int height,
|
|||
stream_format_v.width = width;
|
||||
stream_format_v.height = height;
|
||||
stream_format_v.num_planes = 1;
|
||||
stream_format_v.bits_per_pixel = 24;
|
||||
stream_format_v.bits_per_pixel = bits_per_pixel;
|
||||
stream_format_v.compression_type =
|
||||
((unsigned int)fourcc[3] << 24) +
|
||||
((unsigned int)fourcc[2] << 16) +
|
||||
((unsigned int)fourcc[1] << 8) +
|
||||
((unsigned int)fourcc[0]);
|
||||
stream_format_v.image_size = width * height * 3;
|
||||
stream_format_v.image_size = size;
|
||||
stream_format_v.colors_used = 0;
|
||||
stream_format_v.colors_important = 0;
|
||||
|
||||
|
@ -467,8 +491,12 @@ int
|
|||
gwavi_t::set_codec( const char *fourcc)
|
||||
{
|
||||
if (check_fourcc(fourcc) != 0)
|
||||
{
|
||||
(void)fprintf(stderr, "WARNING: given fourcc does not seem to "
|
||||
"be valid: %s\n", fourcc);
|
||||
}
|
||||
memset( this->fourcc, 0, sizeof(this->fourcc) );
|
||||
strcpy( this->fourcc, fourcc );
|
||||
|
||||
memcpy(stream_header_v.codec, fourcc, 4);
|
||||
stream_format_v.compression_type =
|
||||
|
@ -495,7 +523,7 @@ gwavi_t::set_codec( const char *fourcc)
|
|||
int
|
||||
gwavi_t::set_size( unsigned int width, unsigned int height)
|
||||
{
|
||||
unsigned int size = (width * height * 3);
|
||||
unsigned int size = (width * height * bits_per_pixel) / 8;
|
||||
|
||||
avi_header.data_rate = size;
|
||||
avi_header.width = width;
|
||||
|
|
|
@ -146,6 +146,8 @@ class gwavi_t
|
|||
long offsets_start;
|
||||
unsigned int *offsets;
|
||||
int offset_count;
|
||||
int bits_per_pixel;
|
||||
char fourcc[8];
|
||||
|
||||
// helper functions
|
||||
int write_avi_header(FILE *out, struct gwavi_header_t *avi_header);
|
||||
|
|
Loading…
Reference in New Issue