4e114829 |
/*
* American Laser Games MM Format Demuxer
* Copyright (c) 2006 Peter Ross
* |
b78e7197 |
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or |
4e114829 |
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either |
b78e7197 |
* version 2.1 of the License, or (at your option) any later version. |
4e114829 |
* |
b78e7197 |
* FFmpeg is distributed in the hope that it will be useful, |
4e114829 |
* 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 |
b78e7197 |
* License along with FFmpeg; if not, write to the Free Software |
e5a389a1 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
4e114829 |
*/
/** |
ba87f080 |
* @file |
4e114829 |
* American Laser Games MM Format Demuxer |
8600106a |
* by Peter Ross (pross@xvid.org) |
4e114829 |
*
* 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
*/
|
6a5d31ac |
#include "libavutil/intreadwrite.h" |
4e114829 |
#include "avformat.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;
|
fb6a32c3 |
static int probe(AVProbeData *p) |
4e114829 |
{ |
5837e4ca |
int len, type, fps, w, h;
if (p->buf_size < MM_HEADER_LEN_AV + MM_PREAMBLE_SIZE)
return 0; |
4e114829 |
/* the first chunk is always the header */ |
fead30d4 |
if (AV_RL16(&p->buf[0]) != MM_TYPE_HEADER) |
4e114829 |
return 0; |
5837e4ca |
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) |
4e114829 |
return 0;
/* only return half certainty since this check is a bit sketchy */
return AVPROBE_SCORE_MAX / 2;
}
|
fb6a32c3 |
static int read_header(AVFormatContext *s, |
4e114829 |
AVFormatParameters *ap)
{ |
e4141433 |
MmDemuxContext *mm = s->priv_data; |
ae628ec1 |
AVIOContext *pb = s->pb; |
4e114829 |
AVStream *st;
unsigned int type, length;
unsigned int frame_rate, width, height;
|
b7effd4e |
type = avio_rl16(pb);
length = avio_rl32(pb); |
4e114829 |
if (type != MM_TYPE_HEADER)
return AVERROR_INVALIDDATA;
/* read header */ |
b7effd4e |
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); |
45a8a02a |
avio_skip(pb, length - 10); /* unknown data */ |
4e114829 |
/* video stream */
st = av_new_stream(s, 0);
if (!st) |
769e10f0 |
return AVERROR(ENOMEM); |
72415b2a |
st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
4e114829 |
st->codec->codec_id = CODEC_ID_MMVIDEO;
st->codec->codec_tag = 0; /* no fourcc */
st->codec->width = width;
st->codec->height = height;
av_set_pts_info(st, 64, 1, frame_rate);
/* audio stream */
if (length == MM_HEADER_LEN_AV) {
st = av_new_stream(s, 0);
if (!st) |
769e10f0 |
return AVERROR(ENOMEM); |
72415b2a |
st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
4e114829 |
st->codec->codec_tag = 0; /* no fourcc */
st->codec->codec_id = CODEC_ID_PCM_U8;
st->codec->channels = 1;
st->codec->sample_rate = 8000;
av_set_pts_info(st, 64, 1, 8000); /* 8000 hz */
}
mm->audio_pts = 0;
mm->video_pts = 0;
return 0;
}
|
fb6a32c3 |
static int read_packet(AVFormatContext *s, |
4e114829 |
AVPacket *pkt)
{ |
e4141433 |
MmDemuxContext *mm = s->priv_data; |
ae628ec1 |
AVIOContext *pb = s->pb; |
4e114829 |
unsigned char preamble[MM_PREAMBLE_SIZE];
unsigned int type, length;
while(1) {
|
b7effd4e |
if (avio_read(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) { |
6f3e0b21 |
return AVERROR(EIO); |
4e114829 |
}
|
fead30d4 |
type = AV_RL16(&preamble[0]);
length = AV_RL16(&preamble[2]); |
4e114829 |
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)) |
769e10f0 |
return AVERROR(ENOMEM); |
4e114829 |
memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE); |
b7effd4e |
if (avio_read(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length) |
6f3e0b21 |
return AVERROR(EIO); |
4e114829 |
pkt->size = length + MM_PREAMBLE_SIZE;
pkt->stream_index = 0; |
60f451b6 |
pkt->pts = mm->video_pts;
if (type!=MM_TYPE_PALETTE)
mm->video_pts++; |
4e114829 |
return 0;
case MM_TYPE_AUDIO : |
899681cd |
if (av_get_packet(s->pb, pkt, length)<0) |
769e10f0 |
return AVERROR(ENOMEM); |
4e114829 |
pkt->size = length;
pkt->stream_index = 1;
pkt->pts = mm->audio_pts++;
return 0;
default : |
b4314bff |
av_log(s, AV_LOG_INFO, "unknown chunk type 0x%x\n", type); |
45a8a02a |
avio_skip(pb, length); |
4e114829 |
}
}
return 0;
}
|
c6610a21 |
AVInputFormat ff_mm_demuxer = { |
4e114829 |
"mm", |
bde15e74 |
NULL_IF_CONFIG_SMALL("American Laser Games MM format"), |
4e114829 |
sizeof(MmDemuxContext), |
fb6a32c3 |
probe,
read_header,
read_packet, |
4e114829 |
}; |