rpl.c 13.6 KB
Newer Older
E
Eli Friedman 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * ARMovie/RPL demuxer
 * Copyright (c) 2007 Christian Ohm, 2008 Eli Friedman
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

22
#include <inttypes.h>
23 24
#include <stdlib.h>

25
#include "libavutil/avstring.h"
26
#include "libavutil/dict.h"
E
Eli Friedman 已提交
27
#include "avformat.h"
28
#include "internal.h"
E
Eli Friedman 已提交
29 30 31 32 33 34 35

#define RPL_SIGNATURE "ARMovie\x0A"
#define RPL_SIGNATURE_SIZE 8

/** 256 is arbitrary, but should be big enough for any reasonable file. */
#define RPL_LINE_LENGTH 256

36
static int rpl_probe(const AVProbeData *p)
E
Eli Friedman 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
{
    if (memcmp(p->buf, RPL_SIGNATURE, RPL_SIGNATURE_SIZE))
        return 0;

    return AVPROBE_SCORE_MAX;
}

typedef struct RPLContext {
    // RPL header data
    int32_t frames_per_chunk;

    // Stream position data
    uint32_t chunk_number;
    uint32_t chunk_part;
    uint32_t frame_in_part;
} RPLContext;

54
static int read_line(AVIOContext * pb, char* line, int bufsize)
E
Eli Friedman 已提交
55 56 57
{
    int i;
    for (i = 0; i < bufsize - 1; i++) {
58
        int b = avio_r8(pb);
E
Eli Friedman 已提交
59 60 61 62
        if (b == 0)
            break;
        if (b == '\n') {
            line[i] = '\0';
63
            return avio_feof(pb) ? -1 : 0;
E
Eli Friedman 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
        }
        line[i] = b;
    }
    line[i] = '\0';
    return -1;
}

static int32_t read_int(const char* line, const char** endptr, int* error)
{
    unsigned long result = 0;
    for (; *line>='0' && *line<='9'; line++) {
        if (result > (0x7FFFFFFF - 9) / 10)
            *error = -1;
        result = 10 * result + *line - '0';
    }
    *endptr = line;
    return result;
}

83
static int32_t read_line_and_int(AVIOContext * pb, int* error)
E
Eli Friedman 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
{
    char line[RPL_LINE_LENGTH];
    const char *endptr;
    *error |= read_line(pb, line, sizeof(line));
    return read_int(line, &endptr, error);
}

/** Parsing for fps, which can be a fraction. Unfortunately,
  * the spec for the header leaves out a lot of details,
  * so this is mostly guessing.
  */
static AVRational read_fps(const char* line, int* error)
{
    int64_t num, den = 1;
    AVRational result;
    num = read_int(line, &line, error);
    if (*line == '.')
        line++;
    for (; *line>='0' && *line<='9'; line++) {
        // Truncate any numerator too large to fit into an int64_t
        if (num > (INT64_MAX - 9) / 10 || den > INT64_MAX / 10)
            break;
        num  = 10 * num + *line - '0';
        den *= 10;
    }
    if (!num)
        *error = -1;
    av_reduce(&result.num, &result.den, num, den, 0x7FFFFFFF);
    return result;
}

