Skip to content
Permalink
Browse files

Implement support for interplay MVE 0x06 and 0x10

  • Loading branch information
hpvb committed Jun 17, 2017
1 parent 18f0952 commit 21aa37ae8326a09a72e44ab9352f60e4439f01d9
Showing with 340 additions and 39 deletions.
  1. +263 −15 libavcodec/interplayvideo.c
  2. +77 −24 libavformat/ipmovie.c
@@ -55,8 +55,17 @@ typedef struct IpvideoContext {
HpelDSPContext hdsp;
AVFrame *second_last_frame;
AVFrame *last_frame;

/* For format 0x10 */
AVFrame *cur_decode_frame;
AVFrame *prev_decode_frame;

uint8_t frame_format;
const unsigned char *skip_map;
int skip_map_size;
const unsigned char *decoding_map;
int decoding_map_size;
int video_data_size;

int is_16bpp;
GetByteContext stream_ptr, mv_ptr;
@@ -903,7 +912,188 @@ static int (* const ipvideo_decode_block16[])(IpvideoContext *s, AVFrame *frame)
ipvideo_decode_block_opcode_0xE_16, ipvideo_decode_block_opcode_0x1,
};

static void ipvideo_decode_opcodes(IpvideoContext *s, AVFrame *frame)

static void ipvideo_decode_format_06_opcodes(IpvideoContext *s, AVFrame *frame)
{
int x, y, off_x, off_y;
const unsigned char* decode_map_p;
short opcode;

if (!s->is_16bpp) {
/* this is PAL8, so make the palette available */
memcpy(frame->data[1], s->pal, AVPALETTE_SIZE);

s->stride = frame->linesize[0];
}

s->line_inc = s->stride - 8;
s->upper_motion_limit_offset = (s->avctx->height - 8) * frame->linesize[0]
+ (s->avctx->width - 8) * (1 + s->is_16bpp);

decode_map_p = s->decoding_map;
for (y = 0; y < s->avctx->height; y += 8) {
for (x = 0; x < s->avctx->width; x += 8) {
opcode = AV_RL16(decode_map_p);

ff_tlog(s->avctx,
" block @ (%3d, %3d): opcode 0x%X, data ptr offset %d\n",
x, y, opcode, bytestream2_tell(&s->stream_ptr));

s->pixel_ptr = frame->data[0] + x + y*frame->linesize[0];
if(! opcode) {
int k;
for (k = 0; k < 8; k++) {
bytestream2_get_buffer(&s->stream_ptr, s->pixel_ptr, 8);
s->pixel_ptr += s->stride;
}
} else {
copy_from(s, s->second_last_frame, frame, 0, 0);
}
decode_map_p += 2;
}
}

decode_map_p = s->decoding_map;
for (y = 0; y < s->avctx->height; y += 8) {
for (x = 0; x < s->avctx->width; x += 8) {
opcode = AV_RL16(decode_map_p);

ff_tlog(s->avctx,
" block @ (%3d, %3d): opcode 0x%X, data ptr offset %d\n",
x, y, opcode, bytestream2_tell(&s->stream_ptr));

s->pixel_ptr = frame->data[0] + x + y*frame->linesize[0];
if(opcode < 0) {
off_x = ((unsigned short)opcode - 0xC000) % frame->linesize[0];
off_y = ((unsigned short)opcode - 0xC000) / frame->linesize[0];
copy_from(s, s->last_frame, frame, off_x, off_y);
}
if(opcode > 0) {
off_x = ((unsigned short)opcode - 0x4000) % frame->linesize[0];
off_y = ((unsigned short)opcode - 0x4000) / frame->linesize[0];
copy_from(s, frame, frame, off_x, off_y);
}
decode_map_p += 2;
}
}

if (bytestream2_get_bytes_left(&s->stream_ptr) > 1) {
av_log(s->avctx, AV_LOG_DEBUG,
"decode finished with %d bytes left over\n",
bytestream2_get_bytes_left(&s->stream_ptr));
}
}

