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