115
static int rpl_read_header(AVFormatContext *s)
E
Eli Friedman 已提交
116
{
117
    AVIOContext *pb = s->pb;
E
Eli Friedman 已提交
118 119 120 121
    RPLContext *rpl = s->priv_data;
    AVStream *vst = NULL, *ast = NULL;
    int total_audio_size;
    int error = 0;
122 123
    const char *endptr;
    char audio_type[RPL_LINE_LENGTH];
E
Eli Friedman 已提交
124 125 126

    uint32_t i;

127
    int32_t video_format, audio_format, chunk_catalog_offset, number_of_chunks;
E
Eli Friedman 已提交
128 129 130 131 132 133 134 135 136 137
    AVRational fps;

    char line[RPL_LINE_LENGTH];

    // The header for RPL/ARMovie files is 21 lines of text
    // containing the various header fields.  The fields are always
    // in the same order, and other text besides the first
    // number usually isn't important.
    // (The spec says that there exists some significance
    // for the text in a few cases; samples needed.)
A
Aurelien Jacobs 已提交
138
    error |= read_line(pb, line, sizeof(line));      // ARMovie
139
    error |= read_line(pb, line, sizeof(line));      // movie name
140
    av_dict_set(&s->metadata, "title"    , line, 0);
141
    error |= read_line(pb, line, sizeof(line));      // date/copyright
142
    av_dict_set(&s->metadata, "copyright", line, 0);
143
    error |= read_line(pb, line, sizeof(line));      // author and other
144
    av_dict_set(&s->metadata, "author"   , line, 0);
E
Eli Friedman 已提交
145 146

    // video headers
147 148 149 150 151 152 153 154 155 156
    video_format = read_line_and_int(pb, &error);
    if (video_format) {
        vst = avformat_new_stream(s, NULL);
        if (!vst)
            return AVERROR(ENOMEM);
        vst->codecpar->codec_type      = AVMEDIA_TYPE_VIDEO;
        vst->codecpar->codec_tag       = video_format;
        vst->codecpar->width           = read_line_and_int(pb, &error);  // video width
        vst->codecpar->height          = read_line_and_int(pb, &error);  // video height
        vst->codecpar->bits_per_coded_sample = read_line_and_int(pb, &error);  // video bits per sample
E
Eli Friedman 已提交
157

158 159
        // Figure out the video codec
        switch (vst->codecpar->codec_tag) {
E
Eli Friedman 已提交
160
#if 0
161 162 163
            case 122:
                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE122;
                break;
E
Eli Friedman 已提交
164
#endif
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
            case 124:
                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE124;
                // The header is wrong here, at least sometimes
                vst->codecpar->bits_per_coded_sample = 16;
                break;
            case 130:
                vst->codecpar->codec_id = AV_CODEC_ID_ESCAPE130;
                break;
            default:
                avpriv_report_missing_feature(s, "Video format %s",
                                              av_fourcc2str(vst->codecpar->codec_tag));
                vst->codecpar->codec_id = AV_CODEC_ID_NONE;
        }
    } else {
        for (i = 0; i < 3; i++)
            error |= read_line(pb, line, sizeof(line));
E
Eli Friedman 已提交
181 182
    }

183 184 185 186 187
    error |= read_line(pb, line, sizeof(line));                   // video frames per second
    fps = read_fps(line, &error);
    if (vst)
        avpriv_set_pts_info(vst, 32, fps.den, fps.num);

E
Eli Friedman 已提交
188 189 190 191 192 193
    // Audio headers

    // ARMovie supports multiple audio tracks; I don't have any
    // samples, though. This code will ignore additional tracks.
    audio_format = read_line_and_int(pb, &error);  // audio format ID
    if (audio_format) {
194
        ast = avformat_new_stream(s, NULL);
E
Eli Friedman 已提交
195 196
        if (!ast)
            return AVERROR(ENOMEM);
197 198 199 200
        ast->codecpar->codec_type      = AVMEDIA_TYPE_AUDIO;
        ast->codecpar->codec_tag       = audio_format;
        ast->codecpar->sample_rate     = read_line_and_int(pb, &error);  // audio bitrate
        ast->codecpar->channels        = read_line_and_int(pb, &error);  // number of audio channels
201 202
        error |= read_line(pb, line, sizeof(line));
        ast->codecpar->bits_per_coded_sample = read_int(line, &endptr, &error);  // audio bits per sample
203
        av_strlcpy(audio_type, endptr, RPL_LINE_LENGTH);
E
Eli Friedman 已提交
204 205
        // At least one sample uses 0 for ADPCM, which is really 4 bits
        // per sample.
206 207
        if (ast->codecpar->bits_per_coded_sample == 0)
            ast->codecpar->bits_per_coded_sample = 4;
E
Eli Friedman 已提交
208

209 210 211
        ast->codecpar->bit_rate = ast->codecpar->sample_rate *
                                  ast->codecpar->bits_per_coded_sample *
                                  ast->codecpar->channels;
E
Eli Friedman 已提交
212

213
        ast->codecpar->codec_id = AV_CODEC_ID_NONE;
E
Eli Friedman 已提交
214 215
        switch (audio_format) {
            case 1:
216
                if (ast->codecpar->bits_per_coded_sample == 16) {
E
Eli Friedman 已提交
217
                    // 16-bit audio is always signed
218
                    ast->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
E
Eli Friedman 已提交
219
                    break;
220
                } else if (ast->codecpar->bits_per_coded_sample == 8) {
221
                    if(av_stristr(audio_type, "unsigned") != NULL) {
222 223
                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
                        break;
224
                    } else if(av_stristr(audio_type, "linear") != NULL) {
225 226 227 228 229 230
                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_S8;
                        break;
                    } else {
                        ast->codecpar->codec_id = AV_CODEC_ID_PCM_VIDC;
                        break;
                    }
E
Eli Friedman 已提交
231 232 233 234 235
                }
                // There are some other formats listed as legal per the spec;
                // samples needed.
                break;
            case 101:
236
                if (ast->codecpar->bits_per_coded_sample == 8) {
E
Eli Friedman 已提交
237 238
                    // The samples with this kind of audio that I have
                    // are all unsigned.
239
                    ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
E
Eli Friedman 已提交
240
                    break;
241 242
                } else if (ast->codecpar->bits_per_coded_sample == 4) {
                    ast->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_EA_SEAD;
E
Eli Friedman 已提交
243 244 245 246
                    break;
                }
                break;
        }
247
        if (ast->codecpar->codec_id == AV_CODEC_ID_NONE)
248 249
            avpriv_request_sample(s, "Audio format %"PRId32,
                                  audio_format);
250
        avpriv_set_pts_info(ast, 32, 1, ast->codecpar->bit_rate);
E
Eli Friedman 已提交
251 252 253 254 255 256
    } else {
        for (i = 0; i < 3; i++)
            error |= read_line(pb, line, sizeof(line));
    }

    rpl->frames_per_chunk = read_line_and_int(pb, &error);  // video frames per chunk
257
    if (vst && rpl->frames_per_chunk > 1 && vst->codecpar->codec_tag != 124)
E
Eli Friedman 已提交
258
        av_log(s, AV_LOG_WARNING,
259 260
               "Don't know how to split frames for video format %s. "
               "Video stream will be broken!\n", av_fourcc2str(vst->codecpar->codec_tag));
E
Eli Friedman 已提交
261 262 263 264 265 266 267 268 269 270 271

    number_of_chunks = read_line_and_int(pb, &error);  // number of chunks in the file
    // The number in the header is actually the index of the last chunk.
    number_of_chunks++;

    error |= read_line(pb, line, sizeof(line));  // "even" chunk size in bytes
    error |= read_line(pb, line, sizeof(line));  // "odd" chunk size in bytes
    chunk_catalog_offset =                       // offset of the "chunk catalog"
        read_line_and_int(pb, &error);           //   (file index)
    error |= read_line(pb, line, sizeof(line));  // offset to "helpful" sprite
    error |= read_line(pb, line, sizeof(line));  // size of "helpful" sprite
272
    if (vst) {
273
        error |= read_line(pb, line, sizeof(line));  // offset to key frame list
274 275
        vst->duration = number_of_chunks * rpl->frames_per_chunk;
    }
E
Eli Friedman 已提交
276 277

    // Read the index
A
Anton Khirnov 已提交
278
    avio_seek(pb, chunk_catalog_offset, SEEK_SET);
E
Eli Friedman 已提交
279
    total_audio_size = 0;
280
    for (i = 0; !error && i < number_of_chunks; i++) {
E
Eli Friedman 已提交
281 282
        int64_t offset, video_size, audio_size;
        error |= read_line(pb, line, sizeof(line));
283
        if (3 != sscanf(line, "%"SCNd64" , %"SCNd64" ; %"SCNd64,
284
                        &offset, &video_size, &audio_size)) {
E
Eli Friedman 已提交
285
            error = -1;
286 287
            continue;
        }
288 289 290
        if (vst)
            av_add_index_entry(vst, offset, i * rpl->frames_per_chunk,
                               video_size, rpl->frames_per_chunk, 0);
E
Eli Friedman 已提交
291 292 293 294 295 296 297 298 299 300 301 302 303 304
        if (ast)
            av_add_index_entry(ast, offset + video_size, total_audio_size,
                               audio_size, audio_size * 8, 0);
        total_audio_size += audio_size * 8;
    }

    if (error) return AVERROR(EIO);

    return 0;
}

