226 lines
4.8 KiB
C++
226 lines
4.8 KiB
C++
|
#include "avi_rle_decoder.h"
|
||
|
#include "../Winamp/wa_ipc.h"
|
||
|
#include <limits.h>
|
||
|
#include "rle.h"
|
||
|
#include <intsafe.h>
|
||
|
|
||
|
AVIRLE *AVIRLE::CreateDecoder(nsavi::video_format *stream_format)
|
||
|
{
|
||
|
if (stream_format->bits_per_pixel == 4)
|
||
|
return 0;
|
||
|
|
||
|
size_t bytes_per_pixel = stream_format->bits_per_pixel / 8U;
|
||
|
if (bytes_per_pixel > 4)
|
||
|
return 0;
|
||
|
|
||
|
size_t image_size=0;
|
||
|
if (SizeTMult(stream_format->width, stream_format->height, &image_size) != S_OK || SizeTMult(image_size, bytes_per_pixel, &image_size) != S_OK)
|
||
|
return 0;
|
||
|
|
||
|
void *video_frame = (uint8_t *)malloc(image_size);
|
||
|
if (!video_frame)
|
||
|
return 0;
|
||
|
|
||
|
AVIRLE *decoder = new AVIRLE(video_frame, stream_format, image_size);
|
||
|
if (!decoder)
|
||
|
{
|
||
|
free(video_frame);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return decoder;
|
||
|
}
|
||
|
|
||
|
|
||
|
AVIRLE::AVIRLE(void *video_frame, nsavi::video_format *stream_format, size_t video_frame_size) : stream_format(stream_format), video_frame((uint8_t *)video_frame), video_frame_size(video_frame_size)
|
||
|
{
|
||
|
memset(palette, 0, sizeof(palette));
|
||
|
memcpy(palette, (uint8_t *)stream_format + 44, 1024);
|
||
|
video_outputted=false;
|
||
|
palette_retrieved=false;
|
||
|
}
|
||
|
|
||
|
int AVIRLE::GetPalette(RGB32 **palette)
|
||
|
{
|
||
|
if (!palette_retrieved)
|
||
|
{
|
||
|
*palette = (RGB32 *)(this->palette);
|
||
|
palette_retrieved=true;
|
||
|
return AVI_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return AVI_FAILURE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
int AVIRLE::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip)
|
||
|
{
|
||
|
if (stream_format)
|
||
|
{
|
||
|
*x = stream_format->width;
|
||
|
*y = stream_format->height;
|
||
|
*flip = 1;
|
||
|
switch(stream_format->bits_per_pixel)
|
||
|
{
|
||
|
case 4:
|
||
|
*color_format = '8BGR';
|
||
|
break;
|
||
|
case 8:
|
||
|
*color_format = '8BGR';
|
||
|
break;
|
||
|
case 16:
|
||
|
*color_format = '555R';
|
||
|
break;
|
||
|
case 24:
|
||
|
*color_format = '42GR';
|
||
|
break;
|
||
|
case 32:
|
||
|
*color_format = '23GR';
|
||
|
break;
|
||
|
default:
|
||
|
return AVI_FAILURE;
|
||
|
}
|
||
|
return AVI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return AVI_FAILURE;
|
||
|
}
|
||
|
|
||
|
static bool CheckOverflow(size_t total_size, int current_position, int read_size)
|
||
|
{
|
||
|
if (read_size > (int)total_size) // check separate to avoid overflow
|
||
|
return true;
|
||
|
if (((int)total_size - read_size) < current_position)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int AVIRLE::DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes)
|
||
|
{
|
||
|
if (stream_format)
|
||
|
{
|
||
|
uint32_t bytes_per_pixel = stream_format->bits_per_pixel / 8;
|
||
|
const uint8_t * const rle = (const uint8_t *)inputBuffer;
|
||
|
if (bytes_per_pixel == 2)
|
||
|
{
|
||
|
RLE16(rle, inputBufferBytes, (uint16_t *)video_frame, video_frame_size, stream_format->width);
|
||
|
}
|
||
|
else if (bytes_per_pixel == 1)
|
||
|
{
|
||
|
RLE8(rle, inputBufferBytes, (uint8_t *)video_frame, video_frame_size, stream_format->width);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
int input = 0;
|
||
|
int output = 0;
|
||
|
|
||
|
int next_line = output + bytes_per_pixel*stream_format->width;
|
||
|
while (input < (int)inputBufferBytes && output < (int)video_frame_size)
|
||
|
{
|
||
|
if (CheckOverflow(inputBufferBytes, input, 2)) // we always read at least two bytes
|
||
|
break;
|
||
|
|
||
|
uint8_t b0 = rle[input++];
|
||
|
if (b0)
|
||
|
{
|
||
|
if (CheckOverflow(inputBufferBytes, input, bytes_per_pixel))
|
||
|
break;
|
||
|
|
||
|
if (CheckOverflow(video_frame_size, output, b0*bytes_per_pixel))
|
||
|
break;
|
||
|
|
||
|
uint8_t pixel[4];
|
||
|
memcpy(pixel, &rle[input], bytes_per_pixel);
|
||
|
input += bytes_per_pixel;
|
||
|
while (b0--)
|
||
|
{
|
||
|
memcpy(&video_frame[output], &pixel, bytes_per_pixel);
|
||
|
output+=bytes_per_pixel;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint8_t b1 = rle[input++];
|
||
|
if (b1 == 0)
|
||
|
{
|
||
|
output = next_line;
|
||
|
next_line = output + bytes_per_pixel*stream_format->width;
|
||
|
}
|
||
|
else if (b1 == 1)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
else if (b1 == 2)
|
||
|
{
|
||
|
if (CheckOverflow(inputBufferBytes, input, 2))
|
||
|
break;
|
||
|
|
||
|
uint8_t p1 = rle[input++];
|
||
|
uint8_t p2 = rle[input++];
|
||
|
output += bytes_per_pixel*p1;
|
||
|
output += bytes_per_pixel*p2*stream_format->width;
|
||
|
next_line += bytes_per_pixel*p2*stream_format->width;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (CheckOverflow(inputBufferBytes, input, b1*bytes_per_pixel))
|
||
|
break;
|
||
|
|
||
|
if (CheckOverflow(video_frame_size, output, b1*bytes_per_pixel))
|
||
|
break;
|
||
|
|
||
|
memcpy(&video_frame[output], &rle[input], b1*bytes_per_pixel);
|
||
|
input += b1*bytes_per_pixel;
|
||
|
output += b1*bytes_per_pixel;
|
||
|
if (bytes_per_pixel == 1 && (b1 & 1))
|
||
|
input++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
video_outputted=false;
|
||
|
return AVI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return AVI_FAILURE;
|
||
|
}
|
||
|
|
||
|
void AVIRLE::Flush()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
int AVIRLE::GetPicture(void **data, void **decoder_data)
|
||
|
{
|
||
|
if (!video_outputted && video_frame)
|
||
|
{
|
||
|
*data = video_frame;
|
||
|
*decoder_data=0;
|
||
|
video_outputted=true;
|
||
|
return AVI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return AVI_FAILURE;
|
||
|
}
|
||
|
|
||
|
void AVIRLE::Close()
|
||
|
{
|
||
|
free(video_frame);
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
#define CBCLASS AVIRLE
|
||
|
START_DISPATCH;
|
||
|
CB(GET_OUTPUT_PROPERTIES, GetOutputProperties)
|
||
|
CB(DECODE_CHUNK, DecodeChunk)
|
||
|
VCB(FLUSH, Flush)
|
||
|
VCB(CLOSE, Close)
|
||
|
CB(GET_PICTURE, GetPicture)
|
||
|
CB(GET_PALETTE, GetPalette)
|
||
|
END_DISPATCH;
|
||
|
#undef CBCLASS
|