libavcodec/interplayvideo.c
3ef8be2b
 /*
  * Interplay MVE Video Decoder
  * Copyright (C) 2003 the ffmpeg project
  *
b78e7197
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
3ef8be2b
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
b78e7197
  * version 2.1 of the License, or (at your option) any later version.
3ef8be2b
  *
b78e7197
  * FFmpeg is distributed in the hope that it will be useful,
3ef8be2b
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
b78e7197
  * License along with FFmpeg; if not, write to the Free Software
5509bffa
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3ef8be2b
  */
 
 /**
ba87f080
  * @file
186447f8
  * Interplay MVE Video Decoder by Mike Melanson (melanson@pcisys.net)
3ef8be2b
  * For more information about the Interplay MVE format, visit:
186447f8
  *   http://www.pcisys.net/~melanson/codecs/interplay-mve.txt
  * This code is written in such a way that the identifiers match up
  * with the encoding descriptions in the document.
  *
  * This decoder presently only supports a PAL8 output colorspace.
  *
  * An Interplay video frame consists of 2 parts: The decoding map and
  * the video data. A demuxer must load these 2 parts together in a single
  * buffer before sending it through the stream to this decoder.
3ef8be2b
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "avcodec.h"
2c124cb6
 #include "bytestream.h"
04a75bb7
 #include "hpeldsp.h"
aaf47bcd
 #define BITSTREAM_READER_LE
6000439c
 #include "get_bits.h"
594d4d5d
 #include "internal.h"
3ef8be2b
 
186447f8
 #define PALETTE_COUNT 256
 
3ef8be2b
 typedef struct IpvideoContext {
 
     AVCodecContext *avctx;
04a75bb7
     HpelDSPContext hdsp;
759001c5
     AVFrame *second_last_frame;
     AVFrame *last_frame;
7993df65
     const unsigned char *decoding_map;
3ef8be2b
     int decoding_map_size;
 
8badb23e
     int is_16bpp;
f31a68e7
     GetByteContext stream_ptr, mv_ptr;
bf649a9d
     unsigned char *pixel_ptr;
     int line_inc;
     int stride;
     int upper_motion_limit_offset;
 
2d8591c2
     uint32_t pal[256];
3ef8be2b
 } IpvideoContext;
 
759001c5
 static int copy_from(IpvideoContext *s, AVFrame *src, AVFrame *dst, int delta_x, int delta_y)
bf649a9d
 {
759001c5
     int current_offset = s->pixel_ptr - dst->data[0];
     int motion_offset = current_offset + delta_y * dst->linesize[0]
5b8ebb3f
                        + delta_x * (1 + s->is_16bpp);
aadf8811
     if (motion_offset < 0) {
0c57f819
         av_log(s->avctx, AV_LOG_ERROR, "motion offset < 0 (%d)\n", motion_offset);
089b3d68
         return AVERROR_INVALIDDATA;
aadf8811
     } else if (motion_offset > s->upper_motion_limit_offset) {
0c57f819
         av_log(s->avctx, AV_LOG_ERROR, "motion offset above limit (%d >= %d)\n",
aadf8811
             motion_offset, s->upper_motion_limit_offset);
089b3d68
         return AVERROR_INVALIDDATA;
aadf8811
     }
2843502e
     if (src->data[0] == NULL) {
         av_log(s->avctx, AV_LOG_ERROR, "Invalid decode type, corrupted header?\n");
         return AVERROR(EINVAL);
     }
04a75bb7
     s->hdsp.put_pixels_tab[!s->is_16bpp][0](s->pixel_ptr, src->data[0] + motion_offset,
                                             dst->linesize[0], 8);
bf649a9d
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x0(IpvideoContext *s, AVFrame *frame)
bf649a9d
 {
759001c5
     return copy_from(s, s->last_frame, frame, 0, 0);
aadf8811
 }
bf649a9d
 
759001c5
 static int ipvideo_decode_block_opcode_0x1(IpvideoContext *s, AVFrame *frame)
aadf8811
 {
759001c5
     return copy_from(s, s->second_last_frame, frame, 0, 0);
bf649a9d
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x2(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     unsigned char B;
     int x, y;
 
bf649a9d
     /* copy block from 2 frames ago using a motion vector; need 1 more byte */
dcfb0cad
     if (!s->is_16bpp) {
f31a68e7
         B = bytestream2_get_byte(&s->stream_ptr);
dcfb0cad
     } else {
f31a68e7
         B = bytestream2_get_byte(&s->mv_ptr);
dcfb0cad
     }
186447f8
 
     if (B < 56) {
         x = 8 + (B % 7);
         y = B / 7;
     } else {
         x = -14 + ((B - 56) % 29);
         y =   8 + ((B - 56) / 29);
     }
 
