Mercurial > libavformat.hg
comparison segafilm.c @ 266:8bb470d85249 libavformat
New demuxers: Sega FILM/CPK, Westwood VQA & AUD; new decoders: MS RLE &
Video-1, Apple RPZA, Cinepak, Westwood IMA ADPCM
author | tmmm |
---|---|
date | Wed, 01 Oct 2003 04:39:38 +0000 |
parents | |
children | ff7fb0eb0828 |
comparison
equal
deleted
inserted
replaced
265:786e8286ea4a | 266:8bb470d85249 |
---|---|
1 /* | |
2 * Sega FILM Format (CPK) Demuxer | |
3 * Copyright (c) 2003 The ffmpeg Project | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /** | |
21 * @file segafilm.c | |
22 * Sega FILM (.cpk) file demuxer | |
23 * by Mike Melanson (melanson@pcisys.net) | |
24 * For more information regarding the Sega FILM file format, visit: | |
25 * http://www.pcisys.net/~melanson/codecs/ | |
26 */ | |
27 | |
28 #include "avformat.h" | |
29 | |
30 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) | |
31 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ | |
32 (((uint8_t*)(x))[1] << 16) | \ | |
33 (((uint8_t*)(x))[2] << 8) | \ | |
34 ((uint8_t*)(x))[3]) | |
35 | |
36 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \ | |
37 ( (long)(unsigned char)(ch3) | \ | |
38 ( (long)(unsigned char)(ch2) << 8 ) | \ | |
39 ( (long)(unsigned char)(ch1) << 16 ) | \ | |
40 ( (long)(unsigned char)(ch0) << 24 ) ) | |
41 | |
42 #define FILM_TAG FOURCC_TAG('F', 'I', 'L', 'M') | |
43 #define FDSC_TAG FOURCC_TAG('F', 'D', 'S', 'C') | |
44 #define STAB_TAG FOURCC_TAG('S', 'T', 'A', 'B') | |
45 #define CVID_TAG FOURCC_TAG('c', 'v', 'i', 'd') | |
46 | |
47 typedef struct { | |
48 int stream; | |
49 off_t sample_offset; | |
50 unsigned int sample_size; | |
51 int64_t pts; | |
52 int keyframe; | |
53 } film_sample_t; | |
54 | |
55 typedef struct FilmDemuxContext { | |
56 int video_stream_index; | |
57 int audio_stream_index; | |
58 | |
59 unsigned int audio_type; | |
60 unsigned int audio_samplerate; | |
61 unsigned int audio_bits; | |
62 unsigned int audio_channels; | |
63 | |
64 unsigned int video_type; | |
65 unsigned int sample_count; | |
66 film_sample_t *sample_table; | |
67 unsigned int current_sample; | |
68 | |
69 unsigned int base_clock; | |
70 unsigned int version; | |
71 int cvid_extra_bytes; /* the number of bytes thrown into the Cinepak | |
72 * chunk header to throw off decoders */ | |
73 | |
74 /* buffer used for interleaving stereo PCM data */ | |
75 unsigned char *stereo_buffer; | |
76 int stereo_buffer_size; | |
77 } FilmDemuxContext; | |
78 | |
79 static int film_probe(AVProbeData *p) | |
80 { | |
81 if (p->buf_size < 4) | |
82 return 0; | |
83 | |
84 if (BE_32(&p->buf[0]) != FILM_TAG) | |
85 return 0; | |
86 | |
87 return AVPROBE_SCORE_MAX; | |
88 } | |
89 | |
90 static int film_read_header(AVFormatContext *s, | |
91 AVFormatParameters *ap) | |
92 { | |
93 FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data; | |
94 ByteIOContext *pb = &s->pb; | |
95 AVStream *st; | |
96 unsigned char scratch[256]; | |
97 int i; | |
98 unsigned int data_offset; | |
99 unsigned int audio_frame_counter; | |
100 | |
101 film->sample_table = NULL; | |
102 film->stereo_buffer = NULL; | |
103 film->stereo_buffer_size = 0; | |
104 | |
105 /* load the main FILM header */ | |
106 if (get_buffer(pb, scratch, 16) != 16) | |
107 return -EIO; | |
108 data_offset = BE_32(&scratch[4]); | |
109 film->version = BE_32(&scratch[8]); | |
110 | |
111 /* load the FDSC chunk */ | |
112 if (film->version == 0) { | |
113 /* special case for Lemmings .film files; 20-byte header */ | |
114 if (get_buffer(pb, scratch, 20) != 20) | |
115 return -EIO; | |
116 /* make some assumptions about the audio parameters */ | |
117 film->audio_type = CODEC_ID_PCM_S8; | |
118 film->audio_samplerate = 22050; | |
119 film->audio_channels = 1; | |
120 film->audio_bits = 8; | |
121 } else { | |
122 /* normal Saturn .cpk files; 32-byte header */ | |
123 if (get_buffer(pb, scratch, 32) != 32) | |
124 return -EIO; | |
125 film->audio_samplerate = BE_16(&scratch[24]);; | |
126 film->audio_channels = scratch[21]; | |
127 film->audio_bits = scratch[22]; | |
128 if (film->audio_bits == 8) | |
129 film->audio_type = CODEC_ID_PCM_S8; | |
130 else if (film->audio_bits == 16) | |
131 film->audio_type = CODEC_ID_PCM_S16BE; | |
132 else | |
133 film->audio_type = 0; | |
134 } | |
135 | |
136 if (BE_32(&scratch[0]) != FDSC_TAG) | |
137 return AVERROR_INVALIDDATA; | |
138 | |
139 film->cvid_extra_bytes = 0; | |
140 if (BE_32(&scratch[8]) == CVID_TAG) { | |
141 film->video_type = CODEC_ID_CINEPAK; | |
142 if (film->version) | |
143 film->cvid_extra_bytes = 2; | |
144 else | |
145 film->cvid_extra_bytes = 6; /* Lemmings 3DO case */ | |
146 } else | |
147 film->video_type = 0; | |
148 | |
149 /* initialize the decoder streams */ | |
150 if (film->video_type) { | |
151 st = av_new_stream(s, 0); | |
152 if (!st) | |
153 return AVERROR_NOMEM; | |
154 film->video_stream_index = st->index; | |
155 st->codec.codec_type = CODEC_TYPE_VIDEO; | |
156 st->codec.codec_id = film->video_type; | |
157 st->codec.codec_tag = 0; /* no fourcc */ | |
158 st->codec.width = BE_32(&scratch[16]); | |
159 st->codec.height = BE_32(&scratch[12]); | |
160 } | |
161 | |
162 if (film->audio_type) { | |
163 st = av_new_stream(s, 0); | |
164 if (!st) | |
165 return AVERROR_NOMEM; | |
166 film->audio_stream_index = st->index; | |
167 st->codec.codec_type = CODEC_TYPE_AUDIO; | |
168 st->codec.codec_id = film->audio_type; | |
169 st->codec.codec_tag = 1; | |
170 st->codec.channels = film->audio_channels; | |
171 st->codec.bits_per_sample = film->audio_bits; | |
172 st->codec.sample_rate = film->audio_samplerate; | |
173 st->codec.bit_rate = st->codec.channels * st->codec.sample_rate * | |
174 st->codec.bits_per_sample; | |
175 st->codec.block_align = st->codec.channels * | |
176 st->codec.bits_per_sample / 8; | |
177 } | |
178 | |
179 /* load the sample table */ | |
180 if (get_buffer(pb, scratch, 16) != 16) | |
181 return -EIO; | |
182 if (BE_32(&scratch[0]) != STAB_TAG) | |
183 return AVERROR_INVALIDDATA; | |
184 film->base_clock = BE_32(&scratch[8]); | |
185 film->sample_count = BE_32(&scratch[12]); | |
186 film->sample_table = av_malloc(film->sample_count * sizeof(film_sample_t)); | |
187 | |
188 audio_frame_counter = 0; | |
189 for (i = 0; i < film->sample_count; i++) { | |
190 /* load the next sample record and transfer it to an internal struct */ | |
191 if (get_buffer(pb, scratch, 16) != 16) { | |
192 av_free(film->sample_table); | |
193 return -EIO; | |
194 } | |
195 film->sample_table[i].sample_offset = | |
196 data_offset + BE_32(&scratch[0]); | |
197 film->sample_table[i].sample_size = BE_32(&scratch[4]); | |
198 if (BE_32(&scratch[8]) == 0xFFFFFFFF) { | |
199 film->sample_table[i].stream = film->audio_stream_index; | |
200 film->sample_table[i].pts = audio_frame_counter; | |
201 film->sample_table[i].pts *= film->base_clock; | |
202 film->sample_table[i].pts /= film->audio_samplerate; | |
203 | |
204 audio_frame_counter += (film->sample_table[i].sample_size / | |
205 (film->audio_channels * film->audio_bits / 8)); | |
206 } else { | |
207 film->sample_table[i].stream = film->video_stream_index; | |
208 film->sample_table[i].pts = BE_32(&scratch[8]) & 0x7FFFFFFF; | |
209 film->sample_table[i].keyframe = (scratch[8] & 0x80) ? 0 : 1; | |
210 } | |
211 } | |
212 | |
213 film->current_sample = 0; | |
214 | |
215 /* set the pts reference to match the tick rate of the file */ | |
216 s->pts_num = 1; | |
217 s->pts_den = film->base_clock; | |
218 | |
219 return 0; | |
220 } | |
221 | |
222 static int film_read_packet(AVFormatContext *s, | |
223 AVPacket *pkt) | |
224 { | |
225 FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data; | |
226 ByteIOContext *pb = &s->pb; | |
227 film_sample_t *sample; | |
228 int ret = 0; | |
229 int i; | |
230 int left, right; | |
231 | |
232 if (film->current_sample >= film->sample_count) | |
233 return -EIO; | |
234 | |
235 sample = &film->sample_table[film->current_sample]; | |
236 | |
237 /* position the stream (will probably be there anyway) */ | |
238 url_fseek(pb, sample->sample_offset, SEEK_SET); | |
239 | |
240 /* do a special song and dance when loading FILM Cinepak chunks */ | |
241 if ((sample->stream == film->video_stream_index) && | |
242 (film->video_type == CODEC_ID_CINEPAK)) { | |
243 if (av_new_packet(pkt, sample->sample_size - film->cvid_extra_bytes)) | |
244 return AVERROR_NOMEM; | |
245 ret = get_buffer(pb, pkt->data, 10); | |
246 /* skip the non-spec CVID bytes */ | |
247 url_fseek(pb, film->cvid_extra_bytes, SEEK_CUR); | |
248 ret += get_buffer(pb, pkt->data + 10, | |
249 sample->sample_size - 10 - film->cvid_extra_bytes); | |
250 if (ret != sample->sample_size - film->cvid_extra_bytes) | |
251 ret = -EIO; | |
252 } else if ((sample->stream == film->audio_stream_index) && | |
253 (film->audio_channels == 2)) { | |
254 /* stereo PCM needs to be interleaved */ | |
255 | |
256 if (av_new_packet(pkt, sample->sample_size)) | |
257 return AVERROR_NOMEM; | |
258 | |
259 /* make sure the interleave buffer is large enough */ | |
260 if (sample->sample_size > film->stereo_buffer_size) { | |
261 av_free(film->stereo_buffer); | |
262 film->stereo_buffer_size = sample->sample_size; | |
263 film->stereo_buffer = av_malloc(film->stereo_buffer_size); | |
264 } | |
265 | |
266 ret = get_buffer(pb, film->stereo_buffer, sample->sample_size); | |
267 if (ret != sample->sample_size) | |
268 ret = -EIO; | |
269 | |
270 left = 0; | |
271 right = sample->sample_size / 2; | |
272 for (i = 0; i < sample->sample_size; ) { | |
273 if (film->audio_bits == 8) { | |
274 pkt->data[i++] = film->stereo_buffer[left++]; | |
275 pkt->data[i++] = film->stereo_buffer[right++]; | |
276 } else { | |
277 pkt->data[i++] = film->stereo_buffer[left++]; | |
278 pkt->data[i++] = film->stereo_buffer[left++]; | |
279 pkt->data[i++] = film->stereo_buffer[right++]; | |
280 pkt->data[i++] = film->stereo_buffer[right++]; | |
281 } | |
282 } | |
283 } else { | |
284 if (av_new_packet(pkt, sample->sample_size)) | |
285 return AVERROR_NOMEM; | |
286 ret = get_buffer(pb, pkt->data, sample->sample_size); | |
287 if (ret != sample->sample_size) | |
288 ret = -EIO; | |
289 } | |
290 | |
291 pkt->stream_index = sample->stream; | |
292 pkt->pts = sample->pts; | |
293 | |
294 film->current_sample++; | |
295 | |
296 return ret; | |
297 } | |
298 | |
299 static int film_read_close(AVFormatContext *s) | |
300 { | |
301 FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data; | |
302 | |
303 av_free(film->sample_table); | |
304 av_free(film->stereo_buffer); | |
305 | |
306 return 0; | |
307 } | |
308 | |
309 static AVInputFormat film_iformat = { | |
310 "film_cpk", | |
311 "Sega FILM/CPK format", | |
312 sizeof(FilmDemuxContext), | |
313 film_probe, | |
314 film_read_header, | |
315 film_read_packet, | |
316 film_read_close, | |
317 }; | |
318 | |
319 int film_init(void) | |
320 { | |
321 av_register_input_format(&film_iformat); | |
322 return 0; | |
323 } |