static void ipvideo_decode_format_10_opcodes(IpvideoContext *s, AVFrame *frame)
{
int x, y, off_x, off_y;
const unsigned char* decode_map_p;
const unsigned char* skip_map_p;
short opcode, skip;
int changed_block = 0;

if (!s->is_16bpp) {
/* this is PAL8, so make the palette available */
memcpy(frame->data[1], s->pal, AVPALETTE_SIZE);

s->stride = frame->linesize[0];
}

bytestream2_skip(&s->stream_ptr, 14); /* data starts 14 bytes in */

s->line_inc = s->stride - 8;
s->upper_motion_limit_offset = (s->avctx->height - 8) * frame->linesize[0]
+ (s->avctx->width - 8) * (1 + s->is_16bpp);

decode_map_p = s->decoding_map;
skip_map_p = s->skip_map;
skip = AV_RL16(skip_map_p);
for (y = 0; y < s->avctx->height; y += 8) {
for (x = 0; x < s->avctx->width; x += 8) {
s->pixel_ptr = s->cur_decode_frame->data[0] + x + y*cur_decode_frame->linesize[0];

while (skip <= 0) {
if (skip != -0x8000 && skip != 0) {
opcode = AV_RL16(decode_map_p);
if(! opcode) {
int k;
for (k = 0; k < 8; k++) {
bytestream2_get_buffer(&s->stream_ptr, s->pixel_ptr, 8);
s->pixel_ptr += s->stride;
}
}
decode_map_p += 2;
break;
}
skip_map_p += 2;
skip = AV_RL16(skip_map_p);
}
skip *= 2;
}
}

decode_map_p = s->decoding_map;
skip_map_p = s->skip_map;
skip = AV_RL16(skip_map_p);
for (y = 0; y < s->avctx->height; y += 8) {
for (x = 0; x < s->avctx->width; x += 8) {
s->pixel_ptr = s->cur_decode_frame->data[0] + x + y*cur_decode_frame->linesize[0];

while (skip <= 0) {
if (skip != -0x8000 && skip != 0) {
opcode = AV_RL16(decode_map_p);
if(opcode < 0) {
off_x = ((unsigned short)opcode - 0xC000) % cur_decode_frame->linesize[0];
off_y = ((unsigned short)opcode - 0xC000) / cur_decode_frame->linesize[0];
copy_from(s, s->prev_decode_frame, s->cur_decode_frame, off_x, off_y);
}
if(opcode > 0) {
off_x = ((unsigned short)opcode - 0x4000) % cur_decode_frame->linesize[0];
off_y = ((unsigned short)opcode - 0x4000) / cur_decode_frame->linesize[0];
copy_from(s, s->cur_decode_frame, s->cur_decode_frame, off_x, off_y);
}
decode_map_p += 2;
break;
}
skip_map_p += 2;
skip = AV_RL16(skip_map_p);
}
skip *= 2;
}
}

skip_map_p = s->skip_map;
skip = AV_RL16(skip_map_p);
for (y = 0; y < s->avctx->height; y += 8) {
for (x = 0; x < s->avctx->width; x += 8) {
changed_block = 0;
s->pixel_ptr = frame->data[0] + x + y*frame->linesize[0];
while (skip <= 0) {
if (skip != -0x8000 && skip != 0) {
changed_block = 1;
break;
}
skip_map_p += 2;
skip = AV_RL16(skip_map_p);
}
if(changed_block)
copy_from(s, s->cur_decode_frame, frame, 0, 0);
else
copy_from(s, s->last_frame, frame, 0, 0);
skip *= 2;
}
}

FFSWAP(AVFrame*, s->prev_decode_frame, s->cur_decode_frame);

if (bytestream2_get_bytes_left(&s->stream_ptr) > 1) {
av_log(s->avctx, AV_LOG_DEBUG,
"decode finished with %d bytes left over\n",
bytestream2_get_bytes_left(&s->stream_ptr));
}
}

