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 */