1/* 2 * American Laser Games MM Format Demuxer 3 * Copyright (c) 2006 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 libavformat/mm.c 24 * American Laser Games MM Format Demuxer 25 * by Peter Ross (suxen_drol at hotmail dot com) 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 "avformat.h" 36 37#define MM_PREAMBLE_SIZE 6 38 39#define MM_TYPE_HEADER 0x0 40#define MM_TYPE_INTER 0x5 41#define MM_TYPE_INTRA 0x8 42#define MM_TYPE_INTRA_HH 0xc 43#define MM_TYPE_INTER_HH 0xd 44#define MM_TYPE_INTRA_HHV 0xe 45#define MM_TYPE_INTER_HHV 0xf 46#define MM_TYPE_AUDIO 0x15 47#define MM_TYPE_PALETTE 0x31 48 49#define MM_HEADER_LEN_V 0x16 /* video only */ 50#define MM_HEADER_LEN_AV 0x18 /* video + audio */ 51 52#define MM_PALETTE_COUNT 128 53#define MM_PALETTE_SIZE (MM_PALETTE_COUNT*3) 54 55typedef struct { 56 unsigned int audio_pts, video_pts; 57} MmDemuxContext; 58 59static int mm_probe(AVProbeData *p) 60{ 61 /* the first chunk is always the header */ 62 if (AV_RL16(&p->buf[0]) != MM_TYPE_HEADER) 63 return 0; 64 if (AV_RL32(&p->buf[2]) != MM_HEADER_LEN_V && AV_RL32(&p->buf[2]) != MM_HEADER_LEN_AV) 65 return 0; 66 67 /* only return half certainty since this check is a bit sketchy */ 68 return AVPROBE_SCORE_MAX / 2; 69} 70 71static int mm_read_header(AVFormatContext *s, 72 AVFormatParameters *ap) 73{ 74 MmDemuxContext *mm = s->priv_data; 75 ByteIOContext *pb = s->pb; 76 AVStream *st; 77 78 unsigned int type, length; 79 unsigned int frame_rate, width, height; 80 81 type = get_le16(pb); 82 length = get_le32(pb); 83 84 if (type != MM_TYPE_HEADER) 85 return AVERROR_INVALIDDATA; 86 87 /* read header */ 88 get_le16(pb); /* total number of chunks */ 89 frame_rate = get_le16(pb); 90 get_le16(pb); /* ibm-pc video bios mode */ 91 width = get_le16(pb); 92 height = get_le16(pb); 93 url_fseek(pb, length - 10, SEEK_CUR); /* unknown data */ 94 95 /* video stream */ 96 st = av_new_stream(s, 0); 97 if (!st) 98 return AVERROR(ENOMEM); 99 st->codec->codec_type = CODEC_TYPE_VIDEO; 100 st->codec->codec_id = CODEC_ID_MMVIDEO; 101 st->codec->codec_tag = 0; /* no fourcc */ 102 st->codec->width = width; 103 st->codec->height = height; 104 av_set_pts_info(st, 64, 1, frame_rate); 105 106 /* audio stream */ 107 if (length == MM_HEADER_LEN_AV) { 108 st = av_new_stream(s, 0); 109 if (!st) 110 return AVERROR(ENOMEM); 111 st->codec->codec_type = CODEC_TYPE_AUDIO; 112 st->codec->codec_tag = 0; /* no fourcc */ 113 st->codec->codec_id = CODEC_ID_PCM_U8; 114 st->codec->channels = 1; 115 st->codec->sample_rate = 8000; 116 av_set_pts_info(st, 64, 1, 8000); /* 8000 hz */ 117 } 118 119 mm->audio_pts = 0; 120 mm->video_pts = 0; 121 return 0; 122} 123 124static int mm_read_packet(AVFormatContext *s, 125 AVPacket *pkt) 126{ 127 MmDemuxContext *mm = s->priv_data; 128 ByteIOContext *pb = s->pb; 129 unsigned char preamble[MM_PREAMBLE_SIZE]; 130 unsigned int type, length; 131 132 while(1) { 133 134 if (get_buffer(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) { 135 return AVERROR(EIO); 136 } 137 138 type = AV_RL16(&preamble[0]); 139 length = AV_RL16(&preamble[2]); 140 141 switch(type) { 142 case MM_TYPE_PALETTE : 143 case MM_TYPE_INTER : 144 case MM_TYPE_INTRA : 145 case MM_TYPE_INTRA_HH : 146 case MM_TYPE_INTER_HH : 147 case MM_TYPE_INTRA_HHV : 148 case MM_TYPE_INTER_HHV : 149 /* output preamble + data */ 150 if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE)) 151 return AVERROR(ENOMEM); 152 memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE); 153 if (get_buffer(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length) 154 return AVERROR(EIO); 155 pkt->size = length + MM_PREAMBLE_SIZE; 156 pkt->stream_index = 0; 157 pkt->pts = mm->video_pts; 158 if (type!=MM_TYPE_PALETTE) 159 mm->video_pts++; 160 return 0; 161 162 case MM_TYPE_AUDIO : 163 if (av_get_packet(s->pb, pkt, length)<0) 164 return AVERROR(ENOMEM); 165 pkt->size = length; 166 pkt->stream_index = 1; 167 pkt->pts = mm->audio_pts++; 168 return 0; 169 170 default : 171 av_log(s, AV_LOG_INFO, "unknown chunk type 0x%x\n", type); 172 url_fseek(pb, length, SEEK_CUR); 173 } 174 } 175 176 return 0; 177} 178 179AVInputFormat mm_demuxer = { 180 "mm", 181 NULL_IF_CONFIG_SMALL("American Laser Games MM format"), 182 sizeof(MmDemuxContext), 183 mm_probe, 184 mm_read_header, 185 mm_read_packet, 186}; 187