LCOV - code coverage report
Current view: top level - libavcodec - mmvideo.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 56 108 51.9 %
Date: 2017-12-13 10:57:33 Functions: 5 6 83.3 %

          Line data    Source code
       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           2 : static av_cold int mm_decode_init(AVCodecContext *avctx)
      57             : {
      58           2 :     MmContext *s = avctx->priv_data;
      59             : 
      60           2 :     s->avctx = avctx;
      61             : 
      62           2 :     avctx->pix_fmt = AV_PIX_FMT_PAL8;
      63             : 
      64           4 :     if (!avctx->width || !avctx->height ||
      65           4 :         (avctx->width & 1) || (avctx->height & 1)) {
      66           0 :         av_log(avctx, AV_LOG_ERROR, "Invalid video dimensions: %dx%d\n",
      67             :                avctx->width, avctx->height);
      68           0 :         return AVERROR(EINVAL);
      69             :     }
      70             : 
      71           2 :     s->frame = av_frame_alloc();
      72           2 :     if (!s->frame)
      73           0 :         return AVERROR(ENOMEM);
      74             : 
      75           2 :     return 0;
      76             : }
      77             : 
      78           1 : static void mm_decode_pal(MmContext *s)
      79             : {
      80             :     int i;
      81             : 
      82           1 :     bytestream2_skip(&s->gb, 4);
      83         129 :     for (i = 0; i < 128; i++) {
      84         128 :         s->palette[i] = 0xFFU << 24 | bytestream2_get_be24(&s->gb);
      85         128 :         s->palette[i+128] = s->palette[i]<<2;
      86             :     }
      87           1 : }
      88             : 
      89             : /**
      90             :  * @param half_horiz Half horizontal resolution (0 or 1)
      91             :  * @param half_vert Half vertical resolution (0 or 1)
      92             :  */
      93          31 : static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert)
      94             : {
      95          31 :     int x = 0, y = 0;
      96             : 
      97      168634 :     while (bytestream2_get_bytes_left(&s->gb) > 0) {
      98             :         int run_length, color;
      99             : 
     100      168572 :         if (y >= s->avctx->height)
     101           0 :             return 0;
     102             : 
     103      168572 :         color = bytestream2_get_byte(&s->gb);
     104      168572 :         if (color & 0x80) {
     105       92018 :             run_length = 1;
     106             :         }else{
     107       76554 :             run_length = (color & 0x7f) + 2;
     108       76554 :             color = bytestream2_get_byte(&s->gb);
     109             :         }
     110             : 
     111      168572 :         if (half_horiz)
     112           0 :             run_length *=2;
     113             : 
     114      168572 :         if (run_length > s->avctx->width - x)
     115           0 :             return AVERROR_INVALIDDATA;
     116             : 
     117      168572 :         if (color) {
     118      147172 :             memset(s->frame->data[0] + y*s->frame->linesize[0] + x, color, run_length);
     119      147172 :             if (half_vert && y + half_vert < s->avctx->height)
     120           0 :                 memset(s->frame->data[0] + (y+1)*s->frame->linesize[0] + x, color, run_length);
     121             :         }
     122      168572 :         x+= run_length;
     123             : 
     124      168572 :         if (x >= s->avctx->width) {
     125        4960 :             x=0;
     126        4960 :             y += 1 + half_vert;
     127             :         }
     128             :     }
     129             : 
     130          31 :     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           0 : static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert)
     138             : {
     139           0 :     int data_off = bytestream2_get_le16(&s->gb);
     140           0 :     int y = 0;
     141             :     GetByteContext data_ptr;
     142             : 
     143           0 :     if (bytestream2_get_bytes_left(&s->gb) < data_off)
     144           0 :         return AVERROR_INVALIDDATA;
     145             : 
     146           0 :     bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off);
     147           0 :     while (s->gb.buffer < data_ptr.buffer_start) {
     148             :         int i, j;
     149           0 :         int length = bytestream2_get_byte(&s->gb);
     150           0 :         int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1);
     151           0 :         length &= 0x7F;
     152             : 
     153           0 :         if (length==0) {
     154           0 :             y += x;
     155           0 :             continue;
     156             :         }
     157             : 
     158           0 :         if (y + half_vert >= s->avctx->height)
     159           0 :             return 0;
     160             : 
     161           0 :         for(i=0; i<length; i++) {
     162           0 :             int replace_array = bytestream2_get_byte(&s->gb);
     163           0 :             for(j=0; j<8; j++) {
     164           0 :                 int replace = (replace_array >> (7-j)) & 1;
     165           0 :                 if (x + half_horiz >= s->avctx->width)
     166           0 :                     return AVERROR_INVALIDDATA;
     167           0 :                 if (replace) {
     168           0 :                     int color = bytestream2_get_byte(&data_ptr);
     169           0 :                     s->frame->data[0][y*s->frame->linesize[0] + x] = color;
     170           0 :                     if (half_horiz)
     171           0 :                         s->frame->data[0][y*s->frame->linesize[0] + x + 1] = color;
     172           0 :                     if (half_vert) {
     173           0 :                         s->frame->data[0][(y+1)*s->frame->linesize[0] + x] = color;
     174           0 :                         if (half_horiz)
     175           0 :                             s->frame->data[0][(y+1)*s->frame->linesize[0] + x + 1] = color;
     176             :                     }
     177             :                 }
     178           0 :                 x += 1 + half_horiz;
     179             :             }
     180             :         }
     181             : 
     182           0 :         y += 1 + half_vert;
     183             :     }
     184             : 
     185           0 :     return 0;
     186             : }
     187             : 
     188          32 : static int mm_decode_frame(AVCodecContext *avctx,
     189             :                             void *data, int *got_frame,
     190             :                             AVPacket *avpkt)
     191             : {
     192          32 :     const uint8_t *buf = avpkt->data;
     193          32 :     int buf_size = avpkt->size;
     194          32 :     MmContext *s = avctx->priv_data;
     195             :     int type, res;
     196             : 
     197          32 :     if (buf_size < MM_PREAMBLE_SIZE)
     198           0 :         return AVERROR_INVALIDDATA;
     199          32 :     type = AV_RL16(&buf[0]);
     200          32 :     buf += MM_PREAMBLE_SIZE;
     201          32 :     buf_size -= MM_PREAMBLE_SIZE;
     202          32 :     bytestream2_init(&s->gb, buf, buf_size);
     203             : 
     204          32 :     if ((res = ff_reget_buffer(avctx, s->frame)) < 0)
     205           0 :         return res;
     206             : 
     207          32 :     switch(type) {
     208           1 :     case MM_TYPE_PALETTE   : mm_decode_pal(s); return avpkt->size;
     209          31 :     case MM_TYPE_INTRA     : res = mm_decode_intra(s, 0, 0); break;
     210           0 :     case MM_TYPE_INTRA_HH  : res = mm_decode_intra(s, 1, 0); break;
     211           0 :     case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break;
     212           0 :     case MM_TYPE_INTER     : res = mm_decode_inter(s, 0, 0); break;
     213           0 :     case MM_TYPE_INTER_HH  : res = mm_decode_inter(s, 1, 0); break;
     214           0 :     case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break;
     215           0 :     default:
     216           0 :         res = AVERROR_INVALIDDATA;
     217           0 :         break;
     218             :     }
     219          31 :     if (res < 0)
     220           0 :         return res;
     221             : 
     222          31 :     memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE);
     223             : 
     224          31 :     if ((res = av_frame_ref(data, s->frame)) < 0)
     225           0 :         return res;
     226             : 
     227          31 :     *got_frame      = 1;
     228             : 
     229          31 :     return avpkt->size;
     230             : }
     231             : 
     232           2 : static av_cold int mm_decode_end(AVCodecContext *avctx)
     233             : {
     234           2 :     MmContext *s = avctx->priv_data;
     235             : 
     236           2 :     av_frame_free(&s->frame);
     237             : 
     238           2 :     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             : };

Generated by: LCOV version 1.13