yading@10: /* yading@10: * American Laser Games MM Video Decoder yading@10: * Copyright (c) 2006,2008 Peter Ross yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * American Laser Games MM Video Decoder yading@10: * by Peter Ross (pross@xvid.org) yading@10: * yading@10: * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games, yading@10: * including Mad Dog McCree and Crime Patrol. yading@10: * yading@10: * Technical details here: yading@10: * http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM yading@10: */ yading@10: yading@10: #include "libavutil/intreadwrite.h" yading@10: #include "avcodec.h" yading@10: #include "bytestream.h" yading@10: #include "internal.h" yading@10: yading@10: #define MM_PREAMBLE_SIZE 6 yading@10: yading@10: #define MM_TYPE_INTER 0x5 yading@10: #define MM_TYPE_INTRA 0x8 yading@10: #define MM_TYPE_INTRA_HH 0xc yading@10: #define MM_TYPE_INTER_HH 0xd yading@10: #define MM_TYPE_INTRA_HHV 0xe yading@10: #define MM_TYPE_INTER_HHV 0xf yading@10: #define MM_TYPE_PALETTE 0x31 yading@10: yading@10: typedef struct MmContext { yading@10: AVCodecContext *avctx; yading@10: AVFrame frame; yading@10: int palette[AVPALETTE_COUNT]; yading@10: GetByteContext gb; yading@10: } MmContext; yading@10: yading@10: static av_cold int mm_decode_init(AVCodecContext *avctx) yading@10: { yading@10: MmContext *s = avctx->priv_data; yading@10: yading@10: s->avctx = avctx; yading@10: yading@10: avctx->pix_fmt = AV_PIX_FMT_PAL8; yading@10: yading@10: avcodec_get_frame_defaults(&s->frame); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int mm_decode_pal(MmContext *s) yading@10: { yading@10: int i; yading@10: yading@10: bytestream2_skip(&s->gb, 4); yading@10: for (i = 0; i < 128; i++) { yading@10: s->palette[i] = 0xFFU << 24 | bytestream2_get_be24(&s->gb); yading@10: s->palette[i+128] = s->palette[i]<<2; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * @param half_horiz Half horizontal resolution (0 or 1) yading@10: * @param half_vert Half vertical resolution (0 or 1) yading@10: */ yading@10: static int mm_decode_intra(MmContext * s, int half_horiz, int half_vert) yading@10: { yading@10: int x = 0, y = 0; yading@10: yading@10: while (bytestream2_get_bytes_left(&s->gb) > 0) { yading@10: int run_length, color; yading@10: yading@10: if (y >= s->avctx->height) yading@10: return 0; yading@10: yading@10: color = bytestream2_get_byte(&s->gb); yading@10: if (color & 0x80) { yading@10: run_length = 1; yading@10: }else{ yading@10: run_length = (color & 0x7f) + 2; yading@10: color = bytestream2_get_byte(&s->gb); yading@10: } yading@10: yading@10: if (half_horiz) yading@10: run_length *=2; yading@10: yading@10: if (color) { yading@10: memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length); yading@10: if (half_vert) yading@10: memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length); yading@10: } yading@10: x+= run_length; yading@10: yading@10: if (x >= s->avctx->width) { yading@10: x=0; yading@10: y += 1 + half_vert; yading@10: } yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * @param half_horiz Half horizontal resolution (0 or 1) yading@10: * @param half_vert Half vertical resolution (0 or 1) yading@10: */ yading@10: static int mm_decode_inter(MmContext * s, int half_horiz, int half_vert) yading@10: { yading@10: int data_off = bytestream2_get_le16(&s->gb), y = 0; yading@10: GetByteContext data_ptr; yading@10: yading@10: if (bytestream2_get_bytes_left(&s->gb) < data_off) yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: bytestream2_init(&data_ptr, s->gb.buffer + data_off, bytestream2_get_bytes_left(&s->gb) - data_off); yading@10: while (s->gb.buffer < data_ptr.buffer_start) { yading@10: int i, j; yading@10: int length = bytestream2_get_byte(&s->gb); yading@10: int x = bytestream2_get_byte(&s->gb) + ((length & 0x80) << 1); yading@10: length &= 0x7F; yading@10: yading@10: if (length==0) { yading@10: y += x; yading@10: continue; yading@10: } yading@10: yading@10: if (y + half_vert >= s->avctx->height) yading@10: return 0; yading@10: yading@10: for(i=0; igb); yading@10: for(j=0; j<8; j++) { yading@10: int replace = (replace_array >> (7-j)) & 1; yading@10: if (replace) { yading@10: int color = bytestream2_get_byte(&data_ptr); yading@10: s->frame.data[0][y*s->frame.linesize[0] + x] = color; yading@10: if (half_horiz) yading@10: s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color; yading@10: if (half_vert) { yading@10: s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color; yading@10: if (half_horiz) yading@10: s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color; yading@10: } yading@10: } yading@10: x += 1 + half_horiz; yading@10: } yading@10: } yading@10: yading@10: y += 1 + half_vert; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int mm_decode_frame(AVCodecContext *avctx, yading@10: void *data, int *got_frame, yading@10: AVPacket *avpkt) yading@10: { yading@10: const uint8_t *buf = avpkt->data; yading@10: int buf_size = avpkt->size; yading@10: MmContext *s = avctx->priv_data; yading@10: int type, res; yading@10: yading@10: if (buf_size < MM_PREAMBLE_SIZE) yading@10: return AVERROR_INVALIDDATA; yading@10: type = AV_RL16(&buf[0]); yading@10: buf += MM_PREAMBLE_SIZE; yading@10: buf_size -= MM_PREAMBLE_SIZE; yading@10: bytestream2_init(&s->gb, buf, buf_size); yading@10: yading@10: if ((res = ff_reget_buffer(avctx, &s->frame)) < 0) yading@10: return res; yading@10: yading@10: switch(type) { yading@10: case MM_TYPE_PALETTE : res = mm_decode_pal(s); return avpkt->size; yading@10: case MM_TYPE_INTRA : res = mm_decode_intra(s, 0, 0); break; yading@10: case MM_TYPE_INTRA_HH : res = mm_decode_intra(s, 1, 0); break; yading@10: case MM_TYPE_INTRA_HHV : res = mm_decode_intra(s, 1, 1); break; yading@10: case MM_TYPE_INTER : res = mm_decode_inter(s, 0, 0); break; yading@10: case MM_TYPE_INTER_HH : res = mm_decode_inter(s, 1, 0); break; yading@10: case MM_TYPE_INTER_HHV : res = mm_decode_inter(s, 1, 1); break; yading@10: default: yading@10: res = AVERROR_INVALIDDATA; yading@10: break; yading@10: } yading@10: if (res < 0) yading@10: return res; yading@10: yading@10: memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); yading@10: yading@10: if ((res = av_frame_ref(data, &s->frame)) < 0) yading@10: return res; yading@10: yading@10: *got_frame = 1; yading@10: yading@10: return avpkt->size; yading@10: } yading@10: yading@10: static av_cold int mm_decode_end(AVCodecContext *avctx) yading@10: { yading@10: MmContext *s = avctx->priv_data; yading@10: yading@10: av_frame_unref(&s->frame); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: AVCodec ff_mmvideo_decoder = { yading@10: .name = "mmvideo", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_MMVIDEO, yading@10: .priv_data_size = sizeof(MmContext), yading@10: .init = mm_decode_init, yading@10: .close = mm_decode_end, yading@10: .decode = mm_decode_frame, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"), yading@10: };