VIDEO: Correct SVQ1 Header Decoding and Last Frame Buffering.

Header was incorrectly documnented in reference documents.
Corrected with reference to FFMPEG.
Also, added missing buffering of last frame for P frame decoding.
This commit is contained in:
D G Turner 2012-04-05 05:08:08 +01:00
parent c9bbe5793c
commit 97746e2281
2 changed files with 176 additions and 172 deletions

View File

@ -45,13 +45,12 @@ namespace Video {
#define SVQ1_BLOCK_INTRA 3
SVQ1Decoder::SVQ1Decoder(uint16 width, uint16 height) {
debug(1, "SVQ1Decoder::SVQ1Decoder(width:%d, height:%d)", width, height);
_width = width;
_height = height;
_surface = new Graphics::Surface();
_surface->create(width, height, g_system->getScreenFormat());
_current[0] = new byte[width*height];
_current[1] = new byte[(width/4)*(height/4)];
_current[2] = new byte[(width/4)*(height/4)];
_last[0] = 0;
_last[1] = 0;
_last[2] = 0;
@ -73,10 +72,6 @@ SVQ1Decoder::~SVQ1Decoder() {
_surface->free();
delete _surface;
delete[] _current[0];
delete[] _current[1];
delete[] _current[2];
delete[] _last[0];
delete[] _last[1];
delete[] _last[2];
@ -166,6 +161,89 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
//for (int i = 0; i < length; i++)
// value = checksum_table[data[i] ^ (value >> 8)] ^ ((value & 0xFF) << 8);
}
static const uint8 stringXORTable[256] = {
0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54,
0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D,
0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06,
0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F,
0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0,
0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9,
0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2,
0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B,
0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9,
0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0,
0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B,
0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2,
0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D,
0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44,
0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F,
0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16,
0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB,
0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92,
0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9,
0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0,
0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F,
0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36,
0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D,
0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64,
0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26,
0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F,
0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74,
0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D,
0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82,
0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB,
0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0,
0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9
};
if ((frameCode ^ 0x10) >= 0x50) {
// Decode embedded string
Common::String str;
uint8 stringLen = frameData.getBits(8);
byte xorVal = stringXORTable[stringLen];
for (uint16 i = 0; i < stringLen-1; i++) {
byte data = frameData.getBits(8);
str += data ^ xorVal;
xorVal = stringXORTable[data];
}
debug(1, " Embedded String of %d Characters: \"%s\"", stringLen, str.c_str());
}
byte unk1 = frameData.getBits(2); // Unknown
debug(1, " unk1: %d", unk1);
byte unk2 = frameData.getBits(2); // Unknown
debug(1, " unk2: %d", unk2);
bool unk3 = frameData.getBit(); // Unknown
debug(1, " unk3: %d", unk3);
static const struct { uint w, h; } standardFrameSizes[7] = {
{ 160, 120 }, // 0
{ 128, 96 }, // 1
{ 176, 144 }, // 2
{ 352, 288 }, // 3
{ 704, 576 }, // 4
{ 240, 180 }, // 5
{ 320, 240 } // 6
};
byte frameSizeCode = frameData.getBits(3);
debug(1, " frameSizeCode: %d", frameSizeCode);
uint16 frameWidth, frameHeight;
if (frameSizeCode == 7) {
frameWidth = frameData.getBits(12);
frameHeight = frameData.getBits(12);
} else {
frameWidth = standardFrameSizes[frameSizeCode].w;
frameHeight = standardFrameSizes[frameSizeCode].h;
}
debug(1, " frameWidth: %d", frameWidth);
debug(1, " frameHeight: %d", frameHeight);
if (frameWidth != _width || frameHeight != _height) { // Invalid
warning("Invalid Frame Size");
return _surface;
}
} else if (frameType == 2) { // B Frame
warning("B Frames not supported by SVQ1 decoder");
return _surface;
@ -174,88 +252,6 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
return _surface;
}
static const uint8 stringXORTable[256] = {
0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54,
0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D,
0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06,
0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F,
0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0,
0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9,
0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2,
0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B,
0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9,
0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0,
0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B,
0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2,
0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D,
0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44,
0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F,
0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16,
0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB,
0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92,
0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9,
0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0,
0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F,
0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36,
0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D,
0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64,
0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26,
0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F,
0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74,
0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D,
0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82,
0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB,
0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0,
0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9
};
if ((frameCode ^ 0x10) >= 0x50) {
// Decode embedded string
Common::String str;
uint8 stringLen = frameData.getBits(8);
byte xorVal = stringXORTable[stringLen];
for (uint16 i = 0; i < stringLen-1; i++) {
byte data = frameData.getBits(8);
str += data ^ xorVal;
xorVal = stringXORTable[data];
}
debug(1, " Embedded String of %d Characters: \"%s\"", stringLen, str.c_str());
}
byte unk1 = frameData.getBits(2); // Unknown
debug(1, " unk1: %d", unk1);
byte unk2 = frameData.getBits(2); // Unknown
debug(1, " unk2: %d", unk2);
bool unk3 = frameData.getBit(); // Unknown
debug(1, " unk3: %d", unk3);
static const struct { uint w, h; } standardFrameSizes[7] = {
{ 160, 120 }, // 0
{ 128, 96 }, // 1
{ 176, 144 }, // 2
{ 352, 288 }, // 3
{ 704, 576 }, // 4
{ 240, 180 }, // 5
{ 320, 240 } // 6
};
byte frameSizeCode = frameData.getBits(3);
debug(1, " frameSizeCode: %d", frameSizeCode);
uint16 frameWidth, frameHeight;
if (frameSizeCode == 7) {
frameWidth = frameData.getBits(12);
frameHeight = frameData.getBits(12);
} else {
frameWidth = standardFrameSizes[frameSizeCode].w;
frameHeight = standardFrameSizes[frameSizeCode].h;
}
debug(1, " frameWidth: %d", frameWidth);
debug(1, " frameHeight: %d", frameHeight);
if (frameWidth == 0 || frameHeight == 0) { // Invalid
warning("Invalid Frame Size");
return _surface;
}
bool checksumPresent = frameData.getBit();
debug(1, " checksumPresent: %d", checksumPresent);
if (checksumPresent) {
@ -286,91 +282,98 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st
}
}
if (frameWidth == _surface->w && frameHeight == _surface->h) {
// Decode Y, U and V component planes
for (int i = 0; i < 3; i++) {
int linesize, width, height;
if (i == 0) {
// Y Size is width * height
width = frameWidth;
if (width % 16) {
width /= 16;
width++;
width *= 16;
}
assert(width % 16 == 0);
height = frameHeight;
if (height % 16) {
height /= 16;
height++;
height *= 16;
}
assert(height % 16 == 0);
linesize = width;
} else {
// U and V size is width/4 * height/4
width = frameWidth/4;
if (width % 16) {
width /= 16;
width++;
width *= 16;
}
assert(width % 16 == 0);
height = frameHeight/4;
if (height % 16) {
height /= 16;
height++;
height *= 16;
}
assert(height % 16 == 0);
linesize = width;
byte *current[3];
current[0] = new byte[_width*_height];
current[1] = new byte[(_width/4)*(_height/4)];
current[2] = new byte[(_width/4)*(_height/4)];
// Decode Y, U and V component planes
for (int i = 0; i < 3; i++) {
int linesize, width, height;
if (i == 0) {
// Y Size is width * height
width = _width;
if (width % 16) {
width /= 16;
width++;
width *= 16;
}
if (frameType == 0) { // I Frame
// Keyframe (I)
byte *current = _current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
if (int result = svq1DecodeBlockIntra(&frameData, &current[x], linesize) != 0) {
warning("Error in svq1DecodeBlock %i (keyframe)", result);
return _surface;
}
}
current += 16 * linesize;
}
} else {
// Delta frame (P or B)
// Prediction Motion Vector
Common::Point *pmv = new Common::Point[(width/8) + 3];
byte *previous;
if(frameType == 2) { // B Frame
warning("B Frame not supported currently");
//previous = _next[i];
} else
previous = _last[i];
byte *current = _current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
if (int result = svq1DecodeDeltaBlock(&frameData, &current[x], previous, linesize, pmv, x, y) != 0) {
warning("Error in svq1DecodeDeltaBlock %i", result);
return _surface;
}
}
pmv[0].x = pmv[0].y = 0;
current += 16*linesize;
}
delete[] pmv;
assert(width % 16 == 0);
height = _height;
if (height % 16) {
height /= 16;
height++;
height *= 16;
}
assert(height % 16 == 0);
linesize = _width;
} else {
// U and V size is width/4 * height/4
width = _width/4;
if (width % 16) {
width /= 16;
width++;
width *= 16;
}
assert(width % 16 == 0);
height = _height/4;
if (height % 16) {
height /= 16;
height++;
height *= 16;
}
assert(height % 16 == 0);
linesize = _width/4;
}
convertYUV410ToRGB(_surface, _current[0], _current[1], _current[2], frameWidth, frameHeight, frameWidth, frameWidth/2);
} else
warning("FrameWidth/Height Sanity Check Failed!");
if (frameType == 0) { // I Frame
// Keyframe (I)
byte *currentP = current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
if (int result = svq1DecodeBlockIntra(&frameData, &currentP[x], linesize) != 0) {
warning("Error in svq1DecodeBlock %i (keyframe)", result);
return _surface;
}
}
currentP += 16 * linesize;
}
} else {
// Delta frame (P or B)
// Prediction Motion Vector
Common::Point *pmv = new Common::Point[(width/8) + 3];
byte *previous;
if(frameType == 2) { // B Frame
warning("B Frame not supported currently");
//previous = _next[i];
} else
previous = _last[i];
byte *currentP = current[i];
for (uint16 y = 0; y < height; y += 16) {
for (uint16 x = 0; x < width; x += 16) {
if (int result = svq1DecodeDeltaBlock(&frameData, &currentP[x], previous, linesize, pmv, x, y) != 0) {
warning("Error in svq1DecodeDeltaBlock %i", result);
return _surface;
}
}
pmv[0].x = pmv[0].y = 0;
currentP += 16*linesize;
}
delete[] pmv;
}
}
convertYUV410ToRGB(_surface, current[0], current[1], current[2], _width, _height, _width, _width/4);
for (int i = 0; i < 3; i++) {
delete _last[i];
_last[i] = current[i];
}
return _surface;
}

View File

@ -43,8 +43,9 @@ public:
private:
Graphics::Surface *_surface;
uint16 _width;
uint16 _height;
byte *_current[3];
byte *_last[3];
Common::Huffman *_blockType;