r3d.c 11.5 KB
Newer Older
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * R3D REDCODE demuxer
 * Copyright (c) 2008 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
 *
 * 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
 */

#include "libavutil/intreadwrite.h"
23
#include "libavutil/dict.h"
24
#include "libavutil/mathematics.h"
Baptiste Coudurier's avatar
Baptiste Coudurier committed
25
#include "avformat.h"
26
#include "internal.h"
Baptiste Coudurier's avatar
Baptiste Coudurier committed
27

28
typedef struct R3DContext {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
29 30 31 32 33
    unsigned video_offsets_count;
    unsigned *video_offsets;
    unsigned rdvo_offset;
} R3DContext;

34
typedef struct Atom {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
35 36 37 38 39 40 41
    unsigned size;
    uint32_t tag;
    uint64_t offset;
} Atom;

static int read_atom(AVFormatContext *s, Atom *atom)
{
42
    atom->offset = avio_tell(s->pb);
43
    atom->size = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
44 45
    if (atom->size < 8)
        return -1;
46
    atom->tag = avio_rl32(s->pb);
47
    av_log(s, AV_LOG_TRACE, "atom %u %.4s offset %#"PRIx64"\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
48 49 50 51 52 53
            atom->size, (char*)&atom->tag, atom->offset);
    return atom->size;
}