static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    RPLContext *rpl = s->priv_data;
305
    AVIOContext *pb = s->pb;
E
Eli Friedman 已提交
306 307
    AVStream* stream;
    AVIndexEntry* index_entry;
308
    int ret;
E
Eli Friedman 已提交
309 310 311 312 313 314 315 316 317

    if (rpl->chunk_part == s->nb_streams) {
        rpl->chunk_number++;
        rpl->chunk_part = 0;
    }

    stream = s->streams[rpl->chunk_part];

    if (rpl->chunk_number >= stream->nb_index_entries)
318
        return AVERROR_EOF;
E
Eli Friedman 已提交
319 320 321 322

    index_entry = &stream->index_entries[rpl->chunk_number];

    if (rpl->frame_in_part == 0)
A
Anton Khirnov 已提交
323
        if (avio_seek(pb, index_entry->pos, SEEK_SET) < 0)
E
Eli Friedman 已提交
324 325
            return AVERROR(EIO);

326 327
    if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
        stream->codecpar->codec_tag == 124) {
E
Eli Friedman 已提交
328 329
        // We have to split Escape 124 frames because there are
        // multiple frames per chunk in Escape 124 samples.
M
Mans Rullgard 已提交
330
        uint32_t frame_size;
E
Eli Friedman 已提交
331

M
Mans Rullgard 已提交
332
        avio_skip(pb, 4); /* flags */
333
        frame_size = avio_rl32(pb);
A
Anton Khirnov 已提交
334
        if (avio_seek(pb, -8, SEEK_CUR) < 0)
E
Eli Friedman 已提交
335 336 337
            return AVERROR(EIO);

        ret = av_get_packet(pb, pkt, frame_size);
338 339
        if (ret < 0)
            return ret;
E
Eli Friedman 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353
        if (ret != frame_size) {
            return AVERROR(EIO);
        }
        pkt->duration = 1;
        pkt->pts = index_entry->timestamp + rpl->frame_in_part;
        pkt->stream_index = rpl->chunk_part;

        rpl->frame_in_part++;
        if (rpl->frame_in_part == rpl->frames_per_chunk) {
            rpl->frame_in_part = 0;
            rpl->chunk_part++;
        }
    } else {
        ret = av_get_packet(pb, pkt, index_entry->size);
354 355
        if (ret < 0)
            return ret;
E
Eli Friedman 已提交
356 357 358 359
        if (ret != index_entry->size) {
            return AVERROR(EIO);
        }

360
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
E
Eli Friedman 已提交
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
            // frames_per_chunk should always be one here; the header
            // parsing will warn if it isn't.
            pkt->duration = rpl->frames_per_chunk;
        } else {
            // All the audio codecs supported in this container
            // (at least so far) are constant-bitrate.
            pkt->duration = ret * 8;
        }
        pkt->pts = index_entry->timestamp;
        pkt->stream_index = rpl->chunk_part;
        rpl->chunk_part++;
    }

    // None of the Escape formats have keyframes, and the ADPCM
    // format used doesn't have keyframes.
    if (rpl->chunk_number == 0 && rpl->frame_in_part == 0)
377
        pkt->flags |= AV_PKT_FLAG_KEY;
E
Eli Friedman 已提交
378 379 380 381

    return ret;
}

382
AVInputFormat ff_rpl_demuxer = {
383
    .name           = "rpl",
384
    .long_name      = NULL_IF_CONFIG_SMALL("RPL / ARMovie"),
385 386 387 388
    .priv_data_size = sizeof(RPLContext),
    .read_probe     = rpl_probe,
    .read_header    = rpl_read_header,
    .read_packet    = rpl_read_packet,
E
Eli Friedman 已提交
389
};