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