xref: /third_party/ffmpeg/libavformat/rpl.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * ARMovie/RPL demuxer
3cabdff1aSopenharmony_ci * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman
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#include <inttypes.h>
23cabdff1aSopenharmony_ci#include <stdlib.h>
24cabdff1aSopenharmony_ci
25cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
26cabdff1aSopenharmony_ci#include "libavutil/dict.h"
27cabdff1aSopenharmony_ci#include "avformat.h"
28cabdff1aSopenharmony_ci#include "internal.h"
29cabdff1aSopenharmony_ci
30cabdff1aSopenharmony_ci#define RPL_SIGNATURE "ARMovie\x0A"
31cabdff1aSopenharmony_ci#define RPL_SIGNATURE_SIZE 8
32cabdff1aSopenharmony_ci
33cabdff1aSopenharmony_ci/** 256 is arbitrary, but should be big enough for any reasonable file. */
34cabdff1aSopenharmony_ci#define RPL_LINE_LENGTH 256
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_cistatic int rpl_probe(const AVProbeData *p)
37cabdff1aSopenharmony_ci{
38cabdff1aSopenharmony_ci    if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE))
39cabdff1aSopenharmony_ci        return 0;
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci    return AVPROBE_SCORE_MAX;
42cabdff1aSopenharmony_ci}
43cabdff1aSopenharmony_ci
44cabdff1aSopenharmony_citypedef struct RPLContext {
45cabdff1aSopenharmony_ci    // RPL header data
46cabdff1aSopenharmony_ci    int32_t frames_per_chunk;
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_ci    // Stream position data
49cabdff1aSopenharmony_ci    uint32_t chunk_number;
50cabdff1aSopenharmony_ci    uint32_t chunk_part;
51cabdff1aSopenharmony_ci    uint32_t frame_in_part;
52cabdff1aSopenharmony_ci} RPLContext;
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_cistatic int read_line(AVIOContext * pb, char* line, int bufsize)
55cabdff1aSopenharmony_ci{
56cabdff1aSopenharmony_ci    int i;
57cabdff1aSopenharmony_ci    for (i = 0; i < bufsize - 1; i++) {
58cabdff1aSopenharmony_ci        int b = avio_r8(pb);
59cabdff1aSopenharmony_ci        if (b == 0)
60cabdff1aSopenharmony_ci            break;
61cabdff1aSopenharmony_ci        if (b == '\n') {
62cabdff1aSopenharmony_ci            line[i] = '\0';
63cabdff1aSopenharmony_ci            return avio_feof(pb) ? -1 : 0;
64cabdff1aSopenharmony_ci        }
65cabdff1aSopenharmony_ci        line[i] = b;
66cabdff1aSopenharmony_ci    }
67cabdff1aSopenharmony_ci    line[i] = '\0';
68cabdff1aSopenharmony_ci    return -1;
69cabdff1aSopenharmony_ci}
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_cistatic int32_t read_int(const char* line, const char** endptr, int* error)
72cabdff1aSopenharmony_ci{
73cabdff1aSopenharmony_ci    unsigned long result = 0;
74cabdff1aSopenharmony_ci    for (; *line>='0' && *line<='9'; line++) {
75cabdff1aSopenharmony_ci        if (result > (0x7FFFFFFF - 9) / 10)
76cabdff1aSopenharmony_ci            *error = -1;
77cabdff1aSopenharmony_ci        result = 10 * result + *line - '0';
78cabdff1aSopenharmony_ci    }
79cabdff1aSopenharmony_ci    *endptr = line;
80cabdff1aSopenharmony_ci    return result;
81cabdff1aSopenharmony_ci}
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_cistatic int32_t read_line_and_int(AVIOContext * pb, int* error)
84cabdff1aSopenharmony_ci{
85cabdff1aSopenharmony_ci    char line[RPL_LINE_LENGTH];
86cabdff1aSopenharmony_ci    const char *endptr;
87cabdff1aSopenharmony_ci    *error |= read_line(pb, line, sizeof(line));
88cabdff1aSopenharmony_ci    return read_int(line, &endptr, error);
89cabdff1aSopenharmony_ci}
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci/** Parsing for fps, which can be a fraction. Unfortunately,
92cabdff1aSopenharmony_ci  * the spec for the header leaves out a lot of details,
93cabdff1aSopenharmony_ci  * so this is mostly guessing.
94cabdff1aSopenharmony_ci  */
95cabdff1aSopenharmony_cistatic AVRational read_fps(const char* line, int* error)
96cabdff1aSopenharmony_ci{
97cabdff1aSopenharmony_ci    int64_t num, den = 1;
98cabdff1aSopenharmony_ci    AVRational result;
99cabdff1aSopenharmony_ci    num = read_int(line, &line, error);
100cabdff1aSopenharmony_ci    if (*line == '.')
101cabdff1aSopenharmony_ci        line++;
102cabdff1aSopenharmony_ci    for (; *line>='0' && *line<='9'; line++) {
103cabdff1aSopenharmony_ci        // Truncate any numerator too large to fit into an int64_t
104cabdff1aSopenharmony_ci        if (num > (INT64_MAX - 9) / 10 || den > INT64_MAX / 10)
105cabdff1aSopenharmony_ci            break;
106cabdff1aSopenharmony_ci        num  = 10 * num + (*line - '0');
107cabdff1aSopenharmony_ci        den *= 10;
108cabdff1aSopenharmony_ci    }
109cabdff1aSopenharmony_ci    if (!num)
110cabdff1aSopenharmony_ci        *error = -1;
111cabdff1aSopenharmony_ci    av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF);
112cabdff1aSopenharmony_ci    return result;
113cabdff1aSopenharmony_ci}
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_cistatic int rpl_read_header(AVFormatContext *s)
116cabdff1aSopenharmony_ci{
117cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
118cabdff1aSopenharmony_ci    RPLContext *rpl = s->priv_data;
119cabdff1aSopenharmony_ci    AVStream *vst = NULL, *ast = NULL;
120cabdff1aSopenharmony_ci    int total_audio_size;
121cabdff1aSopenharmony_ci    int error = 0;
122cabdff1aSopenharmony_ci    const char *endptr;
123cabdff1aSopenharmony_ci    char audio_type[RPL_LINE_LENGTH];
124cabdff1aSopenharmony_ci    char audio_codec[RPL_LINE_LENGTH];
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    uint32_t i;
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_ci    int32_t video_format, audio_format, chunk_catalog_offset, number_of_chunks;
129cabdff1aSopenharmony_ci    AVRational fps;
130cabdff1aSopenharmony_ci
131cabdff1aSopenharmony_ci    char line[RPL_LINE_LENGTH];
132cabdff1aSopenharmony_ci
133cabdff1aSopenharmony_ci    // The header for RPL/ARMovie files is 21 lines of text
134cabdff1aSopenharmony_ci    // containing the various header fields.  The fields are always
135cabdff1aSopenharmony_ci    // in the same order, and other text besides the first
136cabdff1aSopenharmony_ci    // number usually isn't important.
137cabdff1aSopenharmony_ci    // (The spec says that there exists some significance
138cabdff1aSopenharmony_ci    // for the text in a few cases; samples needed.)
139cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));      // ARMovie
140cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));      // movie name
141cabdff1aSopenharmony_ci    av_dict_set(&s->metadata, "title"    , line, 0);
142cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));      // date/copyright
143cabdff1aSopenharmony_ci    av_dict_set(&s->metadata, "copyright", line, 0);
144cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));      // author and other
145cabdff1aSopenharmony_ci    av_dict_set(&s->metadata, "author"   , line, 0);
146cabdff1aSopenharmony_ci
147cabdff1aSopenharmony_ci    // video headers
148cabdff1aSopenharmony_ci    video_format = read_line_and_int(pb, &error);
149cabdff1aSopenharmony_ci    if (video_format) {
150cabdff1aSopenharmony_ci        vst = avformat_new_stream(s, NULL);
151cabdff1aSopenharmony_ci        if (!vst)
152cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
153cabdff1aSopenharmony_ci        vst->codecpar->codec_type      = AVMEDIA_TYPE_VIDEO;
154cabdff1aSopenharmony_ci        vst->codecpar->codec_tag       = video_format;
155cabdff1aSopenharmony_ci        vst->codecpar->width           = read_line_and_int(pb, &error);  // video width
156cabdff1aSopenharmony_ci        vst->codecpar->height          = read_line_and_int(pb, &error);  // video height
157cabdff1aSopenharmony_ci        vst->codecpar->bits_per_coded_sample = read_line_and_int(pb, &error);  // video bits per sample
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci        // Figure out the video codec
160cabdff1aSopenharmony_ci        switch (vst->codecpar->codec_tag) {
161cabdff1aSopenharmony_ci#if 0
162cabdff1aSopenharmony_ci            case 122:
163cabdff1aSopenharmony_ci                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE122;
164cabdff1aSopenharmony_ci                break;
165cabdff1aSopenharmony_ci#endif
166cabdff1aSopenharmony_ci            case 124:
167cabdff1aSopenharmony_ci                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE124;
168cabdff1aSopenharmony_ci                // The header is wrong here, at least sometimes
169cabdff1aSopenharmony_ci                vst->codecpar->bits_per_coded_sample = 16;
170cabdff1aSopenharmony_ci                break;
171cabdff1aSopenharmony_ci            case 130:
172cabdff1aSopenharmony_ci                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE130;
173cabdff1aSopenharmony_ci                break;
174cabdff1aSopenharmony_ci            default:
175cabdff1aSopenharmony_ci                avpriv_report_missing_feature(s, "Video format %s",
176cabdff1aSopenharmony_ci                                              av_fourcc2str(vst->codecpar->codec_tag));
177cabdff1aSopenharmony_ci                vst->codecpar->codec_id = AV_CODEC_ID_NONE;
178cabdff1aSopenharmony_ci        }
179cabdff1aSopenharmony_ci    } else {
180cabdff1aSopenharmony_ci        for (i = 0; i < 3; i++)
181cabdff1aSopenharmony_ci            error |= read_line(pb, line, sizeof(line));
182cabdff1aSopenharmony_ci    }
183cabdff1aSopenharmony_ci
184cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));                   // video frames per second
185cabdff1aSopenharmony_ci    fps = read_fps(line, &error);
186cabdff1aSopenharmony_ci    if (vst)
187cabdff1aSopenharmony_ci        avpriv_set_pts_info(vst, 32, fps.den, fps.num);
188cabdff1aSopenharmony_ci
189cabdff1aSopenharmony_ci    // Audio headers
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    // ARMovie supports multiple audio tracks; I don't have any
192cabdff1aSopenharmony_ci    // samples, though. This code will ignore additional tracks.
193cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));
194cabdff1aSopenharmony_ci    audio_format = read_int(line, &endptr, &error);  // audio format ID
195cabdff1aSopenharmony_ci    av_strlcpy(audio_codec, endptr, RPL_LINE_LENGTH);
196cabdff1aSopenharmony_ci    if (audio_format) {
197cabdff1aSopenharmony_ci        int channels;
198cabdff1aSopenharmony_ci        ast = avformat_new_stream(s, NULL);
199cabdff1aSopenharmony_ci        if (!ast)
200cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
201cabdff1aSopenharmony_ci        ast->codecpar->codec_type      = AVMEDIA_TYPE_AUDIO;
202cabdff1aSopenharmony_ci        ast->codecpar->codec_tag       = audio_format;
203cabdff1aSopenharmony_ci        ast->codecpar->sample_rate     = read_line_and_int(pb, &error);  // audio bitrate
204cabdff1aSopenharmony_ci        channels                       = read_line_and_int(pb, &error);  // number of audio channels
205cabdff1aSopenharmony_ci        error |= read_line(pb, line, sizeof(line));
206cabdff1aSopenharmony_ci        ast->codecpar->bits_per_coded_sample = read_int(line, &endptr, &error);  // audio bits per sample
207cabdff1aSopenharmony_ci        av_strlcpy(audio_type, endptr, RPL_LINE_LENGTH);
208cabdff1aSopenharmony_ci        ast->codecpar->ch_layout.nb_channels = channels;
209cabdff1aSopenharmony_ci        // At least one sample uses 0 for ADPCM, which is really 4 bits
210cabdff1aSopenharmony_ci        // per sample.
211cabdff1aSopenharmony_ci        if (ast->codecpar->bits_per_coded_sample == 0)
212cabdff1aSopenharmony_ci            ast->codecpar->bits_per_coded_sample = 4;
213cabdff1aSopenharmony_ci
214cabdff1aSopenharmony_ci        ast->codecpar->bit_rate = ast->codecpar->sample_rate *
215cabdff1aSopenharmony_ci                                  (int64_t)ast->codecpar->ch_layout.nb_channels;
216cabdff1aSopenharmony_ci        if (ast->codecpar->bit_rate > INT64_MAX / ast->codecpar->bits_per_coded_sample)
217cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
218cabdff1aSopenharmony_ci        ast->codecpar->bit_rate *= ast->codecpar->bits_per_coded_sample;
219cabdff1aSopenharmony_ci
220cabdff1aSopenharmony_ci        ast->codecpar->codec_id = AV_CODEC_ID_NONE;
221cabdff1aSopenharmony_ci        switch (audio_format) {
222cabdff1aSopenharmony_ci            case 1:
223cabdff1aSopenharmony_ci                if (ast->codecpar->bits_per_coded_sample == 16) {
224cabdff1aSopenharmony_ci                    // 16-bit audio is always signed
225cabdff1aSopenharmony_ci                    ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
226cabdff1aSopenharmony_ci                } else if (ast->codecpar->bits_per_coded_sample == 8) {
227cabdff1aSopenharmony_ci                    if (av_stristr(audio_type, "unsigned") != NULL)
228cabdff1aSopenharmony_ci                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
229cabdff1aSopenharmony_ci                    else if (av_stristr(audio_type, "linear") != NULL)
230cabdff1aSopenharmony_ci                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_S8;
231cabdff1aSopenharmony_ci                    else
232cabdff1aSopenharmony_ci                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_VIDC;
233cabdff1aSopenharmony_ci                }
234cabdff1aSopenharmony_ci                // There are some other formats listed as legal per the spec;
235cabdff1aSopenharmony_ci                // samples needed.
236cabdff1aSopenharmony_ci                break;
237cabdff1aSopenharmony_ci            case 2:
238cabdff1aSopenharmony_ci                if (av_stristr(audio_codec, "adpcm") != NULL) {
239cabdff1aSopenharmony_ci                    ast->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_ACORN;
240cabdff1aSopenharmony_ci                }
241cabdff1aSopenharmony_ci                break;
242cabdff1aSopenharmony_ci            case 101:
243cabdff1aSopenharmony_ci                if (ast->codecpar->bits_per_coded_sample == 8) {
244cabdff1aSopenharmony_ci                    // The samples with this kind of audio that I have
245cabdff1aSopenharmony_ci                    // are all unsigned.
246cabdff1aSopenharmony_ci                    ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
247cabdff1aSopenharmony_ci                } else if (ast->codecpar->bits_per_coded_sample == 4) {
248cabdff1aSopenharmony_ci                    ast->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_EA_SEAD;
249cabdff1aSopenharmony_ci                }
250cabdff1aSopenharmony_ci                break;
251cabdff1aSopenharmony_ci        }
252cabdff1aSopenharmony_ci        if (ast->codecpar->codec_id == AV_CODEC_ID_NONE)
253cabdff1aSopenharmony_ci            avpriv_request_sample(s, "Audio format %"PRId32" (%s)",
254cabdff1aSopenharmony_ci                                  audio_format, audio_codec);
255cabdff1aSopenharmony_ci        avpriv_set_pts_info(ast, 32, 1, ast->codecpar->bit_rate);
256cabdff1aSopenharmony_ci    } else {
257cabdff1aSopenharmony_ci        for (i = 0; i < 3; i++)
258cabdff1aSopenharmony_ci            error |= read_line(pb, line, sizeof(line));
259cabdff1aSopenharmony_ci    }
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_ci    if (s->nb_streams == 0)
262cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
263cabdff1aSopenharmony_ci
264cabdff1aSopenharmony_ci    rpl->frames_per_chunk = read_line_and_int(pb, &error);  // video frames per chunk
265cabdff1aSopenharmony_ci    if (vst && rpl->frames_per_chunk > 1 && vst->codecpar->codec_tag != 124)
266cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING,
267cabdff1aSopenharmony_ci               "Don't know how to split frames for video format %s. "
268cabdff1aSopenharmony_ci               "Video stream will be broken!\n", av_fourcc2str(vst->codecpar->codec_tag));
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ci    number_of_chunks = read_line_and_int(pb, &error);  // number of chunks in the file
271cabdff1aSopenharmony_ci    if (number_of_chunks == INT_MAX)
272cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
273cabdff1aSopenharmony_ci
274cabdff1aSopenharmony_ci    // The number in the header is actually the index of the last chunk.
275cabdff1aSopenharmony_ci    number_of_chunks++;
276cabdff1aSopenharmony_ci
277cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));  // "even" chunk size in bytes
278cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));  // "odd" chunk size in bytes
279cabdff1aSopenharmony_ci    chunk_catalog_offset =                       // offset of the "chunk catalog"
280cabdff1aSopenharmony_ci        read_line_and_int(pb, &error);           //   (file index)
281cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));  // offset to "helpful" sprite
282cabdff1aSopenharmony_ci    error |= read_line(pb, line, sizeof(line));  // size of "helpful" sprite
283cabdff1aSopenharmony_ci    if (vst) {
284cabdff1aSopenharmony_ci        error |= read_line(pb, line, sizeof(line));  // offset to key frame list
285cabdff1aSopenharmony_ci        vst->duration = number_of_chunks * (int64_t)rpl->frames_per_chunk;
286cabdff1aSopenharmony_ci    }
287cabdff1aSopenharmony_ci
288cabdff1aSopenharmony_ci    // Read the index
289cabdff1aSopenharmony_ci    avio_seek(pb, chunk_catalog_offset, SEEK_SET);
290cabdff1aSopenharmony_ci    total_audio_size = 0;
291cabdff1aSopenharmony_ci    for (i = 0; !error && i < number_of_chunks; i++) {
292cabdff1aSopenharmony_ci        int64_t offset, video_size, audio_size;
293cabdff1aSopenharmony_ci        error |= read_line(pb, line, sizeof(line));
294cabdff1aSopenharmony_ci        if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64,
295cabdff1aSopenharmony_ci                        &offset, &video_size, &audio_size)) {
296cabdff1aSopenharmony_ci            error = -1;
297cabdff1aSopenharmony_ci            continue;
298cabdff1aSopenharmony_ci        }
299cabdff1aSopenharmony_ci        if (vst)
300cabdff1aSopenharmony_ci            av_add_index_entry(vst, offset, i * rpl->frames_per_chunk,
301cabdff1aSopenharmony_ci                               video_size, rpl->frames_per_chunk, 0);
302cabdff1aSopenharmony_ci        if (ast)
303cabdff1aSopenharmony_ci            av_add_index_entry(ast, offset + video_size, total_audio_size,
304cabdff1aSopenharmony_ci                               audio_size, audio_size * 8, 0);
305cabdff1aSopenharmony_ci        total_audio_size += audio_size * 8;
306cabdff1aSopenharmony_ci    }
307cabdff1aSopenharmony_ci
308cabdff1aSopenharmony_ci    if (error)
309cabdff1aSopenharmony_ci        return AVERROR(EIO);
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci    return 0;
312cabdff1aSopenharmony_ci}
313cabdff1aSopenharmony_ci
314cabdff1aSopenharmony_cistatic int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
315cabdff1aSopenharmony_ci{
316cabdff1aSopenharmony_ci    RPLContext *rpl = s->priv_data;
317cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
318cabdff1aSopenharmony_ci    AVStream* stream;
319cabdff1aSopenharmony_ci    FFStream *sti;
320cabdff1aSopenharmony_ci    AVIndexEntry* index_entry;
321cabdff1aSopenharmony_ci    int ret;
322cabdff1aSopenharmony_ci
323cabdff1aSopenharmony_ci    if (rpl->chunk_part == s->nb_streams) {
324cabdff1aSopenharmony_ci        rpl->chunk_number++;
325cabdff1aSopenharmony_ci        rpl->chunk_part = 0;
326cabdff1aSopenharmony_ci    }
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci    stream = s->streams[rpl->chunk_part];
329cabdff1aSopenharmony_ci    sti    = ffstream(stream);
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci    if (rpl->chunk_number >= sti->nb_index_entries)
332cabdff1aSopenharmony_ci        return AVERROR_EOF;
333cabdff1aSopenharmony_ci
334cabdff1aSopenharmony_ci    index_entry = &sti->index_entries[rpl->chunk_number];
335cabdff1aSopenharmony_ci
336cabdff1aSopenharmony_ci    if (rpl->frame_in_part == 0) {
337cabdff1aSopenharmony_ci        if (avio_seek(pb, index_entry->pos, SEEK_SET) < 0)
338cabdff1aSopenharmony_ci            return AVERROR(EIO);
339cabdff1aSopenharmony_ci    }
340cabdff1aSopenharmony_ci
341cabdff1aSopenharmony_ci    if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
342cabdff1aSopenharmony_ci        stream->codecpar->codec_tag == 124) {
343cabdff1aSopenharmony_ci        // We have to split Escape 124 frames because there are
344cabdff1aSopenharmony_ci        // multiple frames per chunk in Escape 124 samples.
345cabdff1aSopenharmony_ci        uint32_t frame_size;
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_ci        avio_skip(pb, 4); /* flags */
348cabdff1aSopenharmony_ci        frame_size = avio_rl32(pb);
349cabdff1aSopenharmony_ci        if (avio_feof(pb) || avio_seek(pb, -8, SEEK_CUR) < 0 || !frame_size)
350cabdff1aSopenharmony_ci            return AVERROR(EIO);
351cabdff1aSopenharmony_ci
352cabdff1aSopenharmony_ci        ret = av_get_packet(pb, pkt, frame_size);
353cabdff1aSopenharmony_ci        if (ret < 0)
354cabdff1aSopenharmony_ci            return ret;
355cabdff1aSopenharmony_ci        if (ret != frame_size)
356cabdff1aSopenharmony_ci            return AVERROR(EIO);
357cabdff1aSopenharmony_ci
358cabdff1aSopenharmony_ci        pkt->duration = 1;
359cabdff1aSopenharmony_ci        pkt->pts = index_entry->timestamp + rpl->frame_in_part;
360cabdff1aSopenharmony_ci        pkt->stream_index = rpl->chunk_part;
361cabdff1aSopenharmony_ci
362cabdff1aSopenharmony_ci        rpl->frame_in_part++;
363cabdff1aSopenharmony_ci        if (rpl->frame_in_part == rpl->frames_per_chunk) {
364cabdff1aSopenharmony_ci            rpl->frame_in_part = 0;
365cabdff1aSopenharmony_ci            rpl->chunk_part++;
366cabdff1aSopenharmony_ci        }
367cabdff1aSopenharmony_ci    } else {
368cabdff1aSopenharmony_ci        ret = av_get_packet(pb, pkt, index_entry->size);
369cabdff1aSopenharmony_ci        if (ret < 0)
370cabdff1aSopenharmony_ci            return ret;
371cabdff1aSopenharmony_ci        if (ret != index_entry->size)
372cabdff1aSopenharmony_ci            return AVERROR(EIO);
373cabdff1aSopenharmony_ci
374cabdff1aSopenharmony_ci        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
375cabdff1aSopenharmony_ci            // frames_per_chunk should always be one here; the header
376cabdff1aSopenharmony_ci            // parsing will warn if it isn't.
377cabdff1aSopenharmony_ci            pkt->duration = rpl->frames_per_chunk;
378cabdff1aSopenharmony_ci        } else {
379cabdff1aSopenharmony_ci            // All the audio codecs supported in this container
380cabdff1aSopenharmony_ci            // (at least so far) are constant-bitrate.
381cabdff1aSopenharmony_ci            pkt->duration = ret * 8;
382cabdff1aSopenharmony_ci        }
383cabdff1aSopenharmony_ci        pkt->pts = index_entry->timestamp;
384cabdff1aSopenharmony_ci        pkt->stream_index = rpl->chunk_part;
385cabdff1aSopenharmony_ci        rpl->chunk_part++;
386cabdff1aSopenharmony_ci    }
387cabdff1aSopenharmony_ci
388cabdff1aSopenharmony_ci    // None of the Escape formats have keyframes, and the ADPCM
389cabdff1aSopenharmony_ci    // format used doesn't have keyframes.
390cabdff1aSopenharmony_ci    if (rpl->chunk_number == 0 && rpl->frame_in_part == 0)
391cabdff1aSopenharmony_ci        pkt->flags |= AV_PKT_FLAG_KEY;
392cabdff1aSopenharmony_ci
393cabdff1aSopenharmony_ci    return ret;
394cabdff1aSopenharmony_ci}
395cabdff1aSopenharmony_ci
396cabdff1aSopenharmony_ciconst AVInputFormat ff_rpl_demuxer = {
397cabdff1aSopenharmony_ci    .name           = "rpl",
398cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("RPL / ARMovie"),
399cabdff1aSopenharmony_ci    .priv_data_size = sizeof(RPLContext),
400cabdff1aSopenharmony_ci    .read_probe     = rpl_probe,
401cabdff1aSopenharmony_ci    .read_header    = rpl_read_header,
402cabdff1aSopenharmony_ci    .read_packet    = rpl_read_packet,
403cabdff1aSopenharmony_ci};
404