PageRenderTime 70ms CodeModel.GetById 12ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/libavformat/rpl.c

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