]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/interplayvideo.c
avcodec/jpeg2000dsp: Fix multiple integer overflows in ict_int()
[ffmpeg] / libavcodec / interplayvideo.c
index df3314d4b70a7509f2ccfd0a74947c7ce1324960..deaa09cba60ff4e9319bb121c794b1ab8717be20 100644 (file)
@@ -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;
@@ -903,7 +910,193 @@ 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_format_06_firstpass(IpvideoContext *s, AVFrame *frame, int16_t 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;
+        }
+    } else {
+        /* Don't try to copy second_last_frame data on the first frames */
+        if (s->avctx->frame_number > 2)
+            copy_from(s, s->second_last_frame, frame, 0, 0);
+    }
+}
+
+static void ipvideo_format_06_secondpass(IpvideoContext *s, AVFrame *frame, int16_t opcode)
+{
+    int off_x, off_y;
+
+    if (opcode < 0) {
+        off_x = ((uint16_t)opcode - 0xC000) % frame->linesize[0];
+        off_y = ((uint16_t)opcode - 0xC000) / frame->linesize[0];
+        copy_from(s, s->last_frame, frame, off_x, off_y);
+    } else if (opcode > 0) {
+        off_x = ((uint16_t)opcode - 0x4000) % frame->linesize[0];
+        off_y = ((uint16_t)opcode - 0x4000) / frame->linesize[0];
+        copy_from(s, frame, frame, off_x, off_y);
+    }
+}
+
+static void (* const ipvideo_format_06_passes[])(IpvideoContext *s, AVFrame *frame, int16_t op) = {
+    ipvideo_format_06_firstpass, ipvideo_format_06_secondpass,
+};
+
+static void ipvideo_decode_format_06_opcodes(IpvideoContext *s, AVFrame *frame)
+{
+    int pass, x, y;
+    int16_t opcode;
+    GetByteContext decoding_map_ptr;
+
+    /* 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);
+
+    for (pass = 0; pass < 2; ++pass) {
+        bytestream2_seek(&decoding_map_ptr, 0, SEEK_SET);
+        for (y = 0; y < s->avctx->height; y += 8) {
+            for (x = 0; x < s->avctx->width; x += 8) {
+                opcode = bytestream2_get_le16(&decoding_map_ptr);
+
+                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];
+                ipvideo_format_06_passes[pass](s, frame, opcode);
+            }
+        }
+    }
+
+    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_format_10_firstpass(IpvideoContext *s, AVFrame *frame, int16_t 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, int16_t opcode)
+{
+    int off_x, off_y;
+
+    if (opcode < 0) {
+        off_x = ((uint16_t)opcode - 0xC000) % s->cur_decode_frame->linesize[0];
+        off_y = ((uint16_t)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 = ((uint16_t)opcode - 0x4000) % s->cur_decode_frame->linesize[0];
+        off_y = ((uint16_t)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, int16_t 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;
+    int16_t 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;
+                    }
+                    if (bytestream2_get_bytes_left(&skip_map_ptr) < 2)
+                        return;
+                    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;
+                }
+                if (bytestream2_get_bytes_left(&skip_map_ptr) < 2)
+                    return;
+                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;
     unsigned char opcode;
@@ -962,6 +1155,7 @@ static void ipvideo_decode_opcodes(IpvideoContext *s, AVFrame *frame)
 static av_cold int ipvideo_decode_init(AVCodecContext *avctx)
 {
     IpvideoContext *s = avctx->priv_data;
+    int ret;
 
     s->avctx = avctx;
 
@@ -972,13 +1166,36 @@ 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) {
-        av_frame_free(&s->last_frame);
-        av_frame_free(&s->second_last_frame);
-        return AVERROR(ENOMEM);
+    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) {
+        ret = AVERROR(ENOMEM);
+        goto error;
     }
 
+    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;
+
+    ret = ff_get_buffer(avctx, s->cur_decode_frame, 0);
+    if (ret < 0)
+        goto error;
+
+    ret = ff_get_buffer(avctx, s->prev_decode_frame, 0);
+    if (ret < 0)
+        goto error;
+
     return 0;
+error:
+    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 ret;
 }
 
 static int ipvideo_decode_frame(AVCodecContext *avctx,
@@ -990,27 +1207,122 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
     IpvideoContext *s = avctx->priv_data;
     AVFrame *frame = data;
     int ret;
+    int send_buffer;
+    int frame_format;
+    int video_data_size;
 
     if (av_packet_get_side_data(avpkt, AV_PKT_DATA_PARAM_CHANGE, NULL)) {
         av_frame_unref(s->last_frame);
         av_frame_unref(s->second_last_frame);
+        av_frame_unref(s->cur_decode_frame);
+        av_frame_unref(s->prev_decode_frame);
     }
 
-    if (buf_size < 2)
+    if (!s->cur_decode_frame->data[0]) {
+        ret = ff_get_buffer(avctx, s->cur_decode_frame, 0);
+        if (ret < 0)
+            return ret;
+
+        ret = ff_get_buffer(avctx, s->prev_decode_frame, 0);
+        if (ret < 0) {
+            av_frame_unref(s->cur_decode_frame);
+            return ret;
+        }
+    }
+
+    if (buf_size < 8)
         return AVERROR_INVALIDDATA;
 
-    /* decoding map contains 4 bits of information per 8x8 block */
-    s->decoding_map_size = AV_RL16(avpkt->data);
+    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:
+            if (s->decoding_map_size) {
+                av_log(avctx, AV_LOG_ERROR, "Decoding map for format 0x06\n");
+                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;
+            }
+
+            /* 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 + 8 + 14; /* 14 bits of op data */
+            video_data_size -= s->decoding_map_size + 14;
+            if (video_data_size <= 0)
+                return AVERROR_INVALIDDATA;
+
+            if (buf_size < 8 + s->decoding_map_size + 14 + video_data_size)
+                return AVERROR_INVALIDDATA;
 
-    /* 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;
+            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;
+            }
 
+            if (buf_size < 8 + video_data_size + s->decoding_map_size + s->skip_map_size)
+                return AVERROR_INVALIDDATA;
 
-    s->decoding_map = buf + 2;
-    bytestream2_init(&s->stream_ptr, buf + 2 + s->decoding_map_size,
-                     buf_size - s->decoding_map_size);
+            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;
+
+        case 0x11:
+            if (! s->decoding_map_size) {
+                av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x11\n");
+                return AVERROR_INVALIDDATA;
+            }
+
+            if (s->skip_map_size) {
+                av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x11\n");
+                return AVERROR_INVALIDDATA;
+            }
+
+            if (buf_size < 8 + video_data_size + s->decoding_map_size)
+                return AVERROR_INVALIDDATA;
+
+            bytestream2_init(&s->stream_ptr, buf + 8, video_data_size);
+            s->decoding_map = buf + 8 + video_data_size;
+
+            break;
+
+        default:
+            av_log(avctx, AV_LOG_ERROR, "Frame type 0x%02X unsupported\n", frame_format);
+    }
+
+    /* ensure we can't overread the packet */
+    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;
+    }
 
     if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
         return ret;
@@ -1026,9 +1338,19 @@ static int ipvideo_decode_frame(AVCodecContext *avctx,
         }
     }
 
-    ipvideo_decode_opcodes(s, frame);
+    switch(frame_format) {
+        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;
+    }
 
-    *got_frame = 1;
+    *got_frame = send_buffer;
 
     /* shuffle frames */
     av_frame_unref(s->second_last_frame);
@@ -1046,6 +1368,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;
 }