forked from FFmpeg/FFmpeg
avcodec/sanm: SMUSH codec48 decoder
Adds a decoder for the SMUSH codec48 video encoding, as is used by the LucasArts game "Mysteries of the Sith". Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com> Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
parent
dd5696c197
commit
b22ce90d42
1 changed files with 223 additions and 0 deletions
|
@ -995,6 +995,227 @@ static int old_codec47(SANMVideoContext *ctx, int top,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scale 4x4 input block to an 8x8 output block
|
||||||
|
static void c48_4to8(uint8_t *dst, const uint8_t *src, const uint16_t w)
|
||||||
|
{
|
||||||
|
uint16_t p;
|
||||||
|
// dst is always at least 16bit aligned
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; j < 8; j += 2) {
|
||||||
|
p = *src++;
|
||||||
|
p = (p << 8) | p;
|
||||||
|
*((uint16_t *)(dst + w * 0 + j)) = p;
|
||||||
|
*((uint16_t *)(dst + w * 1 + j)) = p;
|
||||||
|
}
|
||||||
|
dst += w * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int codec48_block(SANMVideoContext *ctx, uint8_t *dst, uint8_t *db,
|
||||||
|
const uint16_t w)
|
||||||
|
{
|
||||||
|
uint8_t opc, sb[16];
|
||||||
|
int i, j, k, l;
|
||||||
|
int16_t mvofs;
|
||||||
|
uint32_t ofs;
|
||||||
|
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
opc = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
switch (opc) {
|
||||||
|
case 0xFF: // 1x1 -> 8x8 block scale
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
opc = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
sb[i] = opc;
|
||||||
|
c48_4to8(dst, sb, w);
|
||||||
|
break;
|
||||||
|
case 0xFE: // 1x 8x8 copy from deltabuf, 16bit mv from source
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 2)
|
||||||
|
return 1;
|
||||||
|
mvofs = bytestream2_get_le16(&ctx->gb);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ofs = w * i;
|
||||||
|
for (k = 0; k < 8; k++)
|
||||||
|
*(dst + ofs + k) = *(db + ofs + k + mvofs);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xFD: // 2x2 -> 8x8 block scale
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 4)
|
||||||
|
return 1;
|
||||||
|
sb[ 5] = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
sb[ 7] = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
sb[13] = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
sb[15] = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
|
||||||
|
sb[0] = sb[1] = sb[4] = sb[5];
|
||||||
|
sb[2] = sb[3] = sb[6] = sb[7];
|
||||||
|
sb[8] = sb[9] = sb[12] = sb[13];
|
||||||
|
sb[10] = sb[11] = sb[14] = sb[15];
|
||||||
|
c48_4to8(dst, sb, w);
|
||||||
|
break;
|
||||||
|
case 0xFC: // 4x copy 4x4 block, per-block c37_mv from source
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 4)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < 8; i += 4) {
|
||||||
|
for (k = 0; k < 8; k += 4) {
|
||||||
|
opc = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
|
||||||
|
for (j = 0; j < 4; j++) {
|
||||||
|
ofs = (w * (j + i)) + k;
|
||||||
|
for (l = 0; l < 4; l++)
|
||||||
|
*(dst + ofs + l) = *(db + ofs + l + mvofs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xFB: // Copy 4x 4x4 blocks, per-block mv from source
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 8)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < 8; i += 4) {
|
||||||
|
for (k = 0; k < 8; k += 4) {
|
||||||
|
mvofs = bytestream2_get_le16(&ctx->gb);
|
||||||
|
for (j = 0; j < 4; j++) {
|
||||||
|
ofs = (w * (j + i)) + k;
|
||||||
|
for (l = 0; l < 4; l++)
|
||||||
|
*(dst + ofs + l) = *(db + ofs + l + mvofs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xFA: // scale 4x4 input block to 8x8 dest block
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 16)
|
||||||
|
return 1;
|
||||||
|
bytestream2_get_bufferu(&ctx->gb, sb, 16);
|
||||||
|
c48_4to8(dst, sb, w);
|
||||||
|
break;
|
||||||
|
case 0xF9: // 16x 2x2 copy from delta, per-block c37_mv from source
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 16)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < 8; i += 2) {
|
||||||
|
for (j = 0; j < 8; j += 2) {
|
||||||
|
ofs = (w * i) + j;
|
||||||
|
opc = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
|
||||||
|
for (l = 0; l < 2; l++) {
|
||||||
|
*(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
|
||||||
|
*(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xF8: // 16x 2x2 blocks copy, 16bit mv from source
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 32)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < 8; i += 2) {
|
||||||
|
for (j = 0; j < 8; j += 2) {
|
||||||
|
ofs = w * i + j;
|
||||||
|
mvofs = bytestream2_get_le16(&ctx->gb);
|
||||||
|
for (l = 0; l < 2; l++) {
|
||||||
|
*(dst + ofs + l + 0) = *(db + ofs + l + 0 + mvofs);
|
||||||
|
*(dst + ofs + l + w) = *(db + ofs + l + w + mvofs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xF7: // copy 8x8 block from src to dest
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 64)
|
||||||
|
return 1;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ofs = i * w;
|
||||||
|
for (l = 0; l < 8; l++)
|
||||||
|
*(dst + ofs + l) = bytestream2_get_byteu(&ctx->gb);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // copy 8x8 block from prev, c37_mv from source
|
||||||
|
mvofs = c37_mv[opc * 2] + (c37_mv[opc * 2 + 1] * w);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ofs = i * w;
|
||||||
|
for (l = 0; l < 8; l++)
|
||||||
|
*(dst + ofs + l) = *(db + ofs + l + mvofs);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int old_codec48(SANMVideoContext *ctx, int width, int height)
|
||||||
|
{
|
||||||
|
uint8_t *dst, *prev;
|
||||||
|
int compr = bytestream2_get_byte(&ctx->gb);
|
||||||
|
int mvidx = bytestream2_get_byte(&ctx->gb);
|
||||||
|
int seq = bytestream2_get_le16(&ctx->gb);
|
||||||
|
uint32_t decoded_size = bytestream2_get_le32(&ctx->gb);
|
||||||
|
int i, j, flags;
|
||||||
|
|
||||||
|
// all codec48 videos use 1, but just to be safe...
|
||||||
|
if (mvidx != 1) {
|
||||||
|
av_log(ctx->avctx, AV_LOG_ERROR, "Invalid motion base value %d.\n", mvidx);
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytestream2_skip(&ctx->gb, 4);
|
||||||
|
flags = bytestream2_get_byte(&ctx->gb);
|
||||||
|
bytestream2_skip(&ctx->gb, 3);
|
||||||
|
|
||||||
|
if (flags & 8) {
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < 0x8080)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
codec47_read_interptable(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = (uint8_t*)ctx->frm0;
|
||||||
|
prev = (uint8_t*)ctx->frm2;
|
||||||
|
|
||||||
|
if (!seq) {
|
||||||
|
ctx->prev_seq = -1;
|
||||||
|
memset(prev, 0, ctx->aligned_height * width);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (compr) {
|
||||||
|
case 0:
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < width * height)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
for (j = 0; j < height; j++) {
|
||||||
|
bytestream2_get_bufferu(&ctx->gb, dst, width);
|
||||||
|
dst += width;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (rle_decode(ctx, dst, decoded_size))
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (seq == ctx->prev_seq + 1) {
|
||||||
|
for (j = 0; j < height; j += 8) {
|
||||||
|
for (i = 0; i < width; i += 8) {
|
||||||
|
if (codec48_block(ctx, dst + i, prev + i, width))
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
}
|
||||||
|
dst += width * 8;
|
||||||
|
prev += width * 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
if (bytestream2_get_bytes_left(&ctx->gb) < ((width + 1) >> 1) * ((height + 1) >> 1))
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
codec47_comp1(ctx, dst, width, height, width);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
avpriv_report_missing_feature(ctx->avctx,
|
||||||
|
"Subcodec 48 compression %d", compr);
|
||||||
|
return AVERROR_PATCHWELCOME;
|
||||||
|
}
|
||||||
|
ctx->rotate_code = 1; // swap frm[0] and frm[2]
|
||||||
|
ctx->prev_seq = seq;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int process_frame_obj(SANMVideoContext *ctx)
|
static int process_frame_obj(SANMVideoContext *ctx)
|
||||||
{
|
{
|
||||||
uint16_t codec = bytestream2_get_le16u(&ctx->gb);
|
uint16_t codec = bytestream2_get_le16u(&ctx->gb);
|
||||||
|
@ -1030,6 +1251,8 @@ static int process_frame_obj(SANMVideoContext *ctx)
|
||||||
return old_codec37(ctx, top, left, w, h);
|
return old_codec37(ctx, top, left, w, h);
|
||||||
case 47:
|
case 47:
|
||||||
return old_codec47(ctx, top, left, w, h);
|
return old_codec47(ctx, top, left, w, h);
|
||||||
|
case 48:
|
||||||
|
return old_codec48(ctx, w, h);
|
||||||
default:
|
default:
|
||||||
avpriv_request_sample(ctx->avctx, "Subcodec %d", codec);
|
avpriv_request_sample(ctx->avctx, "Subcodec %d", codec);
|
||||||
return AVERROR_PATCHWELCOME;
|
return AVERROR_PATCHWELCOME;
|
||||||
|
|
Loading…
Add table
Reference in a new issue