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