e730036f
     av_dlog(s->avctx, "motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
759001c5
     return copy_from(s, s->second_last_frame, frame, x, y);
186447f8
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x3(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     unsigned char B;
     int x, y;
 
     /* copy 8x8 block from current frame from an up/left block */
 
     /* need 1 more byte for motion */
dcfb0cad
     if (!s->is_16bpp) {
f31a68e7
         B = bytestream2_get_byte(&s->stream_ptr);
dcfb0cad
     } else {
f31a68e7
         B = bytestream2_get_byte(&s->mv_ptr);
dcfb0cad
     }
186447f8
 
     if (B < 56) {
         x = -(8 + (B % 7));
         y = -(B / 7);
     } else {
         x = -(-14 + ((B - 56) % 29));
         y = -(  8 + ((B - 56) / 29));
     }
 
e730036f
     av_dlog(s->avctx, "motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
759001c5
     return copy_from(s, frame, frame, x, y);
186447f8
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x4(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
     unsigned char B, BL, BH;
 
     /* copy a block from the previous frame; need 1 more byte */
dcfb0cad
     if (!s->is_16bpp) {
f31a68e7
         B = bytestream2_get_byte(&s->stream_ptr);
dcfb0cad
     } else {
f31a68e7
         B = bytestream2_get_byte(&s->mv_ptr);
dcfb0cad
     }
186447f8
 
     BL = B & 0x0F;
     BH = (B >> 4) & 0x0F;
     x = -8 + BL;
     y = -8 + BH;
 
e730036f
     av_dlog(s->avctx, "motion byte = %d, (x, y) = (%d, %d)\n", B, x, y);
759001c5
     return copy_from(s, s->last_frame, frame, x, y);
186447f8
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x5(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     signed char x, y;
 
     /* copy a block from the previous frame using an expanded range;
      * need 2 more bytes */
f31a68e7
     x = bytestream2_get_byte(&s->stream_ptr);
     y = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
e730036f
     av_dlog(s->avctx, "motion bytes = %d, %d\n", x, y);
759001c5
     return copy_from(s, s->last_frame, frame, x, y);
186447f8
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x6(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     /* mystery opcode? skip multiple blocks? */
0c57f819
     av_log(s->avctx, AV_LOG_ERROR, "Help! Mystery opcode 0x6 seen\n");
186447f8
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x7(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
9be06a0e
     unsigned char P[2];
186447f8
     unsigned int flags;
 
     /* 2-color encoding */
f31a68e7
     P[0] = bytestream2_get_byte(&s->stream_ptr);
     P[1] = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
9be06a0e
     if (P[0] <= P[1]) {
186447f8
 
         /* need 8 more bytes from the stream */
         for (y = 0; y < 8; y++) {
f31a68e7
             flags = bytestream2_get_byte(&s->stream_ptr) | 0x100;
dd9630e4
             for (; flags != 1; flags >>= 1)
f02bd7e4
                 *s->pixel_ptr++ = P[flags & 1];
bf649a9d
             s->pixel_ptr += s->line_inc;
186447f8
         }
 
     } else {
 
         /* need 2 more bytes from the stream */
f31a68e7
         flags = bytestream2_get_le16(&s->stream_ptr);
186447f8
         for (y = 0; y < 8; y += 2) {
e3114eb1
             for (x = 0; x < 8; x += 2, flags >>= 1) {
51e35028
                 s->pixel_ptr[x                ] =
                 s->pixel_ptr[x + 1            ] =
                 s->pixel_ptr[x +     s->stride] =
                 s->pixel_ptr[x + 1 + s->stride] = P[flags & 1];
186447f8
             }
bf649a9d
             s->pixel_ptr += s->stride * 2;
186447f8
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x8(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
f31a68e7
     unsigned char P[4];
186447f8
     unsigned int flags = 0;
 
     /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
      * either top and bottom or left and right halves */
f31a68e7
     P[0] = bytestream2_get_byte(&s->stream_ptr);
     P[1] = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
     if (P[0] <= P[1]) {
283531a8
         for (y = 0; y < 16; y++) {
             // new values for each 4x4 block
             if (!(y & 3)) {
f31a68e7
                 if (y) {
                     P[0]  = bytestream2_get_byte(&s->stream_ptr);
                     P[1]  = bytestream2_get_byte(&s->stream_ptr);
                 }
                 flags = bytestream2_get_le16(&s->stream_ptr);
186447f8
             }
 
dd9630e4
             for (x = 0; x < 4; x++, flags >>= 1)
283531a8
                 *s->pixel_ptr++ = P[flags & 1];
             s->pixel_ptr += s->stride - 4;
             // switch to right half
             if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
186447f8
         }
 
     } else {
f31a68e7
         flags = bytestream2_get_le32(&s->stream_ptr);
         P[2] = bytestream2_get_byte(&s->stream_ptr);
         P[3] = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
f31a68e7
         if (P[2] <= P[3]) {
0312b77c
 
186447f8
             /* vertical split; left & right halves are 2-color encoded */
 
283531a8
             for (y = 0; y < 16; y++) {
dd9630e4
                 for (x = 0; x < 4; x++, flags >>= 1)
283531a8
                     *s->pixel_ptr++ = P[flags & 1];
                 s->pixel_ptr += s->stride - 4;
                 // switch to right half
                 if (y == 7) {
                     s->pixel_ptr -= 8 * s->stride - 4;
f31a68e7
                     P[0]  = P[2];
                     P[1]  = P[3];
                     flags = bytestream2_get_le32(&s->stream_ptr);
186447f8
                 }
             }
 
         } else {
 
             /* horizontal split; top & bottom halves are 2-color encoded */
 
             for (y = 0; y < 8; y++) {
0312b77c
                 if (y == 4) {
f31a68e7
                     P[0]  = P[2];
                     P[1]  = P[3];
                     flags = bytestream2_get_le32(&s->stream_ptr);
186447f8
                 }
 
f31a68e7
                 for (x = 0; x < 8; x++, flags >>= 1)
f02bd7e4
                     *s->pixel_ptr++ = P[flags & 1];
bf649a9d
                 s->pixel_ptr += s->line_inc;
186447f8
             }
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x9(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
     unsigned char P[4];
 
     /* 4-color encoding */
f31a68e7
     bytestream2_get_buffer(&s->stream_ptr, P, 4);
186447f8
 
268a618b
     if (P[0] <= P[1]) {
         if (P[2] <= P[3]) {
186447f8
 
9aaacc24
             /* 1 of 4 colors for each pixel, need 16 more bytes */
             for (y = 0; y < 8; y++) {
                 /* get the next set of 8 2-bit flags */
f31a68e7
                 int flags = bytestream2_get_le16(&s->stream_ptr);
dd9630e4
                 for (x = 0; x < 8; x++, flags >>= 2)
9aaacc24
                     *s->pixel_ptr++ = P[flags & 0x03];
                 s->pixel_ptr += s->line_inc;
186447f8
             }
 
268a618b
         } else {
9aaacc24
             uint32_t flags;
186447f8
 
9aaacc24
             /* 1 of 4 colors for each 2x2 block, need 4 more bytes */
f31a68e7
             flags = bytestream2_get_le32(&s->stream_ptr);
186447f8
 
9aaacc24
             for (y = 0; y < 8; y += 2) {
                 for (x = 0; x < 8; x += 2, flags >>= 2) {
                     s->pixel_ptr[x                ] =
                     s->pixel_ptr[x + 1            ] =
                     s->pixel_ptr[x +     s->stride] =
                     s->pixel_ptr[x + 1 + s->stride] = P[flags & 0x03];
                 }
                 s->pixel_ptr += s->stride * 2;
186447f8
             }
 
268a618b
         }
     } else {
0448b78b
         uint64_t flags;
186447f8
 
1548839e
         /* 1 of 4 colors for each 2x1 or 1x2 block, need 8 more bytes */
f31a68e7
         flags = bytestream2_get_le64(&s->stream_ptr);
1548839e
         if (P[2] <= P[3]) {
9aaacc24
             for (y = 0; y < 8; y++) {
                 for (x = 0; x < 8; x += 2, flags >>= 2) {
                     s->pixel_ptr[x    ] =
                     s->pixel_ptr[x + 1] = P[flags & 0x03];
                 }
                 s->pixel_ptr += s->stride;
186447f8
             }
268a618b
         } else {
9aaacc24
             for (y = 0; y < 8; y += 2) {
                 for (x = 0; x < 8; x++, flags >>= 2) {
                     s->pixel_ptr[x            ] =
                     s->pixel_ptr[x + s->stride] = P[flags & 0x03];
                 }
                 s->pixel_ptr += s->stride * 2;
186447f8
             }
268a618b
         }
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xA(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
f31a68e7
     unsigned char P[8];
186447f8
     int flags = 0;
 
f31a68e7
     bytestream2_get_buffer(&s->stream_ptr, P, 4);
 
186447f8
     /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
      * either top and bottom or left and right halves */
f31a68e7
     if (P[0] <= P[1]) {
186447f8
 
49a20ffa
         /* 4-color encoding for each quadrant; need 32 bytes */
5e8b83ac
         for (y = 0; y < 16; y++) {
             // new values for each 4x4 block
             if (!(y & 3)) {
f31a68e7
                 if (y) bytestream2_get_buffer(&s->stream_ptr, P, 4);
                 flags = bytestream2_get_le32(&s->stream_ptr);
5e8b83ac
             }
186447f8
 
dd9630e4
             for (x = 0; x < 4; x++, flags >>= 2)
5e8b83ac
                 *s->pixel_ptr++ = P[flags & 0x03];
186447f8
 
5e8b83ac
             s->pixel_ptr += s->stride - 4;
             // switch to right half
             if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
186447f8
         }
 
     } else {
49a20ffa
         // vertical split?
f31a68e7
         int vert;
         uint64_t flags = bytestream2_get_le64(&s->stream_ptr);
 
         bytestream2_get_buffer(&s->stream_ptr, P + 4, 4);
         vert = P[4] <= P[5];
186447f8
 
         /* 4-color encoding for either left and right or top and bottom
49a20ffa
          * halves */
186447f8
 
49a20ffa
         for (y = 0; y < 16; y++) {
             for (x = 0; x < 4; x++, flags >>= 2)
                 *s->pixel_ptr++ = P[flags & 0x03];
186447f8
 
49a20ffa
             if (vert) {
                 s->pixel_ptr += s->stride - 4;
                 // switch to right half
                 if (y == 7) s->pixel_ptr -= 8 * s->stride - 4;
             } else if (y & 1) s->pixel_ptr += s->line_inc;
f31a68e7
 
             // load values for second half
             if (y == 7) {
                 memcpy(P, P + 4, 4);
                 flags = bytestream2_get_le64(&s->stream_ptr);
             }
186447f8
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xB(IpvideoContext *s, AVFrame *frame)
186447f8
 {
03b269dd
     int y;
186447f8
 
     /* 64-color encoding (each pixel in block is a different color) */
     for (y = 0; y < 8; y++) {
f31a68e7
         bytestream2_get_buffer(&s->stream_ptr, s->pixel_ptr, 8);
03b269dd
         s->pixel_ptr  += s->stride;
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xC(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
 
     /* 16-color block encoding: each 2x2 block is a different color */
     for (y = 0; y < 8; y += 2) {
         for (x = 0; x < 8; x += 2) {
32eea24e
             s->pixel_ptr[x                ] =
             s->pixel_ptr[x + 1            ] =
             s->pixel_ptr[x +     s->stride] =
f31a68e7
             s->pixel_ptr[x + 1 + s->stride] = bytestream2_get_byte(&s->stream_ptr);
186447f8
         }
bf649a9d
         s->pixel_ptr += s->stride * 2;
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xD(IpvideoContext *s, AVFrame *frame)
186447f8
 {
03b269dd
     int y;
8514f842
     unsigned char P[2];
186447f8
 
     /* 4-color block encoding: each 4x4 block is a different color */
8514f842
     for (y = 0; y < 8; y++) {
         if (!(y & 3)) {
f31a68e7
             P[0] = bytestream2_get_byte(&s->stream_ptr);
             P[1] = bytestream2_get_byte(&s->stream_ptr);
8514f842
         }
         memset(s->pixel_ptr,     P[0], 4);
         memset(s->pixel_ptr + 4, P[1], 4);
03b269dd
         s->pixel_ptr += s->stride;
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xE(IpvideoContext *s, AVFrame *frame)
186447f8
 {
03b269dd
     int y;
186447f8
     unsigned char pix;
 
     /* 1-color encoding: the whole block is 1 solid color */
f31a68e7
     pix = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
     for (y = 0; y < 8; y++) {
03b269dd
         memset(s->pixel_ptr, pix, 8);
         s->pixel_ptr += s->stride;
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xF(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
9be06a0e
     unsigned char sample[2];
186447f8
 
     /* dithered encoding */
f31a68e7
     sample[0] = bytestream2_get_byte(&s->stream_ptr);
     sample[1] = bytestream2_get_byte(&s->stream_ptr);
186447f8
 
     for (y = 0; y < 8; y++) {
         for (x = 0; x < 8; x += 2) {
9be06a0e
             *s->pixel_ptr++ = sample[  y & 1 ];
             *s->pixel_ptr++ = sample[!(y & 1)];
186447f8
         }
bf649a9d
         s->pixel_ptr += s->line_inc;
186447f8
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x6_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     signed char x, y;
 
     /* copy a block from the second last frame using an expanded range */
f31a68e7
     x = bytestream2_get_byte(&s->stream_ptr);
     y = bytestream2_get_byte(&s->stream_ptr);
a69be798
 
e730036f
     av_dlog(s->avctx, "motion bytes = %d, %d\n", x, y);
759001c5
     return copy_from(s, s->second_last_frame, frame, x, y);
a69be798
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x7_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t P[2];
     unsigned int flags;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 2-color encoding */
f31a68e7
     P[0] = bytestream2_get_le16(&s->stream_ptr);
     P[1] = bytestream2_get_le16(&s->stream_ptr);
a69be798
 
     if (!(P[0] & 0x8000)) {
 
         for (y = 0; y < 8; y++) {
f31a68e7
             flags = bytestream2_get_byte(&s->stream_ptr) | 0x100;
a69be798
             for (; flags != 1; flags >>= 1)
                 *pixel_ptr++ = P[flags & 1];
             pixel_ptr += s->line_inc;
         }
 
     } else {
 
f31a68e7
         flags = bytestream2_get_le16(&s->stream_ptr);
a69be798
         for (y = 0; y < 8; y += 2) {
             for (x = 0; x < 8; x += 2, flags >>= 1) {
                 pixel_ptr[x                ] =
                 pixel_ptr[x + 1            ] =
                 pixel_ptr[x +     s->stride] =
                 pixel_ptr[x + 1 + s->stride] = P[flags & 1];
             }
             pixel_ptr += s->stride * 2;
         }
     }
 
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x8_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
f31a68e7
     uint16_t P[4];
a69be798
     unsigned int flags = 0;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 2-color encoding for each 4x4 quadrant, or 2-color encoding on
      * either top and bottom or left and right halves */
f31a68e7
     P[0] = bytestream2_get_le16(&s->stream_ptr);
     P[1] = bytestream2_get_le16(&s->stream_ptr);
a69be798
 
     if (!(P[0] & 0x8000)) {
 
         for (y = 0; y < 16; y++) {
             // new values for each 4x4 block
             if (!(y & 3)) {
f31a68e7
                 if (y) {
                     P[0] = bytestream2_get_le16(&s->stream_ptr);
                     P[1] = bytestream2_get_le16(&s->stream_ptr);
                 }
                 flags = bytestream2_get_le16(&s->stream_ptr);
a69be798
             }
 
             for (x = 0; x < 4; x++, flags >>= 1)
                 *pixel_ptr++ = P[flags & 1];
             pixel_ptr += s->stride - 4;
             // switch to right half
             if (y == 7) pixel_ptr -= 8 * s->stride - 4;
         }
 
     } else {
 
f31a68e7
         flags = bytestream2_get_le32(&s->stream_ptr);
         P[2]  = bytestream2_get_le16(&s->stream_ptr);
         P[3]  = bytestream2_get_le16(&s->stream_ptr);
a69be798
 
f31a68e7
         if (!(P[2] & 0x8000)) {
a69be798
 
             /* vertical split; left & right halves are 2-color encoded */
 
             for (y = 0; y < 16; y++) {
                 for (x = 0; x < 4; x++, flags >>= 1)
                     *pixel_ptr++ = P[flags & 1];
                 pixel_ptr += s->stride - 4;
                 // switch to right half
                 if (y == 7) {
                     pixel_ptr -= 8 * s->stride - 4;
f31a68e7
                     P[0]  = P[2];
                     P[1]  = P[3];
                     flags = bytestream2_get_le32(&s->stream_ptr);
a69be798
                 }
             }
 
         } else {
 
             /* horizontal split; top & bottom halves are 2-color encoded */
 
             for (y = 0; y < 8; y++) {
                 if (y == 4) {
f31a68e7
                     P[0]  = P[2];
                     P[1]  = P[3];
                     flags = bytestream2_get_le32(&s->stream_ptr);
a69be798
                 }
 
f31a68e7
                 for (x = 0; x < 8; x++, flags >>= 1)
a69be798
                     *pixel_ptr++ = P[flags & 1];
                 pixel_ptr += s->line_inc;
             }
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0x9_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t P[4];
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 4-color encoding */
     for (x = 0; x < 4; x++)
f31a68e7
         P[x] = bytestream2_get_le16(&s->stream_ptr);
a69be798
 
     if (!(P[0] & 0x8000)) {
         if (!(P[2] & 0x8000)) {
 
             /* 1 of 4 colors for each pixel */
             for (y = 0; y < 8; y++) {
                 /* get the next set of 8 2-bit flags */
f31a68e7
                 int flags = bytestream2_get_le16(&s->stream_ptr);
a69be798
                 for (x = 0; x < 8; x++, flags >>= 2)
                     *pixel_ptr++ = P[flags & 0x03];
                 pixel_ptr += s->line_inc;
             }
 
         } else {
             uint32_t flags;
 
             /* 1 of 4 colors for each 2x2 block */
f31a68e7
             flags = bytestream2_get_le32(&s->stream_ptr);
a69be798
 
             for (y = 0; y < 8; y += 2) {
                 for (x = 0; x < 8; x += 2, flags >>= 2) {
                     pixel_ptr[x                ] =
                     pixel_ptr[x + 1            ] =
                     pixel_ptr[x +     s->stride] =
                     pixel_ptr[x + 1 + s->stride] = P[flags & 0x03];
                 }
                 pixel_ptr += s->stride * 2;
             }
 
         }
     } else {
         uint64_t flags;
 
         /* 1 of 4 colors for each 2x1 or 1x2 block */
f31a68e7
         flags = bytestream2_get_le64(&s->stream_ptr);
a69be798
         if (!(P[2] & 0x8000)) {
             for (y = 0; y < 8; y++) {
                 for (x = 0; x < 8; x += 2, flags >>= 2) {
                     pixel_ptr[x    ] =
                     pixel_ptr[x + 1] = P[flags & 0x03];
                 }
                 pixel_ptr += s->stride;
             }
         } else {
             for (y = 0; y < 8; y += 2) {
                 for (x = 0; x < 8; x++, flags >>= 2) {
                     pixel_ptr[x            ] =
                     pixel_ptr[x + s->stride] = P[flags & 0x03];
                 }
                 pixel_ptr += s->stride * 2;
             }
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xA_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
f31a68e7
     uint16_t P[8];
a69be798
     int flags = 0;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
f31a68e7
     for (x = 0; x < 4; x++)
         P[x] = bytestream2_get_le16(&s->stream_ptr);
 
a69be798
     /* 4-color encoding for each 4x4 quadrant, or 4-color encoding on
      * either top and bottom or left and right halves */
f31a68e7
     if (!(P[0] & 0x8000)) {
a69be798
 
         /* 4-color encoding for each quadrant */
         for (y = 0; y < 16; y++) {
             // new values for each 4x4 block
             if (!(y & 3)) {
f31a68e7
                 if (y)
                     for (x = 0; x < 4; x++)
                         P[x] = bytestream2_get_le16(&s->stream_ptr);
                 flags = bytestream2_get_le32(&s->stream_ptr);
a69be798
             }
 
             for (x = 0; x < 4; x++, flags >>= 2)
                 *pixel_ptr++ = P[flags & 0x03];
 
             pixel_ptr += s->stride - 4;
             // switch to right half
             if (y == 7) pixel_ptr -= 8 * s->stride - 4;
         }
 
     } else {
         // vertical split?
f31a68e7
         int vert;
         uint64_t flags = bytestream2_get_le64(&s->stream_ptr);
 
         for (x = 4; x < 8; x++)
             P[x] = bytestream2_get_le16(&s->stream_ptr);
         vert = !(P[4] & 0x8000);
a69be798
 
         /* 4-color encoding for either left and right or top and bottom
          * halves */
 
         for (y = 0; y < 16; y++) {
             for (x = 0; x < 4; x++, flags >>= 2)
                 *pixel_ptr++ = P[flags & 0x03];
 
             if (vert) {
                 pixel_ptr += s->stride - 4;
                 // switch to right half
                 if (y == 7) pixel_ptr -= 8 * s->stride - 4;
             } else if (y & 1) pixel_ptr += s->line_inc;
f31a68e7
 
             // load values for second half
             if (y == 7) {
                 memcpy(P, P + 4, 8);
                 flags = bytestream2_get_le64(&s->stream_ptr);
             }
a69be798
         }
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xB_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 64-color encoding (each pixel in block is a different color) */
     for (y = 0; y < 8; y++) {
         for (x = 0; x < 8; x++)
f31a68e7
             pixel_ptr[x] = bytestream2_get_le16(&s->stream_ptr);
a69be798
         pixel_ptr  += s->stride;
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xC_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 16-color block encoding: each 2x2 block is a different color */
     for (y = 0; y < 8; y += 2) {
         for (x = 0; x < 8; x += 2) {
             pixel_ptr[x                ] =
             pixel_ptr[x + 1            ] =
             pixel_ptr[x +     s->stride] =
f31a68e7
             pixel_ptr[x + 1 + s->stride] = bytestream2_get_le16(&s->stream_ptr);
a69be798
         }
         pixel_ptr += s->stride * 2;
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xD_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t P[2];
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 4-color block encoding: each 4x4 block is a different color */
     for (y = 0; y < 8; y++) {
         if (!(y & 3)) {
f31a68e7
             P[0] = bytestream2_get_le16(&s->stream_ptr);
             P[1] = bytestream2_get_le16(&s->stream_ptr);
a69be798
         }
         for (x = 0; x < 8; x++)
             pixel_ptr[x] = P[x >> 2];
         pixel_ptr += s->stride;
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int ipvideo_decode_block_opcode_0xE_16(IpvideoContext *s, AVFrame *frame)
a69be798
 {
     int x, y;
     uint16_t pix;
     uint16_t *pixel_ptr = (uint16_t*)s->pixel_ptr;
 
     /* 1-color encoding: the whole block is 1 solid color */
f31a68e7
     pix = bytestream2_get_le16(&s->stream_ptr);
a69be798
 
     for (y = 0; y < 8; y++) {
         for (x = 0; x < 8; x++)
             pixel_ptr[x] = pix;
         pixel_ptr += s->stride;
     }
 
     /* report success */
     return 0;
 }
 
759001c5
 static int (* const ipvideo_decode_block[])(IpvideoContext *s, AVFrame *frame) = {
fff60779
     ipvideo_decode_block_opcode_0x0, ipvideo_decode_block_opcode_0x1,
     ipvideo_decode_block_opcode_0x2, ipvideo_decode_block_opcode_0x3,
     ipvideo_decode_block_opcode_0x4, ipvideo_decode_block_opcode_0x5,
     ipvideo_decode_block_opcode_0x6, ipvideo_decode_block_opcode_0x7,
     ipvideo_decode_block_opcode_0x8, ipvideo_decode_block_opcode_0x9,
     ipvideo_decode_block_opcode_0xA, ipvideo_decode_block_opcode_0xB,
     ipvideo_decode_block_opcode_0xC, ipvideo_decode_block_opcode_0xD,
     ipvideo_decode_block_opcode_0xE, ipvideo_decode_block_opcode_0xF,
 };
186447f8
 
759001c5
 static int (* const ipvideo_decode_block16[])(IpvideoContext *s, AVFrame *frame) = {
a69be798
     ipvideo_decode_block_opcode_0x0,    ipvideo_decode_block_opcode_0x1,
     ipvideo_decode_block_opcode_0x2,    ipvideo_decode_block_opcode_0x3,
     ipvideo_decode_block_opcode_0x4,    ipvideo_decode_block_opcode_0x5,
     ipvideo_decode_block_opcode_0x6_16, ipvideo_decode_block_opcode_0x7_16,
     ipvideo_decode_block_opcode_0x8_16, ipvideo_decode_block_opcode_0x9_16,
     ipvideo_decode_block_opcode_0xA_16, ipvideo_decode_block_opcode_0xB_16,
     ipvideo_decode_block_opcode_0xC_16, ipvideo_decode_block_opcode_0xD_16,
     ipvideo_decode_block_opcode_0xE_16, ipvideo_decode_block_opcode_0x1,
 };
 
759001c5
 static void ipvideo_decode_opcodes(IpvideoContext *s, AVFrame *frame)
186447f8
 {
     int x, y;
     unsigned char opcode;
     int ret;
6000439c
     GetBitContext gb;
186447f8
 
f31a68e7
     bytestream2_skip(&s->stream_ptr, 14); /* data starts 14 bytes in */
5a8cf39e
     if (!s->is_16bpp) {
5b8ebb3f
         /* this is PAL8, so make the palette available */
759001c5
         memcpy(frame->data[1], s->pal, AVPALETTE_SIZE);
bf649a9d
 
759001c5
         s->stride = frame->linesize[0];
5a8cf39e
     } else {
759001c5
         s->stride = frame->linesize[0] >> 1;
f31a68e7
         s->mv_ptr = s->stream_ptr;
         bytestream2_skip(&s->mv_ptr, bytestream2_get_le16(&s->stream_ptr));
5a8cf39e
     }
bf649a9d
     s->line_inc = s->stride - 8;
759001c5
     s->upper_motion_limit_offset = (s->avctx->height - 8) * frame->linesize[0]
5b8ebb3f
                                   + (s->avctx->width - 8) * (1 + s->is_16bpp);
bf649a9d
 
6000439c
     init_get_bits(&gb, s->decoding_map, s->decoding_map_size * 8);
9156a5ad
     for (y = 0; y < s->avctx->height; y += 8) {
         for (x = 0; x < s->avctx->width; x += 8) {
6000439c
             opcode = get_bits(&gb, 4);
bf649a9d
 
9676d8eb
             av_dlog(s->avctx,
                     "  block @ (%3d, %3d): encoding 0x%X, data ptr offset %d\n",
                     x, y, opcode, bytestream2_tell(&s->stream_ptr));
bf649a9d
 
5a8cf39e
             if (!s->is_16bpp) {
759001c5
                 s->pixel_ptr = frame->data[0] + x
                               + y*frame->linesize[0];
                 ret = ipvideo_decode_block[opcode](s, frame);
5a8cf39e
             } else {
759001c5
                 s->pixel_ptr = frame->data[0] + x*2
                               + y*frame->linesize[0];
                 ret = ipvideo_decode_block16[opcode](s, frame);
5a8cf39e
             }
bf649a9d
             if (ret != 0) {
0c57f819
                 av_log(s->avctx, AV_LOG_ERROR, "decode problem on frame %d, @ block (%d, %d)\n",
3d973e46
                        s->avctx->frame_number, x, y);
bf649a9d
                 return;
186447f8
             }
         }
     }
f31a68e7
     if (bytestream2_get_bytes_left(&s->stream_ptr) > 1) {
         av_log(s->avctx, AV_LOG_ERROR,
9621646e
                "decode finished with %d bytes left over\n",
f31a68e7
                bytestream2_get_bytes_left(&s->stream_ptr));
bf649a9d
     }
186447f8
 }
 
98a6fff9
 static av_cold int ipvideo_decode_init(AVCodecContext *avctx)
3ef8be2b
 {
     IpvideoContext *s = avctx->priv_data;
 
     s->avctx = avctx;
186447f8
 
da937668
     s->is_16bpp = avctx->bits_per_coded_sample == 16;
716d413c
     avctx->pix_fmt = s->is_16bpp ? AV_PIX_FMT_RGB555 : AV_PIX_FMT_PAL8;
186447f8
 
04a75bb7
     ff_hpeldsp_init(&s->hdsp, avctx->flags);
3ef8be2b
 
759001c5
     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);
     }
d86e8568
 
3ef8be2b
     return 0;
 }
 
 static int ipvideo_decode_frame(AVCodecContext *avctx,
df9b9567
                                 void *data, int *got_frame,
7a00bbad
                                 AVPacket *avpkt)
3ef8be2b
 {
7a00bbad
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
3ef8be2b
     IpvideoContext *s = avctx->priv_data;
759001c5
     AVFrame *frame = data;
089b3d68
     int ret;
3ef8be2b
 
17aa02b9
     /* decoding map contains 4 bits of information per 8x8 block */
     s->decoding_map_size = avctx->width * avctx->height / (8 * 8 * 2);
 
18a508c5
     /* compressed buffer needs to be large enough to at least hold an entire
      * decoding map */
     if (buf_size < s->decoding_map_size)
         return buf_size;
 
80e9e63c
     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);
9df9420d
     }
 
186447f8
     s->decoding_map = buf;
f31a68e7
     bytestream2_init(&s->stream_ptr, buf + s->decoding_map_size,
                      buf_size - s->decoding_map_size);
3ef8be2b
 
1ec94b0f
     if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
089b3d68
         return ret;
3ef8be2b
 
2d8591c2
     if (!s->is_16bpp) {
         const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
         if (pal) {
759001c5
             frame->palette_has_changed = 1;
2d8591c2
             memcpy(s->pal, pal, AVPALETTE_SIZE);
         }
2a2bbcb0
     }
 
759001c5
     ipvideo_decode_opcodes(s, frame);
2d8591c2
 
df9b9567
     *got_frame = 1;
3ef8be2b
 
186447f8
     /* shuffle frames */
759001c5
     av_frame_unref(s->second_last_frame);
     FFSWAP(AVFrame*, s->second_last_frame, s->last_frame);
     if ((ret = av_frame_ref(s->last_frame, frame)) < 0)
         return ret;
3ef8be2b
 
186447f8
     /* report that the buffer was completely consumed */
3ef8be2b
     return buf_size;
 }
 
98a6fff9
 static av_cold int ipvideo_decode_end(AVCodecContext *avctx)
3ef8be2b
 {
     IpvideoContext *s = avctx->priv_data;
 
759001c5
     av_frame_free(&s->last_frame);
     av_frame_free(&s->second_last_frame);
3ef8be2b
 
     return 0;
 }
 
e7e2df27
 AVCodec ff_interplay_video_decoder = {
ec6402b7
     .name           = "interplayvideo",
b2bed932
     .long_name      = NULL_IF_CONFIG_SMALL("Interplay MVE video"),
ec6402b7
     .type           = AVMEDIA_TYPE_VIDEO,
36ef5369
     .id             = AV_CODEC_ID_INTERPLAY_VIDEO,
ec6402b7
     .priv_data_size = sizeof(IpvideoContext),
     .init           = ipvideo_decode_init,
     .close          = ipvideo_decode_end,
     .decode         = ipvideo_decode_frame,
17aa02b9
     .capabilities   = CODEC_CAP_DR1 | CODEC_CAP_PARAM_CHANGE,
3ef8be2b
 };