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