Added I420 (YUV 4:2:0) conversion option to avi recorder.

This commit is contained in:
mjbudd77 2021-04-30 11:57:23 -04:00
parent b9f6bf281f
commit ce554b5e7b
3 changed files with 150 additions and 8 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);