diff options
Diffstat (limited to 'ffmpeg1/libavformat/mm.c')
| -rw-r--r-- | ffmpeg1/libavformat/mm.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/ffmpeg1/libavformat/mm.c b/ffmpeg1/libavformat/mm.c new file mode 100644 index 0000000..12a11ac --- /dev/null +++ b/ffmpeg1/libavformat/mm.c @@ -0,0 +1,197 @@ +/* + * American Laser Games MM Format Demuxer + * Copyright (c) 2006 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * American Laser Games MM Format Demuxer + * by Peter Ross (pross@xvid.org) + * + * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games, + * including Mad Dog McCree and Crime Patrol. + * + * Technical details here: + * http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM + */ + +#include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" + +#define MM_PREAMBLE_SIZE 6 + +#define MM_TYPE_HEADER 0x0 +#define MM_TYPE_INTER 0x5 +#define MM_TYPE_INTRA 0x8 +#define MM_TYPE_INTRA_HH 0xc +#define MM_TYPE_INTER_HH 0xd +#define MM_TYPE_INTRA_HHV 0xe +#define MM_TYPE_INTER_HHV 0xf +#define MM_TYPE_AUDIO 0x15 +#define MM_TYPE_PALETTE 0x31 + +#define MM_HEADER_LEN_V 0x16 /* video only */ +#define MM_HEADER_LEN_AV 0x18 /* video + audio */ + +#define MM_PALETTE_COUNT 128 +#define MM_PALETTE_SIZE (MM_PALETTE_COUNT*3) + +typedef struct { + unsigned int audio_pts, video_pts; +} MmDemuxContext; + +static int probe(AVProbeData *p) +{ + int len, type, fps, w, h; + if (p->buf_size < MM_HEADER_LEN_AV + MM_PREAMBLE_SIZE) + return 0; + /* the first chunk is always the header */ + if (AV_RL16(&p->buf[0]) != MM_TYPE_HEADER) + return 0; + len = AV_RL32(&p->buf[2]); + if (len != MM_HEADER_LEN_V && len != MM_HEADER_LEN_AV) + return 0; + fps = AV_RL16(&p->buf[8]); + w = AV_RL16(&p->buf[12]); + h = AV_RL16(&p->buf[14]); + if (!fps || fps > 60 || !w || w > 2048 || !h || h > 2048) + return 0; + type = AV_RL16(&p->buf[len]); + if (!type || type > 0x31) + return 0; + + /* only return half certainty since this check is a bit sketchy */ + return AVPROBE_SCORE_MAX / 2; +} + +static int read_header(AVFormatContext *s) +{ + MmDemuxContext *mm = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + + unsigned int type, length; + unsigned int frame_rate, width, height; + + type = avio_rl16(pb); + length = avio_rl32(pb); + + if (type != MM_TYPE_HEADER) + return AVERROR_INVALIDDATA; + + /* read header */ + avio_rl16(pb); /* total number of chunks */ + frame_rate = avio_rl16(pb); + avio_rl16(pb); /* ibm-pc video bios mode */ + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_skip(pb, length - 10); /* unknown data */ + + /* video stream */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_MMVIDEO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->width = width; + st->codec->height = height; + avpriv_set_pts_info(st, 64, 1, frame_rate); + + /* audio stream */ + if (length == MM_HEADER_LEN_AV) { + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_tag = 0; /* no fourcc */ + st->codec->codec_id = AV_CODEC_ID_PCM_U8; + st->codec->channels = 1; + st->codec->channel_layout = AV_CH_LAYOUT_MONO; + st->codec->sample_rate = 8000; + avpriv_set_pts_info(st, 64, 1, 8000); /* 8000 hz */ + } + + mm->audio_pts = 0; + mm->video_pts = 0; + return 0; +} + +static int read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MmDemuxContext *mm = s->priv_data; + AVIOContext *pb = s->pb; + unsigned char preamble[MM_PREAMBLE_SIZE]; + unsigned int type, length; + + while(1) { + + if (avio_read(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) { + return AVERROR(EIO); + } + + type = AV_RL16(&preamble[0]); + length = AV_RL16(&preamble[2]); + + switch(type) { + case MM_TYPE_PALETTE : + case MM_TYPE_INTER : + case MM_TYPE_INTRA : + case MM_TYPE_INTRA_HH : + case MM_TYPE_INTER_HH : + case MM_TYPE_INTRA_HHV : + case MM_TYPE_INTER_HHV : + /* output preamble + data */ + if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE)) + return AVERROR(ENOMEM); + memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE); + if (avio_read(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length) + return AVERROR(EIO); + pkt->size = length + MM_PREAMBLE_SIZE; + pkt->stream_index = 0; + pkt->pts = mm->video_pts; + if (type!=MM_TYPE_PALETTE) + mm->video_pts++; + return 0; + + case MM_TYPE_AUDIO : + if (av_get_packet(s->pb, pkt, length)<0) + return AVERROR(ENOMEM); + pkt->stream_index = 1; + pkt->pts = mm->audio_pts++; + return 0; + + default : + av_log(s, AV_LOG_INFO, "unknown chunk type 0x%x\n", type); + avio_skip(pb, length); + } + } +} + +AVInputFormat ff_mm_demuxer = { + .name = "mm", + .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM"), + .priv_data_size = sizeof(MmDemuxContext), + .read_probe = probe, + .read_header = read_header, + .read_packet = read_packet, +}; |