static void ipvideo_decode_format_11_opcodes(IpvideoContext *s, AVFrame *frame)
{
int x, y;
unsigned char opcode;
@@ -972,12 +1162,26 @@ static av_cold int ipvideo_decode_init(AVCodecContext *avctx)

s->last_frame = av_frame_alloc();
s->second_last_frame = av_frame_alloc();
if (!s->last_frame || !s->second_last_frame) {
s->cur_decode_frame = av_frame_alloc();
s->prev_decode_frame = av_frame_alloc();
if (!s->last_frame || !s->second_last_frame ||
!s->cur_decode_frame || !s->prev_decode_frame) {
av_frame_free(&s->last_frame);
av_frame_free(&s->second_last_frame);
av_frame_free(&s->cur_decode_frame);
av_frame_free(&s->prev_decode_frame);
return AVERROR(ENOMEM);
}

s->cur_decode_frame->width = avctx->width;
s->prev_decode_frame->width = avctx->width;
s->cur_decode_frame->height = avctx->height;
s->prev_decode_frame->height = avctx->height;
s->cur_decode_frame->format = avctx->pix_fmt;
s->prev_decode_frame->format = avctx->pix_fmt;

ff_get_buffer(avctx, s->cur_decode_frame, 0);
ff_get_buffer(avctx, s->prev_decode_frame, 0);
return 0;
}

@@ -999,18 +1203,53 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
if (buf_size < 2)
return AVERROR_INVALIDDATA;

/* decoding map contains 4 bits of information per 8x8 block */
s->decoding_map_size = AV_RL16(avpkt->data);

/* compressed buffer needs to be large enough to at least hold an entire
* decoding map */
if (buf_size < s->decoding_map_size + 2)
return buf_size;


s->decoding_map = buf + 2;
bytestream2_init(&s->stream_ptr, buf + 2 + s->decoding_map_size,
buf_size - s->decoding_map_size);
s->frame_format = AV_RB8(avpkt->data);
switch (s->frame_format) {
case 0x06:
/* Format 0x06 has decoding map appended to the top of pixel data */
/* decoding map contains 16 bits of information per 8x8 block */
s->decoding_map_size = ((s->avctx->width / 8) * (s->avctx->height / 8)) * 2;
s->decoding_map = buf + 3 + 14; /* 14 bits of op data */

s->video_data_size = AV_RL16(avpkt->data + 1) - s->decoding_map_size - 14;
bytestream2_init(&s->stream_ptr, buf + 3 + s->decoding_map_size + 14, s->video_data_size);

if (buf_size < s->decoding_map_size + 2)
return buf_size;
break;

case 0x10:
/* Format 0x10 has a decoding map, pixel data, and a skip map */
s->video_data_size = AV_RL16(avpkt->data + 1);
bytestream2_init(&s->stream_ptr, buf + 3, s->video_data_size);

/* decoding map contains 16 bits of information per 8x8 block */
s->decoding_map_size = AV_RL16(avpkt->data + 3 + s->video_data_size);
s->decoding_map = buf + 3 + s->video_data_size + 2;

/* skip map contains 16 bits of information per 15 blocks, ish */
s->skip_map_size = AV_RL16(avpkt->data + 3 + s->video_data_size + 2 + s->decoding_map_size);
s->skip_map = buf + 3 + s->video_data_size + 2 + s->decoding_map_size + 2;
break;

case 0x11:
/* Format 0x11 has a decoding map and pixel data */
s->video_data_size = AV_RL16(avpkt->data + 1);
bytestream2_init(&s->stream_ptr, buf + 3, s->video_data_size);

/* decoding map contains 4 bits of information per 8x8 block */
s->decoding_map_size = AV_RL16(avpkt->data + 3 + s->video_data_size);
s->decoding_map = buf + 3 + s->video_data_size + 2;

/* compressed buffer needs to be large enough to at least hold an entire
* decoding map */
if (buf_size < s->decoding_map_size + 2)
return buf_size;
break;

default:
av_log(avctx, AV_LOG_ERROR, "Frame type 0x%02X unsupported\n", s->frame_format);
}

if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
return ret;
@@ -1026,7 +1265,14 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
}
}

ipvideo_decode_opcodes(s, frame);
if(s->frame_format == 0x06)
ipvideo_decode_format_06_opcodes(s, frame);

if(s->frame_format == 0x10)
ipvideo_decode_format_10_opcodes(s, frame);

if(s->frame_format == 0x11)
ipvideo_decode_format_11_opcodes(s, frame);

*got_frame = 1;

@@ -1046,6 +1292,8 @@ static av_cold int ipvideo_decode_end(AVCodecContext *avctx)

av_frame_free(&s->last_frame);
av_frame_free(&s->second_last_frame);
av_frame_free(&s->cur_decode_frame);
av_frame_free(&s->prev_decode_frame);

return 0;
}

0 comments on commit 21aa37a

Please sign in to comment.