Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * American Laser Games MM Video Decoder
  3.  * Copyright (c) 2006,2008 Peter Ross
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. /**
  23.  * @file
  24.  * American Laser Games MM Video Decoder
  25.  * by Peter Ross (pross@xvid.org)
  26.  *
  27.  * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games,
  28.  * including Mad Dog McCree and Crime Patrol.
  29.  *
  30.  * Technical details here:
  31.  *  http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM
  32.  */
  33.  
  34. #include "libavutil/intreadwrite.h"
  35. #include "avcodec.h"
  36. #include "bytestream.h"
  37. #include "internal.h"
  38.  
  39. #define MM_PREAMBLE_SIZE    6
  40.  
  41. #define MM_TYPE_INTER       0x5
  42. #define MM_TYPE_INTRA       0x8
  43. #define MM_TYPE_INTRA_HH    0xc
  44. #define MM_TYPE_INTER_HH    0xd
  45. #define MM_TYPE_INTRA_HHV   0xe
  46. #define MM_TYPE_INTER_HHV   0xf
  47. #define MM_TYPE_PALETTE     0x31
  48.  
  49. typedef struct MmContext {
  50.     AVCodecContext *avctx;
  51.     AVFrame *frame;
  52.     unsigned int palette[AVPALETTE_COUNT];
  53.     GetByteContext gb;
  54. } MmContext;
  55.  
  56. static av_cold int mm_decode_init(AVCodecContext *avctx)
  57. {
  58.     MmContext *s = avctx->priv_data;
  59.  
  60.     s->avctx = avctx;
  61.  
  62.     avctx->pix_fmt = AV_PIX_FMT_PAL8;
  63.  
  64.     if (!avctx->width || !avctx->height ||
  65.         (avctx->width & 1) || (avctx->height & 1)) {
  66.         av_log(avctx, AV_LOG_ERROR, "Invalid video dimensions: %dx%d\n",
  67.                avctx->width, avctx->height);
  68.         return AVERROR(EINVAL);
  69.     }
  70.  
  71.     s->frame = av_frame_alloc();
  72.     if (!s->frame)
  73.         return AVERROR(ENOMEM);
  74.  
  75.     return 0;
  76. }
  77.  
  78. static void mm_decode_pal(MmContext *s)
  79. {
  80.     int i;
  81.  
  82.     bytestream2_skip(&s->gb, 4);
  83.     for (i = 0; i < 128; i++) {
  84.         s->palette[i] = 0xFFU << 24 | bytestream2_get_be24(&s->gb);
  85.         s->palette[i+128] = s->palette[i]<<2;
  86.     }
  87. }
  88.  
  89. /**
  90.  * @param half_horiz Half horizontal resolution (0 or 1)
  91.  * @param half_vert Half vertical resolution (0 or 1)
  92.  */
  93. static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert)
  94. {
  95.     int x = 0, y = 0;
  96.  
  97.     while (bytestream2_get_bytes_left(&s->gb) > 0) {
  98.         int run_length, color;
  99.  
  100.         if (y >= s->avctx->height)
  101.             return 0;
  102.  
  103.         color = bytestream2_get_byte(&s->gb);
  104.         if (color & 0x80) {
  105.             run_length = 1;
  106.         }else{
  107.             run_length = (color & 0x7f) + 2;
  108.             color = bytestream2_get_byte(&s->gb);
  109.         }
  110.  
  111.         if (half_horiz)
  112.             run_length *=2;
  113.  
  114.         if (run_length > s->avctx->width - x)
  115.             return AVERROR_INVALIDDATA;
  116.  
  117.         if (color) {
  118.             memset(s->frame->data[0] + y*s->frame->linesize[0] + x, color, run_length);
  119.             if (half_vert && y + half_vert < s->avctx->height)
  120.                 memset(s->frame->data[0] + (y+1)*s->frame->linesize[0] + x, color, run_length);
  121.         }
  122.         x+= run_length;
  123.  
  124.         if (x >= s->avctx->width) {
  125.             x=0;
  126.             y += 1 + half_vert;
  127.         }
  128.     }
  129.  
  130.     return 0;
  131. }
  132.  
  133. /**
  134.  * @param half_horiz Half horizontal resolution (0 or 1)
  135.  * @param half_vert Half vertical resolution (0 or 1)
  136.  */
  137. static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert)
  138. {
  139.     int data_off = bytestream2_get_le16(&s->gb);
  140.     int y = 0;
  141.     GetByteContext data_ptr;
  142.  
  143.     if (bytestream2_get_bytes_left(&s->gb) < data_off)
  144.         return AVERROR_INVALIDDATA;
  145.  
  146.     bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off);
  147.     while (s->gb.buffer < data_ptr.buffer_start) {
  148.         int i, j;
  149.         int length = bytestream2_get_byte(&s->gb);
  150.         int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1);
  151.         length &= 0x7F;
  152.  
  153.         if (length==0) {
  154.             y += x;
  155.             continue;
  156.         }
  157.  
  158.         if (y + half_vert >= s->avctx->height)
  159.             return 0;
  160.  
  161.         for(i=0; i<length; i++) {
  162.             int replace_array = bytestream2_get_byte(&s->gb);
  163.             for(j=0; j<8; j++) {
  164.                 int replace = (replace_array >> (7-j)) & 1;
  165.                 if (x + half_horiz >= s->avctx->width)
  166.                     return AVERROR_INVALIDDATA;
  167.                 if (replace) {
  168.                     int color = bytestream2_get_byte(&data_ptr);
  169.                     s->frame->data[0][y*s->frame->linesize[0] + x] = color;
  170.                     if (half_horiz)
  171.                         s->frame->data[0][y*s->frame->linesize[0] + x + 1] = color;
  172.                     if (half_vert) {
  173.                         s->frame->data[0][(y+1)*s->frame->linesize[0] + x] = color;
  174.                         if (half_horiz)
  175.                             s->frame->data[0][(y+1)*s->frame->linesize[0] + x + 1] = color;
  176.                     }
  177.                 }
  178.                 x += 1 + half_horiz;
  179.             }
  180.         }
  181.  
  182.         y += 1 + half_vert;
  183.     }
  184.  
  185.     return 0;
  186. }
  187.  
  188. static int mm_decode_frame(AVCodecContext *avctx,
  189.                             void *data, int *got_frame,
  190.                             AVPacket *avpkt)
  191. {
  192.     const uint8_t *buf = avpkt->data;
  193.     int buf_size = avpkt->size;
  194.     MmContext *s = avctx->priv_data;
  195.     int type, res;
  196.  
  197.     if (buf_size < MM_PREAMBLE_SIZE)
  198.         return AVERROR_INVALIDDATA;
  199.     type = AV_RL16(&buf[0]);
  200.     buf += MM_PREAMBLE_SIZE;
  201.     buf_size -= MM_PREAMBLE_SIZE;
  202.     bytestream2_init(&s->gb, buf, buf_size);
  203.  
  204.     if ((res = ff_reget_buffer(avctx, s->frame)) < 0)
  205.         return res;
  206.  
  207.     switch(type) {
  208.     case MM_TYPE_PALETTE   : mm_decode_pal(s); return avpkt->size;
  209.     case MM_TYPE_INTRA     : res = mm_decode_intra(s, 0, 0); break;
  210.     case MM_TYPE_INTRA_HH  : res = mm_decode_intra(s, 1, 0); break;
  211.     case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break;
  212.     case MM_TYPE_INTER     : res = mm_decode_inter(s, 0, 0); break;
  213.     case MM_TYPE_INTER_HH  : res = mm_decode_inter(s, 1, 0); break;
  214.     case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break;
  215.     default:
  216.         res = AVERROR_INVALIDDATA;
  217.         break;
  218.     }
  219.     if (res < 0)
  220.         return res;
  221.  
  222.     memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE);
  223.  
  224.     if ((res = av_frame_ref(data, s->frame)) < 0)
  225.         return res;
  226.  
  227.     *got_frame      = 1;
  228.  
  229.     return avpkt->size;
  230. }
  231.  
  232. static av_cold int mm_decode_end(AVCodecContext *avctx)
  233. {
  234.     MmContext *s = avctx->priv_data;
  235.  
  236.     av_frame_free(&s->frame);
  237.  
  238.     return 0;
  239. }
  240.  
  241. AVCodec ff_mmvideo_decoder = {
  242.     .name           = "mmvideo",
  243.     .long_name      = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"),
  244.     .type           = AVMEDIA_TYPE_VIDEO,
  245.     .id             = AV_CODEC_ID_MMVIDEO,
  246.     .priv_data_size = sizeof(MmContext),
  247.     .init           = mm_decode_init,
  248.     .close          = mm_decode_end,
  249.     .decode         = mm_decode_frame,
  250.     .capabilities   = AV_CODEC_CAP_DR1,
  251. };
  252.