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