mm.c
Go to the documentation of this file.
1 /*
2  * American Laser Games MM Format Demuxer
3  * Copyright (c) 2006 Peter Ross
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
34 #include "libavutil/intreadwrite.h"
35 #include "avformat.h"
36 #include "internal.h"
37 
38 #define MM_PREAMBLE_SIZE 6
39 
40 #define MM_TYPE_HEADER 0x0
41 #define MM_TYPE_INTER 0x5
42 #define MM_TYPE_INTRA 0x8
43 #define MM_TYPE_INTRA_HH 0xc
44 #define MM_TYPE_INTER_HH 0xd
45 #define MM_TYPE_INTRA_HHV 0xe
46 #define MM_TYPE_INTER_HHV 0xf
47 #define MM_TYPE_AUDIO 0x15
48 #define MM_TYPE_PALETTE 0x31
49 
50 #define MM_HEADER_LEN_V 0x16 /* video only */
51 #define MM_HEADER_LEN_AV 0x18 /* video + audio */
52 
53 #define MM_PALETTE_COUNT 128
54 #define MM_PALETTE_SIZE (MM_PALETTE_COUNT*3)
55 
56 typedef struct {
57  unsigned int audio_pts, video_pts;
59 
60 static int probe(AVProbeData *p)
61 {
62  int len, type, fps, w, h;
64  return 0;
65  /* the first chunk is always the header */
66  if (AV_RL16(&p->buf[0]) != MM_TYPE_HEADER)
67  return 0;
68  len = AV_RL32(&p->buf[2]);
69  if (len != MM_HEADER_LEN_V && len != MM_HEADER_LEN_AV)
70  return 0;
71  fps = AV_RL16(&p->buf[8]);
72  w = AV_RL16(&p->buf[12]);
73  h = AV_RL16(&p->buf[14]);
74  if (!fps || fps > 60 || !w || w > 2048 || !h || h > 2048)
75  return 0;
76  type = AV_RL16(&p->buf[len]);
77  if (!type || type > 0x31)
78  return 0;
79 
80  /* only return half certainty since this check is a bit sketchy */
81  return AVPROBE_SCORE_MAX / 2;
82 }
83 
86 {
87  MmDemuxContext *mm = s->priv_data;
88  AVIOContext *pb = s->pb;
89  AVStream *st;
90 
91  unsigned int type, length;
92  unsigned int frame_rate, width, height;
93 
94  type = avio_rl16(pb);
95  length = avio_rl32(pb);
96 
97  if (type != MM_TYPE_HEADER)
98  return AVERROR_INVALIDDATA;
99 
100  /* read header */
101  avio_rl16(pb); /* total number of chunks */
102  frame_rate = avio_rl16(pb);
103  avio_rl16(pb); /* ibm-pc video bios mode */
104  width = avio_rl16(pb);
105  height = avio_rl16(pb);
106  avio_skip(pb, length - 10); /* unknown data */
107 
108  /* video stream */
109  st = avformat_new_stream(s, NULL);
110  if (!st)
111  return AVERROR(ENOMEM);
114  st->codec->codec_tag = 0; /* no fourcc */
115  st->codec->width = width;
116  st->codec->height = height;
117  avpriv_set_pts_info(st, 64, 1, frame_rate);
118 
119  /* audio stream */
120  if (length == MM_HEADER_LEN_AV) {
121  st = avformat_new_stream(s, NULL);
122  if (!st)
123  return AVERROR(ENOMEM);
125  st->codec->codec_tag = 0; /* no fourcc */
127  st->codec->channels = 1;
128  st->codec->sample_rate = 8000;
129  avpriv_set_pts_info(st, 64, 1, 8000); /* 8000 hz */
130  }
131 
132  mm->audio_pts = 0;
133  mm->video_pts = 0;
134  return 0;
135 }
136 
138  AVPacket *pkt)
139 {
140  MmDemuxContext *mm = s->priv_data;
141  AVIOContext *pb = s->pb;
142  unsigned char preamble[MM_PREAMBLE_SIZE];
143  unsigned int type, length;
144 
145  while(1) {
146 
147  if (avio_read(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) {
148  return AVERROR(EIO);
149  }
150 
151  type = AV_RL16(&preamble[0]);
152  length = AV_RL16(&preamble[2]);
153 
154  switch(type) {
155  case MM_TYPE_PALETTE :
156  case MM_TYPE_INTER :
157  case MM_TYPE_INTRA :
158  case MM_TYPE_INTRA_HH :
159  case MM_TYPE_INTER_HH :
160  case MM_TYPE_INTRA_HHV :
161  case MM_TYPE_INTER_HHV :
162  /* output preamble + data */
163  if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE))
164  return AVERROR(ENOMEM);
165  memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE);
166  if (avio_read(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length)
167  return AVERROR(EIO);
168  pkt->size = length + MM_PREAMBLE_SIZE;
169  pkt->stream_index = 0;
170  pkt->pts = mm->video_pts;
171  if (type!=MM_TYPE_PALETTE)
172  mm->video_pts++;
173  return 0;
174 
175  case MM_TYPE_AUDIO :
176  if (av_get_packet(s->pb, pkt, length)<0)
177  return AVERROR(ENOMEM);
178  pkt->size = length;
179  pkt->stream_index = 1;
180  pkt->pts = mm->audio_pts++;
181  return 0;
182 
183  default :
184  av_log(s, AV_LOG_INFO, "unknown chunk type 0x%x\n", type);
185  avio_skip(pb, length);
186  }
187  }
188 }
189 
191  .name = "mm",
192  .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM format"),
193  .priv_data_size = sizeof(MmDemuxContext),
194  .read_probe = probe,
197 };