Subversion Repositories Kolibri OS

Rev

Go to most recent revision | 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.     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.     avcodec_get_frame_defaults(&s->frame);
  65.  
  66.     return 0;
  67. }
  68.  
  69. static int mm_decode_pal(MmContext *s)
  70. {
  71.     int i;
  72.  
  73.     bytestream2_skip(&s->gb, 4);
  74.     for (i = 0; i < 128; i++) {
  75.         s->palette[i] = 0xFFU << 24 | bytestream2_get_be24(&s->gb);
  76.         s->palette[i+128] = s->palette[i]<<2;
  77.     }
  78.  
  79.     return 0;
  80. }
  81.  
  82. /**
  83.  * @param half_horiz Half horizontal resolution (0 or 1)
  84.  * @param half_vert Half vertical resolution (0 or 1)
  85.  */
  86. static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert)
  87. {
  88.     int x = 0, y = 0;
  89.  
  90.     while (bytestream2_get_bytes_left(&s->gb) > 0) {
  91.         int run_length, color;
  92.  
  93.         if (y >= s->avctx->height)
  94.             return 0;
  95.  
  96.         color = bytestream2_get_byte(&s->gb);
  97.         if (color & 0x80) {
  98.             run_length = 1;
  99.         }else{
  100.             run_length = (color & 0x7f) + 2;
  101.             color = bytestream2_get_byte(&s->gb);
  102.         }
  103.  
  104.         if (half_horiz)
  105.             run_length *=2;
  106.  
  107.         if (run_length > s->avctx->width - x)
  108.             return AVERROR_INVALIDDATA;
  109.  
  110.         if (color) {
  111.             memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length);
  112.             if (half_vert)
  113.                 memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length);
  114.         }
  115.         x+= run_length;
  116.  
  117.         if (x >= s->avctx->width) {
  118.             x=0;
  119.             y += 1 + half_vert;
  120.         }
  121.     }
  122.  
  123.     return 0;
  124. }
  125.  
  126. /**
  127.  * @param half_horiz Half horizontal resolution (0 or 1)
  128.  * @param half_vert Half vertical resolution (0 or 1)
  129.  */
  130. static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert)
  131. {
  132.     int data_off = bytestream2_get_le16(&s->gb), y = 0;
  133.     GetByteContext data_ptr;
  134.  
  135.     if (bytestream2_get_bytes_left(&s->gb) < data_off)
  136.         return AVERROR_INVALIDDATA;
  137.  
  138.     bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off);
  139.     while (s->gb.buffer < data_ptr.buffer_start) {
  140.         int i, j;
  141.         int length = bytestream2_get_byte(&s->gb);
  142.         int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1);
  143.         length &= 0x7F;
  144.  
  145.         if (length==0) {
  146.             y += x;
  147.             continue;
  148.         }
  149.  
  150.         if (y + half_vert >= s->avctx->height)
  151.             return 0;
  152.  
  153.         for(i=0; i<length; i++) {
  154.             int replace_array = bytestream2_get_byte(&s->gb);
  155.             for(j=0; j<8; j++) {
  156.                 int replace = (replace_array >> (7-j)) & 1;
  157.                 if (x + half_horiz >= s->avctx->width)
  158.                     return AVERROR_INVALIDDATA;
  159.                 if (replace) {
  160.                     int color = bytestream2_get_byte(&data_ptr);
  161.                     s->frame.data[0][y*s->frame.linesize[0] + x] = color;
  162.                     if (half_horiz)
  163.                         s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color;
  164.                     if (half_vert) {
  165.                         s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color;
  166.                         if (half_horiz)
  167.                             s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color;
  168.                     }
  169.                 }
  170.                 x += 1 + half_horiz;
  171.             }
  172.         }
  173.  
  174.         y += 1 + half_vert;
  175.     }
  176.  
  177.     return 0;
  178. }
  179.  
  180. static int mm_decode_frame(AVCodecContext *avctx,
  181.                             void *data, int *got_frame,
  182.                             AVPacket *avpkt)
  183. {
  184.     const uint8_t *buf = avpkt->data;
  185.     int buf_size = avpkt->size;
  186.     MmContext *s = avctx->priv_data;
  187.     int type, res;
  188.  
  189.     if (buf_size < MM_PREAMBLE_SIZE)
  190.         return AVERROR_INVALIDDATA;
  191.     type = AV_RL16(&buf[0]);
  192.     buf += MM_PREAMBLE_SIZE;
  193.     buf_size -= MM_PREAMBLE_SIZE;
  194.     bytestream2_init(&s->gb, buf, buf_size);
  195.  
  196.     if ((res = ff_reget_buffer(avctx, &s->frame)) < 0)
  197.         return res;
  198.  
  199.     switch(type) {
  200.     case MM_TYPE_PALETTE   : res = mm_decode_pal(s); return avpkt->size;
  201.     case MM_TYPE_INTRA     : res = mm_decode_intra(s, 0, 0); break;
  202.     case MM_TYPE_INTRA_HH  : res = mm_decode_intra(s, 1, 0); break;
  203.     case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break;
  204.     case MM_TYPE_INTER     : res = mm_decode_inter(s, 0, 0); break;
  205.     case MM_TYPE_INTER_HH  : res = mm_decode_inter(s, 1, 0); break;
  206.     case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break;
  207.     default:
  208.         res = AVERROR_INVALIDDATA;
  209.         break;
  210.     }
  211.     if (res < 0)
  212.         return res;
  213.  
  214.     memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE);
  215.  
  216.     if ((res = av_frame_ref(data, &s->frame)) < 0)
  217.         return res;
  218.  
  219.     *got_frame      = 1;
  220.  
  221.     return avpkt->size;
  222. }
  223.  
  224. static av_cold int mm_decode_end(AVCodecContext *avctx)
  225. {
  226.     MmContext *s = avctx->priv_data;
  227.  
  228.     av_frame_unref(&s->frame);
  229.  
  230.     return 0;
  231. }
  232.  
  233. AVCodec ff_mmvideo_decoder = {
  234.     .name           = "mmvideo",
  235.     .long_name      = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"),
  236.     .type           = AVMEDIA_TYPE_VIDEO,
  237.     .id             = AV_CODEC_ID_MMVIDEO,
  238.     .priv_data_size = sizeof(MmContext),
  239.     .init           = mm_decode_init,
  240.     .close          = mm_decode_end,
  241.     .decode         = mm_decode_frame,
  242.     .capabilities   = CODEC_CAP_DR1,
  243. };
  244.