ffmpeg / libavformat / ipmovie.c @ 8badb23e
History | View | Annotate | Download (20.5 KB)
1 |
/*
|
---|---|
2 |
* Interplay MVE File Demuxer
|
3 |
* Copyright (c) 2003 The ffmpeg Project
|
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/ipmovie.c
|
24 |
* Interplay MVE file demuxer
|
25 |
* by Mike Melanson (melanson@pcisys.net)
|
26 |
* For more information regarding the Interplay MVE file format, visit:
|
27 |
* http://www.pcisys.net/~melanson/codecs/
|
28 |
* The aforementioned site also contains a command line utility for parsing
|
29 |
* IP MVE files so that you can get a good idea of the typical structure of
|
30 |
* such files. This demuxer is not the best example to use if you are trying
|
31 |
* to write your own as it uses a rather roundabout approach for splitting
|
32 |
* up and sending out the chunks.
|
33 |
*/
|
34 |
|
35 |
#include "libavutil/intreadwrite.h" |
36 |
#include "avformat.h" |
37 |
|
38 |
/* debugging support: #define DEBUG_IPMOVIE as non-zero to see extremely
|
39 |
* verbose information about the demux process */
|
40 |
#define DEBUG_IPMOVIE 0 |
41 |
|
42 |
#if DEBUG_IPMOVIE
|
43 |
#undef printf
|
44 |
#define debug_ipmovie printf
|
45 |
#else
|
46 |
static inline void debug_ipmovie(const char *format, ...) { } |
47 |
#endif
|
48 |
|
49 |
#define CHUNK_PREAMBLE_SIZE 4 |
50 |
#define OPCODE_PREAMBLE_SIZE 4 |
51 |
|
52 |
#define CHUNK_INIT_AUDIO 0x0000 |
53 |
#define CHUNK_AUDIO_ONLY 0x0001 |
54 |
#define CHUNK_INIT_VIDEO 0x0002 |
55 |
#define CHUNK_VIDEO 0x0003 |
56 |
#define CHUNK_SHUTDOWN 0x0004 |
57 |
#define CHUNK_END 0x0005 |
58 |
/* these last types are used internally */
|
59 |
#define CHUNK_DONE 0xFFFC |
60 |
#define CHUNK_NOMEM 0xFFFD |
61 |
#define CHUNK_EOF 0xFFFE |
62 |
#define CHUNK_BAD 0xFFFF |
63 |
|
64 |
#define OPCODE_END_OF_STREAM 0x00 |
65 |
#define OPCODE_END_OF_CHUNK 0x01 |
66 |
#define OPCODE_CREATE_TIMER 0x02 |
67 |
#define OPCODE_INIT_AUDIO_BUFFERS 0x03 |
68 |
#define OPCODE_START_STOP_AUDIO 0x04 |
69 |
#define OPCODE_INIT_VIDEO_BUFFERS 0x05 |
70 |
#define OPCODE_UNKNOWN_06 0x06 |
71 |
#define OPCODE_SEND_BUFFER 0x07 |
72 |
#define OPCODE_AUDIO_FRAME 0x08 |
73 |
#define OPCODE_SILENCE_FRAME 0x09 |
74 |
#define OPCODE_INIT_VIDEO_MODE 0x0A |
75 |
#define OPCODE_CREATE_GRADIENT 0x0B |
76 |
#define OPCODE_SET_PALETTE 0x0C |
77 |
#define OPCODE_SET_PALETTE_COMPRESSED 0x0D |
78 |
#define OPCODE_UNKNOWN_0E 0x0E |
79 |
#define OPCODE_SET_DECODING_MAP 0x0F |
80 |
#define OPCODE_UNKNOWN_10 0x10 |
81 |
#define OPCODE_VIDEO_DATA 0x11 |
82 |
#define OPCODE_UNKNOWN_12 0x12 |
83 |
#define OPCODE_UNKNOWN_13 0x13 |
84 |
#define OPCODE_UNKNOWN_14 0x14 |
85 |
#define OPCODE_UNKNOWN_15 0x15 |
86 |
|
87 |
#define PALETTE_COUNT 256 |
88 |
|
89 |
typedef struct IPMVEContext { |
90 |
|
91 |
unsigned char *buf; |
92 |
int buf_size;
|
93 |
|
94 |
uint64_t frame_pts_inc; |
95 |
|
96 |
unsigned int video_bpp; |
97 |
unsigned int video_width; |
98 |
unsigned int video_height; |
99 |
int64_t video_pts; |
100 |
|
101 |
unsigned int audio_bits; |
102 |
unsigned int audio_channels; |
103 |
unsigned int audio_sample_rate; |
104 |
enum CodecID audio_type;
|
105 |
unsigned int audio_frame_count; |
106 |
|
107 |
int video_stream_index;
|
108 |
int audio_stream_index;
|
109 |
|
110 |
int64_t audio_chunk_offset; |
111 |
int audio_chunk_size;
|
112 |
int64_t video_chunk_offset; |
113 |
int video_chunk_size;
|
114 |
int64_t decode_map_chunk_offset; |
115 |
int decode_map_chunk_size;
|
116 |
|
117 |
int64_t next_chunk_offset; |
118 |
|
119 |
AVPaletteControl palette_control; |
120 |
|
121 |
} IPMVEContext; |
122 |
|
123 |
static int load_ipmovie_packet(IPMVEContext *s, ByteIOContext *pb, |
124 |
AVPacket *pkt) { |
125 |
|
126 |
int chunk_type;
|
127 |
|
128 |
if (s->audio_chunk_offset) {
|
129 |
|
130 |
/* adjust for PCM audio by skipping chunk header */
|
131 |
if (s->audio_type != CODEC_ID_INTERPLAY_DPCM) {
|
132 |
s->audio_chunk_offset += 6;
|
133 |
s->audio_chunk_size -= 6;
|
134 |
} |
135 |
|
136 |
url_fseek(pb, s->audio_chunk_offset, SEEK_SET); |
137 |
s->audio_chunk_offset = 0;
|
138 |
|
139 |
if (s->audio_chunk_size != av_get_packet(pb, pkt, s->audio_chunk_size))
|
140 |
return CHUNK_EOF;
|
141 |
|
142 |
pkt->stream_index = s->audio_stream_index; |
143 |
pkt->pts = s->audio_frame_count; |
144 |
|
145 |
/* audio frame maintenance */
|
146 |
if (s->audio_type != CODEC_ID_INTERPLAY_DPCM)
|
147 |
s->audio_frame_count += |
148 |
(s->audio_chunk_size / s->audio_channels / (s->audio_bits / 8));
|
149 |
else
|
150 |
s->audio_frame_count += |
151 |
(s->audio_chunk_size - 6) / s->audio_channels;
|
152 |
|
153 |
debug_ipmovie("sending audio frame with pts %"PRId64" (%d audio frames)\n", |
154 |
pkt->pts, s->audio_frame_count); |
155 |
|
156 |
chunk_type = CHUNK_VIDEO; |
157 |
|
158 |
} else if (s->decode_map_chunk_offset) { |
159 |
|
160 |
/* send both the decode map and the video data together */
|
161 |
|
162 |
if (av_new_packet(pkt, s->decode_map_chunk_size + s->video_chunk_size))
|
163 |
return CHUNK_NOMEM;
|
164 |
|
165 |
pkt->pos= s->decode_map_chunk_offset; |
166 |
url_fseek(pb, s->decode_map_chunk_offset, SEEK_SET); |
167 |
s->decode_map_chunk_offset = 0;
|
168 |
|
169 |
if (get_buffer(pb, pkt->data, s->decode_map_chunk_size) !=
|
170 |
s->decode_map_chunk_size) { |
171 |
av_free_packet(pkt); |
172 |
return CHUNK_EOF;
|
173 |
} |
174 |
|
175 |
url_fseek(pb, s->video_chunk_offset, SEEK_SET); |
176 |
s->video_chunk_offset = 0;
|
177 |
|
178 |
if (get_buffer(pb, pkt->data + s->decode_map_chunk_size,
|
179 |
s->video_chunk_size) != s->video_chunk_size) { |
180 |
av_free_packet(pkt); |
181 |
return CHUNK_EOF;
|
182 |
} |
183 |
|
184 |
pkt->stream_index = s->video_stream_index; |
185 |
pkt->pts = s->video_pts; |
186 |
|
187 |
debug_ipmovie("sending video frame with pts %"PRId64"\n", |
188 |
pkt->pts); |
189 |
|
190 |
s->video_pts += s->frame_pts_inc; |
191 |
|
192 |
chunk_type = CHUNK_VIDEO; |
193 |
|
194 |
} else {
|
195 |
|
196 |
url_fseek(pb, s->next_chunk_offset, SEEK_SET); |
197 |
chunk_type = CHUNK_DONE; |
198 |
|
199 |
} |
200 |
|
201 |
return chunk_type;
|
202 |
} |
203 |
|
204 |
/* This function loads and processes a single chunk in an IP movie file.
|
205 |
* It returns the type of chunk that was processed. */
|
206 |
static int process_ipmovie_chunk(IPMVEContext *s, ByteIOContext *pb, |
207 |
AVPacket *pkt) |
208 |
{ |
209 |
unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; |
210 |
int chunk_type;
|
211 |
int chunk_size;
|
212 |
unsigned char opcode_preamble[OPCODE_PREAMBLE_SIZE]; |
213 |
unsigned char opcode_type; |
214 |
unsigned char opcode_version; |
215 |
int opcode_size;
|
216 |
unsigned char scratch[1024]; |
217 |
int i, j;
|
218 |
int first_color, last_color;
|
219 |
int audio_flags;
|
220 |
unsigned char r, g, b; |
221 |
|
222 |
/* see if there are any pending packets */
|
223 |
chunk_type = load_ipmovie_packet(s, pb, pkt); |
224 |
if (chunk_type != CHUNK_DONE)
|
225 |
return chunk_type;
|
226 |
|
227 |
/* read the next chunk, wherever the file happens to be pointing */
|
228 |
if (url_feof(pb))
|
229 |
return CHUNK_EOF;
|
230 |
if (get_buffer(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) !=
|
231 |
CHUNK_PREAMBLE_SIZE) |
232 |
return CHUNK_BAD;
|
233 |
chunk_size = AV_RL16(&chunk_preamble[0]);
|
234 |
chunk_type = AV_RL16(&chunk_preamble[2]);
|
235 |
|
236 |
debug_ipmovie("chunk type 0x%04X, 0x%04X bytes: ", chunk_type, chunk_size);
|
237 |
|
238 |
switch (chunk_type) {
|
239 |
|
240 |
case CHUNK_INIT_AUDIO:
|
241 |
debug_ipmovie("initialize audio\n");
|
242 |
break;
|
243 |
|
244 |
case CHUNK_AUDIO_ONLY:
|
245 |
debug_ipmovie("audio only\n");
|
246 |
break;
|
247 |
|
248 |
case CHUNK_INIT_VIDEO:
|
249 |
debug_ipmovie("initialize video\n");
|
250 |
break;
|
251 |
|
252 |
case CHUNK_VIDEO:
|
253 |
debug_ipmovie("video (and audio)\n");
|
254 |
break;
|
255 |
|
256 |
case CHUNK_SHUTDOWN:
|
257 |
debug_ipmovie("shutdown\n");
|
258 |
break;
|
259 |
|
260 |
case CHUNK_END:
|
261 |
debug_ipmovie("end\n");
|
262 |
break;
|
263 |
|
264 |
default:
|
265 |
debug_ipmovie("invalid chunk\n");
|
266 |
chunk_type = CHUNK_BAD; |
267 |
break;
|
268 |
|
269 |
} |
270 |
|
271 |
while ((chunk_size > 0) && (chunk_type != CHUNK_BAD)) { |
272 |
|
273 |
/* read the next chunk, wherever the file happens to be pointing */
|
274 |
if (url_feof(pb)) {
|
275 |
chunk_type = CHUNK_EOF; |
276 |
break;
|
277 |
} |
278 |
if (get_buffer(pb, opcode_preamble, CHUNK_PREAMBLE_SIZE) !=
|
279 |
CHUNK_PREAMBLE_SIZE) { |
280 |
chunk_type = CHUNK_BAD; |
281 |
break;
|
282 |
} |
283 |
|
284 |
opcode_size = AV_RL16(&opcode_preamble[0]);
|
285 |
opcode_type = opcode_preamble[2];
|
286 |
opcode_version = opcode_preamble[3];
|
287 |
|
288 |
chunk_size -= OPCODE_PREAMBLE_SIZE; |
289 |
chunk_size -= opcode_size; |
290 |
if (chunk_size < 0) { |
291 |
debug_ipmovie("chunk_size countdown just went negative\n");
|
292 |
chunk_type = CHUNK_BAD; |
293 |
break;
|
294 |
} |
295 |
|
296 |
debug_ipmovie(" opcode type %02X, version %d, 0x%04X bytes: ",
|
297 |
opcode_type, opcode_version, opcode_size); |
298 |
switch (opcode_type) {
|
299 |
|
300 |
case OPCODE_END_OF_STREAM:
|
301 |
debug_ipmovie("end of stream\n");
|
302 |
url_fseek(pb, opcode_size, SEEK_CUR); |
303 |
break;
|
304 |
|
305 |
case OPCODE_END_OF_CHUNK:
|
306 |
debug_ipmovie("end of chunk\n");
|
307 |
url_fseek(pb, opcode_size, SEEK_CUR); |
308 |
break;
|
309 |
|
310 |
case OPCODE_CREATE_TIMER:
|
311 |
debug_ipmovie("create timer\n");
|
312 |
if ((opcode_version > 0) || (opcode_size > 6)) { |
313 |
debug_ipmovie("bad create_timer opcode\n");
|
314 |
chunk_type = CHUNK_BAD; |
315 |
break;
|
316 |
} |
317 |
if (get_buffer(pb, scratch, opcode_size) !=
|
318 |
opcode_size) { |
319 |
chunk_type = CHUNK_BAD; |
320 |
break;
|
321 |
} |
322 |
s->frame_pts_inc = ((uint64_t)AV_RL32(&scratch[0])) * AV_RL16(&scratch[4]); |
323 |
debug_ipmovie(" %.2f frames/second (timer div = %d, subdiv = %d)\n",
|
324 |
1000000.0/s->frame_pts_inc, AV_RL32(&scratch[0]), AV_RL16(&scratch[4])); |
325 |
break;
|
326 |
|
327 |
case OPCODE_INIT_AUDIO_BUFFERS:
|
328 |
debug_ipmovie("initialize audio buffers\n");
|
329 |
if ((opcode_version > 1) || (opcode_size > 10)) { |
330 |
debug_ipmovie("bad init_audio_buffers opcode\n");
|
331 |
chunk_type = CHUNK_BAD; |
332 |
break;
|
333 |
} |
334 |
if (get_buffer(pb, scratch, opcode_size) !=
|
335 |
opcode_size) { |
336 |
chunk_type = CHUNK_BAD; |
337 |
break;
|
338 |
} |
339 |
s->audio_sample_rate = AV_RL16(&scratch[4]);
|
340 |
audio_flags = AV_RL16(&scratch[2]);
|
341 |
/* bit 0 of the flags: 0 = mono, 1 = stereo */
|
342 |
s->audio_channels = (audio_flags & 1) + 1; |
343 |
/* bit 1 of the flags: 0 = 8 bit, 1 = 16 bit */
|
344 |
s->audio_bits = (((audio_flags >> 1) & 1) + 1) * 8; |
345 |
/* bit 2 indicates compressed audio in version 1 opcode */
|
346 |
if ((opcode_version == 1) && (audio_flags & 0x4)) |
347 |
s->audio_type = CODEC_ID_INTERPLAY_DPCM; |
348 |
else if (s->audio_bits == 16) |
349 |
s->audio_type = CODEC_ID_PCM_S16LE; |
350 |
else
|
351 |
s->audio_type = CODEC_ID_PCM_U8; |
352 |
debug_ipmovie("audio: %d bits, %d Hz, %s, %s format\n",
|
353 |
s->audio_bits, |
354 |
s->audio_sample_rate, |
355 |
(s->audio_channels == 2) ? "stereo" : "mono", |
356 |
(s->audio_type == CODEC_ID_INTERPLAY_DPCM) ? |
357 |
"Interplay audio" : "PCM"); |
358 |
break;
|
359 |
|
360 |
case OPCODE_START_STOP_AUDIO:
|
361 |
debug_ipmovie("start/stop audio\n");
|
362 |
url_fseek(pb, opcode_size, SEEK_CUR); |
363 |
break;
|
364 |
|
365 |
case OPCODE_INIT_VIDEO_BUFFERS:
|
366 |
debug_ipmovie("initialize video buffers\n");
|
367 |
if ((opcode_version > 2) || (opcode_size > 8)) { |
368 |
debug_ipmovie("bad init_video_buffers opcode\n");
|
369 |
chunk_type = CHUNK_BAD; |
370 |
break;
|
371 |
} |
372 |
if (get_buffer(pb, scratch, opcode_size) !=
|
373 |
opcode_size) { |
374 |
chunk_type = CHUNK_BAD; |
375 |
break;
|
376 |
} |
377 |
s->video_width = AV_RL16(&scratch[0]) * 8; |
378 |
s->video_height = AV_RL16(&scratch[2]) * 8; |
379 |
if (opcode_version < 2 || !AV_RL16(&scratch[6])) { |
380 |
s->video_bpp = 8;
|
381 |
} else {
|
382 |
s->video_bpp = 16;
|
383 |
} |
384 |
debug_ipmovie("video resolution: %d x %d\n",
|
385 |
s->video_width, s->video_height); |
386 |
break;
|
387 |
|
388 |
case OPCODE_UNKNOWN_06:
|
389 |
case OPCODE_UNKNOWN_0E:
|
390 |
case OPCODE_UNKNOWN_10:
|
391 |
case OPCODE_UNKNOWN_12:
|
392 |
case OPCODE_UNKNOWN_13:
|
393 |
case OPCODE_UNKNOWN_14:
|
394 |
case OPCODE_UNKNOWN_15:
|
395 |
debug_ipmovie("unknown (but documented) opcode %02X\n", opcode_type);
|
396 |
url_fseek(pb, opcode_size, SEEK_CUR); |
397 |
break;
|
398 |
|
399 |
case OPCODE_SEND_BUFFER:
|
400 |
debug_ipmovie("send buffer\n");
|
401 |
url_fseek(pb, opcode_size, SEEK_CUR); |
402 |
break;
|
403 |
|
404 |
case OPCODE_AUDIO_FRAME:
|
405 |
debug_ipmovie("audio frame\n");
|
406 |
|
407 |
/* log position and move on for now */
|
408 |
s->audio_chunk_offset = url_ftell(pb); |
409 |
s->audio_chunk_size = opcode_size; |
410 |
url_fseek(pb, opcode_size, SEEK_CUR); |
411 |
break;
|
412 |
|
413 |
case OPCODE_SILENCE_FRAME:
|
414 |
debug_ipmovie("silence frame\n");
|
415 |
url_fseek(pb, opcode_size, SEEK_CUR); |
416 |
break;
|
417 |
|
418 |
case OPCODE_INIT_VIDEO_MODE:
|
419 |
debug_ipmovie("initialize video mode\n");
|
420 |
url_fseek(pb, opcode_size, SEEK_CUR); |
421 |
break;
|
422 |
|
423 |
case OPCODE_CREATE_GRADIENT:
|
424 |
debug_ipmovie("create gradient\n");
|
425 |
url_fseek(pb, opcode_size, SEEK_CUR); |
426 |
break;
|
427 |
|
428 |
case OPCODE_SET_PALETTE:
|
429 |
debug_ipmovie("set palette\n");
|
430 |
/* check for the logical maximum palette size
|
431 |
* (3 * 256 + 4 bytes) */
|
432 |
if (opcode_size > 0x304) { |
433 |
debug_ipmovie("demux_ipmovie: set_palette opcode too large\n");
|
434 |
chunk_type = CHUNK_BAD; |
435 |
break;
|
436 |
} |
437 |
if (get_buffer(pb, scratch, opcode_size) != opcode_size) {
|
438 |
chunk_type = CHUNK_BAD; |
439 |
break;
|
440 |
} |
441 |
|
442 |
/* load the palette into internal data structure */
|
443 |
first_color = AV_RL16(&scratch[0]);
|
444 |
last_color = first_color + AV_RL16(&scratch[2]) - 1; |
445 |
/* sanity check (since they are 16 bit values) */
|
446 |
if ((first_color > 0xFF) || (last_color > 0xFF)) { |
447 |
debug_ipmovie("demux_ipmovie: set_palette indexes out of range (%d -> %d)\n",
|
448 |
first_color, last_color); |
449 |
chunk_type = CHUNK_BAD; |
450 |
break;
|
451 |
} |
452 |
j = 4; /* offset of first palette data */ |
453 |
for (i = first_color; i <= last_color; i++) {
|
454 |
/* the palette is stored as a 6-bit VGA palette, thus each
|
455 |
* component is shifted up to a 8-bit range */
|
456 |
r = scratch[j++] * 4;
|
457 |
g = scratch[j++] * 4;
|
458 |
b = scratch[j++] * 4;
|
459 |
s->palette_control.palette[i] = (r << 16) | (g << 8) | (b); |
460 |
} |
461 |
/* indicate a palette change */
|
462 |
s->palette_control.palette_changed = 1;
|
463 |
break;
|
464 |
|
465 |
case OPCODE_SET_PALETTE_COMPRESSED:
|
466 |
debug_ipmovie("set palette compressed\n");
|
467 |
url_fseek(pb, opcode_size, SEEK_CUR); |
468 |
break;
|
469 |
|
470 |
case OPCODE_SET_DECODING_MAP:
|
471 |
debug_ipmovie("set decoding map\n");
|
472 |
|
473 |
/* log position and move on for now */
|
474 |
s->decode_map_chunk_offset = url_ftell(pb); |
475 |
s->decode_map_chunk_size = opcode_size; |
476 |
url_fseek(pb, opcode_size, SEEK_CUR); |
477 |
break;
|
478 |
|
479 |
case OPCODE_VIDEO_DATA:
|
480 |
debug_ipmovie("set video data\n");
|
481 |
|
482 |
/* log position and move on for now */
|
483 |
s->video_chunk_offset = url_ftell(pb); |
484 |
s->video_chunk_size = opcode_size; |
485 |
url_fseek(pb, opcode_size, SEEK_CUR); |
486 |
break;
|
487 |
|
488 |
default:
|
489 |
debug_ipmovie("*** unknown opcode type\n");
|
490 |
chunk_type = CHUNK_BAD; |
491 |
break;
|
492 |
|
493 |
} |
494 |
} |
495 |
|
496 |
/* make a note of where the stream is sitting */
|
497 |
s->next_chunk_offset = url_ftell(pb); |
498 |
|
499 |
/* dispatch the first of any pending packets */
|
500 |
if ((chunk_type == CHUNK_VIDEO) || (chunk_type == CHUNK_AUDIO_ONLY))
|
501 |
chunk_type = load_ipmovie_packet(s, pb, pkt); |
502 |
|
503 |
return chunk_type;
|
504 |
} |
505 |
|
506 |
static const char signature[] = "Interplay MVE File\x1A\0\x1A"; |
507 |
|
508 |
static int ipmovie_probe(AVProbeData *p) |
509 |
{ |
510 |
uint8_t *b = p->buf; |
511 |
uint8_t *b_end = p->buf + p->buf_size - sizeof(signature);
|
512 |
do {
|
513 |
if (memcmp(b++, signature, sizeof(signature)) == 0) |
514 |
return AVPROBE_SCORE_MAX;
|
515 |
} while (b < b_end);
|
516 |
|
517 |
return 0; |
518 |
} |
519 |
|
520 |
static int ipmovie_read_header(AVFormatContext *s, |
521 |
AVFormatParameters *ap) |
522 |
{ |
523 |
IPMVEContext *ipmovie = s->priv_data; |
524 |
ByteIOContext *pb = s->pb; |
525 |
AVPacket pkt; |
526 |
AVStream *st; |
527 |
unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; |
528 |
int chunk_type;
|
529 |
uint8_t signature_buffer[sizeof(signature)];
|
530 |
|
531 |
get_buffer(pb, signature_buffer, sizeof(signature_buffer));
|
532 |
while (memcmp(signature_buffer, signature, sizeof(signature))) { |
533 |
memmove(signature_buffer, signature_buffer + 1, sizeof(signature_buffer) - 1); |
534 |
signature_buffer[sizeof(signature_buffer) - 1] = get_byte(pb); |
535 |
if (url_feof(pb))
|
536 |
return AVERROR_EOF;
|
537 |
} |
538 |
/* initialize private context members */
|
539 |
ipmovie->video_pts = ipmovie->audio_frame_count = 0;
|
540 |
ipmovie->audio_chunk_offset = ipmovie->video_chunk_offset = |
541 |
ipmovie->decode_map_chunk_offset = 0;
|
542 |
|
543 |
/* on the first read, this will position the stream at the first chunk */
|
544 |
ipmovie->next_chunk_offset = url_ftell(pb) + 4;
|
545 |
|
546 |
/* process the first chunk which should be CHUNK_INIT_VIDEO */
|
547 |
if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_VIDEO)
|
548 |
return AVERROR_INVALIDDATA;
|
549 |
|
550 |
/* peek ahead to the next chunk-- if it is an init audio chunk, process
|
551 |
* it; if it is the first video chunk, this is a silent file */
|
552 |
if (get_buffer(pb, chunk_preamble, CHUNK_PREAMBLE_SIZE) !=
|
553 |
CHUNK_PREAMBLE_SIZE) |
554 |
return AVERROR(EIO);
|
555 |
chunk_type = AV_RL16(&chunk_preamble[2]);
|
556 |
url_fseek(pb, -CHUNK_PREAMBLE_SIZE, SEEK_CUR); |
557 |
|
558 |
if (chunk_type == CHUNK_VIDEO)
|
559 |
ipmovie->audio_type = CODEC_ID_NONE; /* no audio */
|
560 |
else if (process_ipmovie_chunk(ipmovie, pb, &pkt) != CHUNK_INIT_AUDIO) |
561 |
return AVERROR_INVALIDDATA;
|
562 |
|
563 |
/* initialize the stream decoders */
|
564 |
st = av_new_stream(s, 0);
|
565 |
if (!st)
|
566 |
return AVERROR(ENOMEM);
|
567 |
av_set_pts_info(st, 63, 1, 1000000); |
568 |
ipmovie->video_stream_index = st->index; |
569 |
st->codec->codec_type = CODEC_TYPE_VIDEO; |
570 |
st->codec->codec_id = CODEC_ID_INTERPLAY_VIDEO; |
571 |
st->codec->codec_tag = 0; /* no fourcc */ |
572 |
st->codec->width = ipmovie->video_width; |
573 |
st->codec->height = ipmovie->video_height; |
574 |
st->codec->bits_per_coded_sample = ipmovie->video_bpp; |
575 |
|
576 |
/* palette considerations */
|
577 |
st->codec->palctrl = &ipmovie->palette_control; |
578 |
|
579 |
if (ipmovie->audio_type) {
|
580 |
st = av_new_stream(s, 0);
|
581 |
if (!st)
|
582 |
return AVERROR(ENOMEM);
|
583 |
av_set_pts_info(st, 32, 1, ipmovie->audio_sample_rate); |
584 |
ipmovie->audio_stream_index = st->index; |
585 |
st->codec->codec_type = CODEC_TYPE_AUDIO; |
586 |
st->codec->codec_id = ipmovie->audio_type; |
587 |
st->codec->codec_tag = 0; /* no tag */ |
588 |
st->codec->channels = ipmovie->audio_channels; |
589 |
st->codec->sample_rate = ipmovie->audio_sample_rate; |
590 |
st->codec->bits_per_coded_sample = ipmovie->audio_bits; |
591 |
st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * |
592 |
st->codec->bits_per_coded_sample; |
593 |
if (st->codec->codec_id == CODEC_ID_INTERPLAY_DPCM)
|
594 |
st->codec->bit_rate /= 2;
|
595 |
st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; |
596 |
} |
597 |
|
598 |
return 0; |
599 |
} |
600 |
|
601 |
static int ipmovie_read_packet(AVFormatContext *s, |
602 |
AVPacket *pkt) |
603 |
{ |
604 |
IPMVEContext *ipmovie = s->priv_data; |
605 |
ByteIOContext *pb = s->pb; |
606 |
int ret;
|
607 |
|
608 |
ret = process_ipmovie_chunk(ipmovie, pb, pkt); |
609 |
if (ret == CHUNK_BAD)
|
610 |
ret = AVERROR_INVALIDDATA; |
611 |
else if (ret == CHUNK_EOF) |
612 |
ret = AVERROR(EIO); |
613 |
else if (ret == CHUNK_NOMEM) |
614 |
ret = AVERROR(ENOMEM); |
615 |
else if (ret == CHUNK_VIDEO) |
616 |
ret = 0;
|
617 |
else
|
618 |
ret = -1;
|
619 |
|
620 |
return ret;
|
621 |
} |
622 |
|
623 |
AVInputFormat ipmovie_demuxer = { |
624 |
"ipmovie",
|
625 |
NULL_IF_CONFIG_SMALL("Interplay MVE format"),
|
626 |
sizeof(IPMVEContext),
|
627 |
ipmovie_probe, |
628 |
ipmovie_read_header, |
629 |
ipmovie_read_packet, |
630 |
}; |