r3d.c 10.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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/*
 * 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"
#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)
{
41
    atom->offset = avio_tell(s->pb);
42
    atom->size = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
43 44
    if (atom->size < 8)
        return -1;
45
    atom->tag = avio_rl32(s->pb);
46
    av_dlog(s, "atom %d %.4s offset %#llx\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
47 48 49 50 51 52 53
            atom->size, (char*)&atom->tag, atom->offset);
    return atom->size;
}

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

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

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

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

69
    tmp = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
70 71
    av_set_pts_info(st, 32, 1, tmp);

72
    tmp = avio_rb32(s->pb); // filenum
73
    av_dlog(s, "filenum %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
74

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

77 78
    st->codec->width  = avio_rb32(s->pb);
    st->codec->height = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
79

80
    tmp = avio_rb16(s->pb); // unknown
81
    av_dlog(s, "unknown2 %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
82

83 84
    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
85

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

98
    avio_read(s->pb, filename, 257);
99
    filename[sizeof(filename)-1] = 0;
100
    av_metadata_set2(&st->metadata, "filename", filename, 0);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
101

102 103 104 105
    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
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
            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++) {
123
        r3d->video_offsets[i] = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
124 125 126 127
        if (!r3d->video_offsets[i]) {
            r3d->video_offsets_count = i;
            break;
        }
128
        av_dlog(s, "video offset %d: %#x\n", i, r3d->video_offsets[i]);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
129 130 131 132 133
    }

    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;
134
    av_dlog(s, "duration %lld\n", st->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
135 136 137 138 139 140 141 142 143

    return 0;
}

static void r3d_read_reos(AVFormatContext *s)
{
    R3DContext *r3d = s->priv_data;
    int tmp;

144 145 146 147
    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
148

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

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

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

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;
    }

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

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

217
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
218

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

222 223
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
224
    av_dlog(s, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
225

226
    tmp = avio_rb16(s->pb); // unknown
227
    av_dlog(s, "unknown %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
228 229

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

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

236
        tmp = avio_rb32(s->pb);
237
        av_dlog(s, "width %d\n", tmp);
238
        tmp = avio_rb32(s->pb);
239
        av_dlog(s, "height %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
240

241
        tmp = avio_rb32(s->pb);
242
        av_dlog(s, "metadata len %d\n", tmp);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
243
    }
244
    tmp = atom->size - 8 - (avio_tell(s->pb) - pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
245 246
    if (tmp < 0)
        return -1;
247 248
    ret = av_get_packet(s->pb, pkt, tmp);
    if (ret < 0) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
249 250 251 252 253 254 255 256 257
        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;
258
    av_dlog(s, "pkt dts %lld duration %d\n", pkt->dts, pkt->duration);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
259 260 261 262 263 264 265 266

    return 0;
}

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

271
    dts = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
272

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

275
    samples = avio_rb32(s->pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
276

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

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

283 284
    tmp  = avio_r8(s->pb); // major version
    tmp2 = avio_r8(s->pb); // minor version
285
    av_dlog(s, "version %d.%d\n", tmp, tmp2);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
286

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

290
    size = atom->size - 8 - (avio_tell(s->pb) - pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
291 292
    if (size < 0)
        return -1;
293 294 295 296
    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
297 298 299 300 301
    }

    pkt->stream_index = 1;
    pkt->dts = dts;
    pkt->duration = av_rescale(samples, st->time_base.den, st->codec->sample_rate);
302
    av_dlog(s, "pkt dts %lld duration %d samples %d sample rate %d\n",
Baptiste Coudurier's avatar
Baptiste Coudurier committed
303 304 305 306 307 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
            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:
335
            avio_skip(s->pb, atom.size-8);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
        }
    }
    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);
359
    av_dlog(s, "seek frame num %d timestamp %lld\n", frame_num, sample_time);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
360 361

    if (frame_num < r3d->video_offsets_count) {
362
        avio_seek(s->pb, r3d->video_offsets_count, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    } 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;
}

380
AVInputFormat ff_r3d_demuxer = {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
381 382 383 384 385 386 387 388 389
    "r3d",
    NULL_IF_CONFIG_SMALL("REDCODE R3D format"),
    sizeof(R3DContext),
    r3d_probe,
    r3d_read_header,
    r3d_read_packet,
    r3d_close,
    r3d_seek,
};