r3d.c 10.8 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 23 24
/*
 * 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
 */

//#define DEBUG

#include "libavutil/intreadwrite.h"
25
#include "libavutil/dict.h"
26
#include "libavutil/mathematics.h"
Baptiste Coudurier's avatar
Baptiste Coudurier committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include "avformat.h"

typedef struct {
    unsigned video_offsets_count;
    unsigned *video_offsets;
    unsigned rdvo_offset;
} R3DContext;

typedef struct {
    unsigned size;
    uint32_t tag;
    uint64_t offset;
} Atom;

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

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

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

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

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

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

75
    tmp = avio_rb32(s->pb); // filenum
76
    av_dlog(s, "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_dlog(s, "unknown2 %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
85

86 87
    st->codec->time_base.den = avio_rb16(s->pb);
    st->codec->time_base.num = avio_rb16(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
88

89
    tmp = avio_r8(s->pb); // audio channels
90
    av_dlog(s, "audio channels %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
91 92
    if (tmp > 0) {
        AVStream *ast = av_new_stream(s, 1);
93 94
        if (!ast)
            return AVERROR(ENOMEM);
95
        ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
96 97 98 99 100
        ast->codec->codec_id = CODEC_ID_PCM_S32BE;
        ast->codec->channels = tmp;
        av_set_pts_info(ast, 32, 1, st->time_base.den);
    }

101
    avio_read(s->pb, filename, 257);
102
    filename[sizeof(filename)-1] = 0;
103
    av_dict_set(&st->metadata, "filename", filename, 0);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
104

105 106 107 108
    av_dlog(s, "filename %s\n", filename);
    av_dlog(s, "resolution %dx%d\n", st->codec->width, st->codec->height);
    av_dlog(s, "timescale %d\n", st->time_base.den);
    av_dlog(s, "frame rate %d/%d\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
            st->codec->time_base.num, st->codec->time_base.den);

    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++) {
126
        r3d->video_offsets[i] = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
127 128 129 130
        if (!r3d->video_offsets[i]) {
            r3d->video_offsets_count = i;
            break;
        }
131
        av_dlog(s, "video offset %d: %#x\n", i, r3d->video_offsets[i]);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
132 133 134 135 136
    }

    if (st->codec->time_base.den)
        st->duration = (uint64_t)r3d->video_offsets_count*
            st->time_base.den*st->codec->time_base.num/st->codec->time_base.den;
137
    av_dlog(s, "duration %"PRId64"\n", st->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
138 139 140 141 142 143 144

    return 0;
}

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

147 148 149 150
    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
151

152
    tmp = avio_rb32(s->pb);
153
    av_dlog(s, "num video chunks %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
154

155
    tmp = avio_rb32(s->pb);
156
    av_dlog(s, "num audio chunks %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
157

158
    avio_skip(s->pb, 6*4);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
}

static int r3d_read_header(AVFormatContext *s, AVFormatParameters *ap)
{
    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;
    }

181
    s->data_offset = avio_tell(s->pb);
182
    av_dlog(s, "data offset %#"PRIx64"\n", s->data_offset);
183
    if (!s->pb->seekable)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
184 185
        return 0;
    // find REOB/REOF/REOS to load index
186
    avio_seek(s->pb, avio_size(s->pb)-48-8, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
187 188 189 190 191 192 193 194 195 196 197
    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) {
198
        avio_seek(s->pb, r3d->rdvo_offset, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
199 200 201 202 203 204 205 206 207
        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:
208
    avio_seek(s->pb, s->data_offset, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
209 210 211 212 213 214
    return 0;
}

static int r3d_read_redv(AVFormatContext *s, AVPacket *pkt, Atom *atom)
{
    AVStream *st = s->streams[0];
215 216
    int tmp;
    int av_unused tmp2;
217
    uint64_t pos = avio_tell(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
218
    unsigned dts;
219
    int ret;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
220

221
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
222

223
    tmp = avio_rb32(s->pb);
224
    av_dlog(s, "frame num %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
225

226 227
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
228
    av_dlog(s, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
229

230
    tmp = avio_rb16(s->pb); // unknown
231
    av_dlog(s, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
232 233

    if (tmp > 4) {
234
        tmp = avio_rb16(s->pb); // unknown
235
        av_dlog(s, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
236

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

240
        tmp = avio_rb32(s->pb);
241
        av_dlog(s, "width %d\n", tmp);
242
        tmp = avio_rb32(s->pb);
243
        av_dlog(s, "height %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
244

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

    pkt->stream_index = 0;
    pkt->dts = dts;
    if (st->codec->time_base.den)
        pkt->duration = (uint64_t)st->time_base.den*
            st->codec->time_base.num/st->codec->time_base.den;
262
    av_dlog(s, "pkt dts %"PRId64" duration %d\n", pkt->dts, pkt->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
263 264 265 266 267 268 269

    return 0;
}

static int r3d_read_reda(AVFormatContext *s, AVPacket *pkt, Atom *atom)
{
    AVStream *st = s->streams[1];
270 271
    int av_unused tmp, tmp2;
    int samples, size;
272
    uint64_t pos = avio_tell(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
273
    unsigned dts;
274
    int ret;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
275

276
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
277

278
    st->codec->sample_rate = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
279

280
    samples = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
281

282
    tmp = avio_rb32(s->pb);
283
    av_dlog(s, "packet num %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
284

285
    tmp = avio_rb16(s->pb); // unkown
286
    av_dlog(s, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
287

288 289
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
290
    av_dlog(s, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
291

292
    tmp = avio_rb32(s->pb); // unknown
293
    av_dlog(s, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
294

295
    size = atom->size - 8 - (avio_tell(s->pb) - pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
296 297
    if (size < 0)
        return -1;
298 299 300 301
    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
302 303 304 305 306
    }

    pkt->stream_index = 1;
    pkt->dts = dts;
    pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
307
    av_dlog(s, "pkt dts %"PRId64" duration %d samples %d sample rate %d\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
            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:
340
            avio_skip(s->pb, atom.size-8);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
        }
    }
    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;

    if (!st->codec->time_base.num || !st->time_base.den)
        return -1;

    frame_num = sample_time*st->codec->time_base.den/
        ((int64_t)st->codec->time_base.num*st->time_base.den);
364 365
    av_dlog(s, "seek frame num %d timestamp %"PRId64"\n",
            frame_num, sample_time);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
366 367

    if (frame_num < r3d->video_offsets_count) {
368
        avio_seek(s->pb, r3d->video_offsets_count, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    } 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;
}

386
AVInputFormat ff_r3d_demuxer = {
387 388 389 390 391 392 393 394
    .name           = "r3d",
    .long_name      = NULL_IF_CONFIG_SMALL("REDCODE R3D format"),
    .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
395
};