summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libavcodec/interplayvideo.c182
-rw-r--r--libavformat/ipmovie.c68
2 files changed, 227 insertions, 23 deletions
diff --git a/libavcodec/interplayvideo.c b/libavcodec/interplayvideo.c
index 431eeb12c7..421de26cb1 100644
--- a/libavcodec/interplayvideo.c
+++ b/libavcodec/interplayvideo.c
@@ -55,8 +55,15 @@ typedef struct IpvideoContext {
HpelDSPContext hdsp;
AVFrame *second_last_frame;
AVFrame *last_frame;
+
+ /* For format 0x10 */
+ AVFrame *cur_decode_frame;
+ AVFrame *prev_decode_frame;
+
const unsigned char *decoding_map;
int decoding_map_size;
+ const unsigned char *skip_map;
+ int skip_map_size;
int is_16bpp;
GetByteContext stream_ptr, mv_ptr;
@@ -977,6 +984,114 @@ static void ipvideo_decode_format_06_opcodes(IpvideoContext *s, AVFrame *frame)
}
}
+static void ipvideo_format_10_firstpass(IpvideoContext *s, AVFrame *frame, short opcode)
+{
+ int line;
+
+ if (!opcode) {
+ for (line = 0; line < 8; ++line) {
+ bytestream2_get_buffer(&s->stream_ptr, s->pixel_ptr, 8);
+ s->pixel_ptr += s->stride;
+ }
+ }
+}
+
+static void ipvideo_format_10_secondpass(IpvideoContext *s, AVFrame *frame, short opcode)
+{
+ int off_x, off_y;
+
+ if (opcode < 0) {
+ off_x = ((unsigned short)opcode - 0xC000) % s->cur_decode_frame->linesize[0];
+ off_y = ((unsigned short)opcode - 0xC000) / s->cur_decode_frame->linesize[0];
+ copy_from(s, s->prev_decode_frame, s->cur_decode_frame, off_x, off_y);
+ } else if (opcode > 0) {
+ off_x = ((unsigned short)opcode - 0x4000) % s->cur_decode_frame->linesize[0];
+ off_y = ((unsigned short)opcode - 0x4000) / s->cur_decode_frame->linesize[0];
+ copy_from(s, s->cur_decode_frame, s->cur_decode_frame, off_x, off_y);
+ }
+}
+
+static void (* const ipvideo_format_10_passes[])(IpvideoContext *s, AVFrame *frame, short op) = {
+ ipvideo_format_10_firstpass, ipvideo_format_10_secondpass,
+};
+
+static void ipvideo_decode_format_10_opcodes(IpvideoContext *s, AVFrame *frame)
+{
+ int pass, x, y, changed_block;
+ short opcode, skip;
+ GetByteContext decoding_map_ptr;
+ GetByteContext skip_map_ptr;
+
+ bytestream2_skip(&s->stream_ptr, 14); /* data starts 14 bytes in */
+
+ /* 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);
+
+ bytestream2_init(&decoding_map_ptr, s->decoding_map, s->decoding_map_size);
+ bytestream2_init(&skip_map_ptr, s->skip_map, s->skip_map_size);
+
+ for (pass = 0; pass < 2; ++pass) {
+ bytestream2_seek(&decoding_map_ptr, 0, SEEK_SET);
+ bytestream2_seek(&skip_map_ptr, 0, SEEK_SET);
+ skip = bytestream2_get_le16(&skip_map_ptr);
+
+ 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 * s->cur_decode_frame->linesize[0];
+
+ while (skip <= 0) {
+ if (skip != -0x8000 && skip) {
+ opcode = bytestream2_get_le16(&decoding_map_ptr);
+ ipvideo_format_10_passes[pass](s, frame, opcode);
+ break;
+ }
+ skip = bytestream2_get_le16(&skip_map_ptr);
+ }
+ skip *= 2;
+ }
+ }
+ }
+
+ bytestream2_seek(&skip_map_ptr, 0, SEEK_SET);
+ skip = bytestream2_get_le16(&skip_map_ptr);
+ 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) {
+ changed_block = 1;
+ break;
+ }
+ skip = bytestream2_get_le16(&skip_map_ptr);
+ }
+
+ if (changed_block) {
+ copy_from(s, s->cur_decode_frame, frame, 0, 0);
+ } else {
+ /* Don't try to copy last_frame data on the first frame */
+ if (s->avctx->frame_number)
+ 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;
@@ -1046,12 +1161,27 @@ 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;
}
@@ -1073,13 +1203,14 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
av_frame_unref(s->second_last_frame);
}
- if (buf_size < 6)
+ if (buf_size < 8)
return AVERROR_INVALIDDATA;
frame_format = AV_RL8(buf);
send_buffer = AV_RL8(buf + 1);
video_data_size = AV_RL16(buf + 2);
s->decoding_map_size = AV_RL16(buf + 4);
+ s->skip_map_size = AV_RL16(buf + 6);
switch(frame_format) {
case 0x06:
@@ -1088,6 +1219,11 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
return AVERROR_INVALIDDATA;
}
+ if (s->skip_map_size) {
+ av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x06\n");
+ return AVERROR_INVALIDDATA;
+ }
+
if (s->is_16bpp) {
av_log(avctx, AV_LOG_ERROR, "Video format 0x06 does not support 16bpp movies\n");
return AVERROR_INVALIDDATA;
@@ -1095,9 +1231,31 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
/* Decoding map for 0x06 frame format is at the top of pixeldata */
s->decoding_map_size = ((s->avctx->width / 8) * (s->avctx->height / 8)) * 2;
- s->decoding_map = buf + 6 + 14; /* 14 bits of op data */
+ s->decoding_map = buf + 8 + 14; /* 14 bits of op data */
video_data_size -= s->decoding_map_size + 14;
- bytestream2_init(&s->stream_ptr, buf + 6 + s->decoding_map_size + 14, video_data_size);
+ bytestream2_init(&s->stream_ptr, buf + 8 + s->decoding_map_size + 14, video_data_size);
+
+ break;
+
+ case 0x10:
+ if (! s->decoding_map_size) {
+ av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x10\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (! s->skip_map_size) {
+ av_log(avctx, AV_LOG_ERROR, "Empty skip map for format 0x10\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (s->is_16bpp) {
+ av_log(avctx, AV_LOG_ERROR, "Video format 0x10 does not support 16bpp movies\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ bytestream2_init(&s->stream_ptr, buf + 8, video_data_size);
+ s->decoding_map = buf + 8 + video_data_size;
+ s->skip_map = buf + 8 + video_data_size + s->decoding_map_size;
break;
@@ -1107,8 +1265,13 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
return AVERROR_INVALIDDATA;
}
- bytestream2_init(&s->stream_ptr, buf + 6, video_data_size);
- s->decoding_map = buf + 6 + video_data_size;
+ if (s->skip_map_size) {
+ av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x11\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ bytestream2_init(&s->stream_ptr, buf + 8, video_data_size);
+ s->decoding_map = buf + 8 + video_data_size;
break;
@@ -1117,7 +1280,7 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
}
/* ensure we can't overread the packet */
- if (buf_size < 6 + s->decoding_map_size + video_data_size) {
+ if (buf_size < 8 + s->decoding_map_size + video_data_size + s->skip_map_size) {
av_log(avctx, AV_LOG_ERROR, "Invalid IP packet size\n");
return AVERROR_INVALIDDATA;
}
@@ -1140,6 +1303,9 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
case 0x06:
ipvideo_decode_format_06_opcodes(s, frame);
break;
+ case 0x10:
+ ipvideo_decode_format_10_opcodes(s, frame);
+ break;
case 0x11:
ipvideo_decode_format_11_opcodes(s, frame);
break;
@@ -1163,6 +1329,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;
}
diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c
index 7f2454b0f0..7f5a8c62ef 100644
--- a/libavformat/ipmovie.c
+++ b/libavformat/ipmovie.c
@@ -66,9 +66,9 @@
#define OPCODE_CREATE_GRADIENT 0x0B
#define OPCODE_SET_PALETTE 0x0C
#define OPCODE_SET_PALETTE_COMPRESSED 0x0D
-#define OPCODE_UNKNOWN_0E 0x0E
+#define OPCODE_SET_SKIP_MAP 0x0E
#define OPCODE_SET_DECODING_MAP 0x0F
-#define OPCODE_UNKNOWN_10 0x10
+#define OPCODE_VIDEO_DATA_10 0x10
#define OPCODE_VIDEO_DATA_11 0x11
#define OPCODE_UNKNOWN_12 0x12
#define OPCODE_UNKNOWN_13 0x13
@@ -107,6 +107,8 @@ typedef struct IPMVEContext {
int audio_chunk_size;
int64_t video_chunk_offset;
int video_chunk_size;
+ int64_t skip_map_chunk_offset;
+ int skip_map_chunk_size;
int64_t decode_map_chunk_offset;
int decode_map_chunk_size;
@@ -156,9 +158,9 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
} else if (s->frame_format) {
- /* send the frame format, decode map, the video data, and the send_buffer flag together */
+ /* send the frame format, decode map, the video data, skip map, and the send_buffer flag together */
- if (av_new_packet(pkt, 6 + s->decode_map_chunk_size + s->video_chunk_size))
+ if (av_new_packet(pkt, 8 + s->decode_map_chunk_size + s->video_chunk_size + s->skip_map_chunk_size))
return CHUNK_NOMEM;
if (s->has_palette) {
@@ -181,6 +183,7 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
AV_WL8(pkt->data + 1, s->send_buffer);
AV_WL16(pkt->data + 2, s->video_chunk_size);
AV_WL16(pkt->data + 4, s->decode_map_chunk_size);
+ AV_WL16(pkt->data + 6, s->skip_map_chunk_size);
s->frame_format = 0;
s->send_buffer = 0;
@@ -189,24 +192,39 @@ static int load_ipmovie_packet(IPMVEContext *s, AVIOContext *pb,
avio_seek(pb, s->video_chunk_offset, SEEK_SET);
s->video_chunk_offset = 0;
- if (avio_read(pb, pkt->data + 6, s->video_chunk_size) !=
+ if (avio_read(pb, pkt->data + 8, s->video_chunk_size) !=
s->video_chunk_size) {
av_packet_unref(pkt);
return CHUNK_EOF;
}
- pkt->pos = s->decode_map_chunk_offset;
- avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET);
- s->decode_map_chunk_offset = 0;
+ if (s->decode_map_chunk_size) {
+ pkt->pos = s->decode_map_chunk_offset;
+ avio_seek(pb, s->decode_map_chunk_offset, SEEK_SET);
+ s->decode_map_chunk_offset = 0;
- if (avio_read(pb, pkt->data + 6 + s->video_chunk_size,
- s->decode_map_chunk_size) != s->decode_map_chunk_size) {
- av_packet_unref(pkt);
- return CHUNK_EOF;
+ if (avio_read(pb, pkt->data + 8 + s->video_chunk_size,
+ s->decode_map_chunk_size) != s->decode_map_chunk_size) {
+ av_packet_unref(pkt);
+ return CHUNK_EOF;
+ }
+ }
+
+ if (s->skip_map_chunk_size) {
+ pkt->pos = s->skip_map_chunk_offset;
+ avio_seek(pb, s->skip_map_chunk_offset, SEEK_SET);
+ s->skip_map_chunk_offset = 0;
+
+ if (avio_read(pb, pkt->data + 8 + s->video_chunk_size + s->decode_map_chunk_size,
+ s->skip_map_chunk_size) != s->skip_map_chunk_size) {
+ av_packet_unref(pkt);
+ return CHUNK_EOF;
+ }
}
s->video_chunk_size = 0;
s->decode_map_chunk_size = 0;
+ s->skip_map_chunk_size = 0;
pkt->stream_index = s->video_stream_index;
pkt->pts = s->video_pts;
@@ -444,8 +462,6 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
s->video_width, s->video_height);
break;
- case OPCODE_UNKNOWN_0E:
- case OPCODE_UNKNOWN_10:
case OPCODE_UNKNOWN_12:
case OPCODE_UNKNOWN_13:
case OPCODE_UNKNOWN_14:
@@ -527,6 +543,15 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
avio_skip(pb, opcode_size);
break;
+ case OPCODE_SET_SKIP_MAP:
+ av_log(s->avf, AV_LOG_TRACE, "set skip map\n");
+
+ /* log position and move on for now */
+ s->skip_map_chunk_offset = avio_tell(pb);
+ s->skip_map_chunk_size = opcode_size;
+ avio_skip(pb, opcode_size);
+ break;
+
case OPCODE_SET_DECODING_MAP:
av_log(s->avf, AV_LOG_TRACE, "set decoding map\n");
@@ -546,6 +571,16 @@ static int process_ipmovie_chunk(IPMVEContext *s, AVIOContext *pb,
avio_skip(pb, opcode_size);
break;
+ case OPCODE_VIDEO_DATA_10:
+ av_log(s->avf, AV_LOG_TRACE, "set video data format 0x10\n");
+ s->frame_format = 0x10;
+
+ /* log position and move on for now */
+ s->video_chunk_offset = avio_tell(pb);
+ s->video_chunk_size = opcode_size;
+ avio_skip(pb, opcode_size);
+ break;
+
case OPCODE_VIDEO_DATA_11:
av_log(s->avf, AV_LOG_TRACE, "set video data format 0x11\n");
s->frame_format = 0x11;
@@ -614,8 +649,9 @@ static int ipmovie_read_header(AVFormatContext *s)
/* initialize private context members */
ipmovie->video_pts = ipmovie->audio_frame_count = 0;
ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset =
- ipmovie->decode_map_chunk_offset = 0;
- ipmovie->decode_map_chunk_size = ipmovie->video_chunk_size = 0;
+ ipmovie->decode_map_chunk_offset = ipmovie->skip_map_chunk_offset = 0;
+ ipmovie->decode_map_chunk_size = ipmovie->video_chunk_size =
+ ipmovie->skip_map_chunk_size = 0;
ipmovie->send_buffer = ipmovie->frame_format = 0;
/* on the first read, this will position the stream at the first chunk */