static int r3d_read_red1(AVFormatContext *s)
{
54
    AVStream *st = avformat_new_stream(s, NULL);
55
    char filename[258];
56 57
    int tmp;
    int av_unused tmp2;
58
    AVRational framerate;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
59 60

    if (!st)
61
        return AVERROR(ENOMEM);
62
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
63
    st->codec->codec_id = AV_CODEC_ID_JPEG2000;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
64

65 66
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
67
    av_log(s, AV_LOG_TRACE, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
68

69
    tmp = avio_rb16(s->pb); // unknown
70
    av_log(s, AV_LOG_TRACE, "unknown1 %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
71

72
    tmp = avio_rb32(s->pb);
73
    avpriv_set_pts_info(st, 32, 1, tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
74

75
    tmp = avio_rb32(s->pb); // filenum
76
    av_log(s, AV_LOG_TRACE, "filenum %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
77

78
    avio_skip(s->pb, 32); // unknown
Baptiste Coudurier's avatar
Baptiste Coudurier committed
79

80 81
    st->codec->width  = avio_rb32(s->pb);
    st->codec->height = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
82

83
    tmp = avio_rb16(s->pb); // unknown
84
    av_log(s, AV_LOG_TRACE, "unknown2 %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
85

86 87
    framerate.num = avio_rb16(s->pb);
    framerate.den = avio_rb16(s->pb);
88
    if (framerate.num > 0 && framerate.den > 0) {
89 90 91 92 93
#if FF_API_R_FRAME_RATE
        st->r_frame_rate =
#endif
        st->avg_frame_rate = framerate;
    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
94

95
    tmp = avio_r8(s->pb); // audio channels
96
    av_log(s, AV_LOG_TRACE, "audio channels %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
97
    if (tmp > 0) {
98
        AVStream *ast = avformat_new_stream(s, NULL);
99 100
        if (!ast)
            return AVERROR(ENOMEM);
101
        ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
102
        ast->codec->codec_id = AV_CODEC_ID_PCM_S32BE;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
103
        ast->codec->channels = tmp;
104
        avpriv_set_pts_info(ast, 32, 1, st->time_base.den);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
105 106
    }

107
    avio_read(s->pb, filename, 257);
108
    filename[sizeof(filename)-1] = 0;
109
    av_dict_set(&st->metadata, "filename", filename, 0);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
110

111 112 113 114
    av_log(s, AV_LOG_TRACE, "filename %s\n", filename);
    av_log(s, AV_LOG_TRACE, "resolution %dx%d\n", st->codec->width, st->codec->height);
    av_log(s, AV_LOG_TRACE, "timescale %d\n", st->time_base.den);
    av_log(s, AV_LOG_TRACE, "frame rate %d/%d\n",
115
            framerate.num, framerate.den);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

    return 0;
}

static int r3d_read_rdvo(AVFormatContext *s, Atom *atom)
{
    R3DContext *r3d = s->priv_data;
    AVStream *st = s->streams[0];
    int i;

    r3d->video_offsets_count = (atom->size - 8) / 4;
    r3d->video_offsets = av_malloc(atom->size);
    if (!r3d->video_offsets)
        return AVERROR(ENOMEM);

    for (i = 0; i < r3d->video_offsets_count; i++) {
132
        r3d->video_offsets[i] = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
133 134 135 136
        if (!r3d->video_offsets[i]) {
            r3d->video_offsets_count = i;
            break;
        }
137
        av_log(s, AV_LOG_TRACE, "video offset %d: %#x\n", i, r3d->video_offsets[i]);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
138 139
    }

140
    if (st->avg_frame_rate.num)
141
        st->duration = av_rescale_q(r3d->video_offsets_count,
142
                                    av_inv_q(st->avg_frame_rate),
143
                                    st->time_base);
144
    av_log(s, AV_LOG_TRACE, "duration %"PRId64"\n", st->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
145 146 147 148 149 150 151

    return 0;
}

static void r3d_read_reos(AVFormatContext *s)
{
    R3DContext *r3d = s->priv_data;
152
    int av_unused tmp;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
153

154 155 156 157
    r3d->rdvo_offset = avio_rb32(s->pb);
    avio_rb32(s->pb); // rdvs offset
    avio_rb32(s->pb); // rdao offset
    avio_rb32(s->pb); // rdas offset
Baptiste Coudurier's avatar
Baptiste Coudurier committed
158

159
    tmp = avio_rb32(s->pb);
160
    av_log(s, AV_LOG_TRACE, "num video chunks %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
161

162
    tmp = avio_rb32(s->pb);
163
    av_log(s, AV_LOG_TRACE, "num audio chunks %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
164

165
    avio_skip(s->pb, 6*4);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
166 167
}

168
static int r3d_read_header(AVFormatContext *s)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
{
    R3DContext *r3d = s->priv_data;
    Atom atom;
    int ret;

    if (read_atom(s, &atom) < 0) {
        av_log(s, AV_LOG_ERROR, "error reading atom\n");
        return -1;
    }
    if (atom.tag == MKTAG('R','E','D','1')) {
        if ((ret = r3d_read_red1(s)) < 0) {
            av_log(s, AV_LOG_ERROR, "error parsing 'red1' atom\n");
            return ret;
        }
    } else {
        av_log(s, AV_LOG_ERROR, "could not find 'red1' atom\n");
        return -1;
    }

188
    s->internal->data_offset = avio_tell(s->pb);
189
    av_log(s, AV_LOG_TRACE, "data offset %#"PRIx64"\n", s->internal->data_offset);
190
    if (!s->pb->seekable)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
191 192
        return 0;
    // find REOB/REOF/REOS to load index
193
    avio_seek(s->pb, avio_size(s->pb)-48-8, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
194 195 196 197 198 199 200 201 202 203 204
    if (read_atom(s, &atom) < 0)
        av_log(s, AV_LOG_ERROR, "error reading end atom\n");

    if (atom.tag != MKTAG('R','E','O','B') &&
        atom.tag != MKTAG('R','E','O','F') &&
        atom.tag != MKTAG('R','E','O','S'))
        goto out;

    r3d_read_reos(s);

    if (r3d->rdvo_offset) {
205
        avio_seek(s->pb, r3d->rdvo_offset, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
206 207 208 209 210 211 212 213 214
        if (read_atom(s, &atom) < 0)
            av_log(s, AV_LOG_ERROR, "error reading 'rdvo' atom\n");
        if (atom.tag == MKTAG('R','D','V','O')) {
            if (r3d_read_rdvo(s, &atom) < 0)
                av_log(s, AV_LOG_ERROR, "error parsing 'rdvo' atom\n");
        }
    }

 out:
215
    avio_seek(s->pb, s->internal->data_offset, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
216 217 218 219 220 221
    return 0;
}

static int r3d_read_redv(AVFormatContext *s, AVPacket *pkt, Atom *atom)
{
    AVStream *st = s->streams[0];
222 223
    int tmp;
    int av_unused tmp2;
Luca Barbato's avatar
Luca Barbato committed
224
    int64_t pos = avio_tell(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
225
    unsigned dts;
226
    int ret;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
227

228
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
229

230
    tmp = avio_rb32(s->pb);
231
    av_log(s, AV_LOG_TRACE, "frame num %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
232

233 234
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
235
    av_log(s, AV_LOG_TRACE, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
236

237
    tmp = avio_rb16(s->pb); // unknown
238
    av_log(s, AV_LOG_TRACE, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
239 240

    if (tmp > 4) {
241
        tmp = avio_rb16(s->pb); // unknown
242
        av_log(s, AV_LOG_TRACE, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
243

244
        tmp = avio_rb16(s->pb); // unknown
245
        av_log(s, AV_LOG_TRACE, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
246

247
        tmp = avio_rb32(s->pb);
248
        av_log(s, AV_LOG_TRACE, "width %d\n", tmp);
249
        tmp = avio_rb32(s->pb);
250
        av_log(s, AV_LOG_TRACE, "height %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
251

252
        tmp = avio_rb32(s->pb);
253
        av_log(s, AV_LOG_TRACE, "metadata len %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
254
    }
255
    tmp = atom->size - 8 - (avio_tell(s->pb) - pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
256 257
    if (tmp < 0)
        return -1;
258 259
    ret = av_get_packet(s->pb, pkt, tmp);
    if (ret < 0) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
260 261 262 263 264 265
        av_log(s, AV_LOG_ERROR, "error reading video packet\n");
        return -1;
    }

    pkt->stream_index = 0;
    pkt->dts = dts;
266
    if (st->avg_frame_rate.num)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
267
        pkt->duration = (uint64_t)st->time_base.den*
268
            st->avg_frame_rate.den/st->avg_frame_rate.num;
269
    av_log(s, AV_LOG_TRACE, "pkt dts %"PRId64" duration %d\n", pkt->dts, pkt->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
270 271 272 273 274 275 276

    return 0;
}

static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom)
{
    AVStream *st = s->streams[1];
277 278
    int av_unused tmp, tmp2;
    int samples, size;
Luca Barbato's avatar
Luca Barbato committed
279
    int64_t pos = avio_tell(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
280
    unsigned dts;
281
    int ret;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
282

283
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
284

285
    st->codec->sample_rate = avio_rb32(s->pb);
286 287
    if (st->codec->sample_rate <= 0) {
        av_log(s, AV_LOG_ERROR, "Bad sample rate\n");
288 289
        return AVERROR_INVALIDDATA;
    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
290

291
    samples = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
292

293
    tmp = avio_rb32(s->pb);
294
    av_log(s, AV_LOG_TRACE, "packet num %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
295

296
    tmp = avio_rb16(s->pb); // unknown
297
    av_log(s, AV_LOG_TRACE, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
298

299 300
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
301
    av_log(s, AV_LOG_TRACE, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
302

303
    tmp = avio_rb32(s->pb); // unknown
304
    av_log(s, AV_LOG_TRACE, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
305

306
    size = atom->size - 8 - (avio_tell(s->pb) - pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
307 308
    if (size < 0)
        return -1;
309 310 311 312
    ret = av_get_packet(s->pb, pkt, size);
    if (ret < 0) {
        av_log(s, AV_LOG_ERROR, "error reading audio packet\n");
        return ret;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
313 314 315 316
    }

    pkt->stream_index = 1;
    pkt->dts = dts;
317 318
    if (st->codec->sample_rate)
        pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
319
    av_log(s, AV_LOG_TRACE, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
            pkt->dts, pkt->duration, samples, st->codec->sample_rate);

    return 0;
}

static int r3d_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    Atom atom;
    int err = 0;

    while (!err) {
        if (read_atom(s, &atom) < 0) {
            err = -1;
            break;
        }
        switch (atom.tag) {
        case MKTAG('R','E','D','V'):
            if (s->streams[0]->discard == AVDISCARD_ALL)
                goto skip;
            if (!(err = r3d_read_redv(s, pkt, &atom)))
                return 0;
            break;
        case MKTAG('R','E','D','A'):
            if (s->nb_streams < 2)
                return -1;
            if (s->streams[1]->discard == AVDISCARD_ALL)
                goto skip;
            if (!(err = r3d_read_reda(s, pkt, &atom)))
                return 0;
            break;
        default:
        skip:
352
            avio_skip(s->pb, atom.size-8);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
        }
    }
    return err;
}

static int r3d_probe(AVProbeData *p)
{
    if (AV_RL32(p->buf + 4) == MKTAG('R','E','D','1'))
        return AVPROBE_SCORE_MAX;
    return 0;
}

static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
{
    AVStream *st = s->streams[0]; // video stream
    R3DContext *r3d = s->priv_data;
    int frame_num;

371
    if (!st->avg_frame_rate.num)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
372 373
        return -1;

374
    frame_num = av_rescale_q(sample_time, st->time_base,
375
                             av_inv_q(st->avg_frame_rate));
376
    av_log(s, AV_LOG_TRACE, "seek frame num %d timestamp %"PRId64"\n",
377
            frame_num, sample_time);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
378 379

    if (frame_num < r3d->video_offsets_count) {
380 381
        if (avio_seek(s->pb, r3d->video_offsets_count, SEEK_SET) < 0)
            return -1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
    } else {
        av_log(s, AV_LOG_ERROR, "could not seek to frame %d\n", frame_num);
        return -1;
    }

    return 0;
}

static int r3d_close(AVFormatContext *s)
{
    R3DContext *r3d = s->priv_data;

    av_freep(&r3d->video_offsets);

    return 0;
}

399
AVInputFormat ff_r3d_demuxer = {
400
    .name           = "r3d",
401
    .long_name      = NULL_IF_CONFIG_SMALL("REDCODE R3D"),
402 403 404 405 406 407
    .priv_data_size = sizeof(R3DContext),
    .read_probe     = r3d_probe,
    .read_header    = r3d_read_header,
    .read_packet    = r3d_read_packet,
    .read_close     = r3d_close,
    .read_seek      = r3d_seek,
Baptiste Coudurier's avatar
Baptiste Coudurier committed
408
};