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