movenc.c 52 KB
Newer Older
1
/*
2
 * MOV, 3GP, MP4 muxer
3
 * Copyright (c) 2003 Thomas Raivio.
4
 * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>.
5
 *
6 7 8
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
9 10
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14 15 16 17 18
 * 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
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 22
 */
#include "avformat.h"
23
#include "riff.h"
24
#include "avio.h"
25
#include "isom.h"
26
#include "avc.h"
27

28 29 30
#undef NDEBUG
#include <assert.h>

31 32 33
#define MOV_INDEX_CLUSTER_SIZE 16384
#define globalTimescale 1000

34 35 36
#define MODE_MP4 0
#define MODE_MOV 1
#define MODE_3GP 2
37
#define MODE_PSP 3 // example working PSP command line:
38
// ffmpeg -i testinput.avi  -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
39
#define MODE_3G2 4
40

41
typedef struct MOVIentry {
42 43
    unsigned int flags, size;
    uint64_t     pos;
44
    unsigned int samplesInChunk;
45
    char         key_frame;
46
    unsigned int entries;
47
    int64_t      cts;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
48
    int64_t      dts;
49 50 51
} MOVIentry;

typedef struct MOVIndex {
52
    int         mode;
53 54 55
    int         entry;
    long        timescale;
    long        time;
56
    int64_t     trackDuration;
57
    long        sampleCount;
58
    long        sampleSize;
59
    int         hasKeyframes;
60
    int         hasBframes;
61
    int         language;
62
    int         trackID;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
63
    int         tag; ///< stsd fourcc
64 65 66
    AVCodecContext *enc;

    int         vosLen;
67
    uint8_t     *vosData;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
68
    MOVIentry   *cluster;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
69
    int         audio_vbr;
70 71
} MOVTrack;

72
typedef struct MOVContext {
73
    int     mode;
74
    int64_t time;
75
    int     nb_streams;
76
    offset_t mdat_pos;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
77
    uint64_t mdat_size;
78 79 80 81
    long    timescale;
    MOVTrack tracks[MAX_STREAMS];
} MOVContext;

Diego Biurrun's avatar
Diego Biurrun committed
82
//FIXME support 64 bit variant with wide placeholders
83
static offset_t updateSize (ByteIOContext *pb, offset_t pos)
84
{
85
    offset_t curpos = url_ftell(pb);
86
    url_fseek(pb, pos, SEEK_SET);
87
    put_be32(pb, curpos - pos); /* rewrite size */
88
    url_fseek(pb, curpos, SEEK_SET);
89 90

    return curpos - pos;
91 92
}

93
/* Chunk offset atom */
94
static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track)
95 96
{
    int i;
Benjamin Larsson's avatar
Benjamin Larsson committed
97
    int mode64 = 0; //   use 32 bit size variant if possible
98
    offset_t pos = url_ftell(pb);
99
    put_be32(pb, 0); /* size */
100 101 102 103 104
    if (pos > UINT32_MAX) {
        mode64 = 1;
        put_tag(pb, "co64");
    } else
        put_tag(pb, "stco");
105 106 107
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, track->entry); /* entry count */
    for (i=0; i<track->entry; i++) {
108
        if(mode64 == 1)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
109
            put_be64(pb, track->cluster[i].pos);
110
        else
Baptiste Coudurier's avatar
Baptiste Coudurier committed
111
            put_be32(pb, track->cluster[i].pos);
112
    }
113
    return updateSize (pb, pos);
114 115
}

116
/* Sample size atom */
117
static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track)
118
{
119
    int equalChunks = 1;
120
    int i, j, entries = 0, tst = -1, oldtst = -1;
121

122
    offset_t pos = url_ftell(pb);
123
    put_be32(pb, 0); /* size */
124 125 126
    put_tag(pb, "stsz");
    put_be32(pb, 0); /* version & flags */

127
    for (i=0; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
128
        tst = track->cluster[i].size/track->cluster[i].entries;
129 130
        if(oldtst != -1 && tst != oldtst) {
            equalChunks = 0;
131 132
        }
        oldtst = tst;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
133
        entries += track->cluster[i].entries;
134
    }
135
    if (equalChunks) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
136
        int sSize = track->cluster[0].size/track->cluster[0].entries;
137
        put_be32(pb, sSize); // sample size
138
        put_be32(pb, entries); // sample count
139
    }
140
    else {
141 142
        put_be32(pb, 0); // sample size
        put_be32(pb, entries); // sample count
143
        for (i=0; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
144 145 146
            for ( j=0; j<track->cluster[i].entries; j++) {
                put_be32(pb, track->cluster[i].size /
                         track->cluster[i].entries);
147
            }
148 149
        }
    }
150
    return updateSize (pb, pos);
151 152
}

153
/* Sample to chunk atom */
154
static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track)
155
{
156 157
    int index = 0, oldval = -1, i;
    offset_t entryPos, curpos;
158

159
    offset_t pos = url_ftell(pb);
160
    put_be32(pb, 0); /* size */
161
    put_tag(pb, "stsc");
162
    put_be32(pb, 0); // version & flags
163
    entryPos = url_ftell(pb);
164
    put_be32(pb, track->entry); // entry count
165
    for (i=0; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
166
        if(oldval != track->cluster[i].samplesInChunk)
167
        {
168
            put_be32(pb, i+1); // first chunk
Baptiste Coudurier's avatar
Baptiste Coudurier committed
169
            put_be32(pb, track->cluster[i].samplesInChunk); // samples per chunk
170
            put_be32(pb, 0x1); // sample description index
Baptiste Coudurier's avatar
Baptiste Coudurier committed
171
            oldval = track->cluster[i].samplesInChunk;
172
            index++;
173 174
        }
    }
175 176
    curpos = url_ftell(pb);
    url_fseek(pb, entryPos, SEEK_SET);
177
    put_be32(pb, index); // rewrite size
178
    url_fseek(pb, curpos, SEEK_SET);
179

180
    return updateSize (pb, pos);
181 182
}

183
/* Sync sample atom */
184
static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track)
185
{
186 187 188
    offset_t curpos, entryPos;
    int i, index = 0;
    offset_t pos = url_ftell(pb);
189
    put_be32(pb, 0); // size
190
    put_tag(pb, "stss");
191
    put_be32(pb, 0); // version & flags
192
    entryPos = url_ftell(pb);
193
    put_be32(pb, track->entry); // entry count
194
    for (i=0; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
195
        if(track->cluster[i].key_frame == 1) {
196 197 198 199 200 201
            put_be32(pb, i+1);
            index++;
        }
    }
    curpos = url_ftell(pb);
    url_fseek(pb, entryPos, SEEK_SET);
202
    put_be32(pb, index); // rewrite size
203 204
    url_fseek(pb, curpos, SEEK_SET);
    return updateSize (pb, pos);
205 206
}

207
static int mov_write_amr_tag(ByteIOContext *pb, MOVTrack *track)
208 209
{
    put_be32(pb, 0x11); /* size */
210 211
    if (track->mode == MODE_MOV) put_tag(pb, "samr");
    else                         put_tag(pb, "damr");
212
    put_tag(pb, "FFMP");
213
    put_byte(pb, 0); /* decoder version */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
214

Baptiste Coudurier's avatar
Baptiste Coudurier committed
215
    put_be16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */
216 217
    put_byte(pb, 0x00); /* Mode change period (no restriction) */
    put_byte(pb, 0x01); /* Frames per sample */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
218 219 220
    return 0x11;
}

221 222 223 224 225 226 227 228
static int mov_write_enda_tag(ByteIOContext *pb)
{
    put_be32(pb, 10);
    put_tag(pb, "enda");
    put_be16(pb, 1); /* little endian */
    return 10;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
229 230
static unsigned int descrLength(unsigned int len)
{
Michael Niedermayer's avatar
Michael Niedermayer committed
231 232 233
    int i;
    for(i=1; len>>(7*i); i++);
    return len + 1 + i;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
234 235
}

Michael Niedermayer's avatar
Michael Niedermayer committed
236
static void putDescr(ByteIOContext *pb, int tag, unsigned int size)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
237
{
Michael Niedermayer's avatar
Michael Niedermayer committed
238 239 240 241 242
    int i= descrLength(size) - size - 2;
    put_byte(pb, tag);
    for(; i>0; i--)
        put_byte(pb, (size>>(7*i)) | 0x80);
    put_byte(pb, size & 0x7F);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
243 244 245 246 247
}

static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
{
    offset_t pos = url_ftell(pb);
248
    int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

    put_be32(pb, 0);               // size
    put_tag(pb, "esds");
    put_be32(pb, 0);               // Version

    // ES descriptor
    putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
             descrLength(1));
    put_be16(pb, track->trackID);
    put_byte(pb, 0x00);            // flags (= no flags)

    // DecoderConfig descriptor
    putDescr(pb, 0x04, 13 + decoderSpecificInfoLen);

    // Object type indication
Baptiste Coudurier's avatar
Baptiste Coudurier committed
264
    put_byte(pb, codec_get_tag(ff_mp4_obj_type, track->enc->codec_id));
Baptiste Coudurier's avatar
Baptiste Coudurier committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295

    // the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio)
    // plus 1 bit to indicate upstream and 1 bit set to 1 (reserved)
    if(track->enc->codec_type == CODEC_TYPE_AUDIO)
        put_byte(pb, 0x15);            // flags (= Audiostream)
    else
        put_byte(pb, 0x11);            // flags (= Visualstream)

    put_byte(pb,  track->enc->rc_buffer_size>>(3+16));             // Buffersize DB (24 bits)
    put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF);          // Buffersize DB

    put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate));     // maxbitrate  (FIXME should be max rate in any 1 sec window)
    if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0)
        put_be32(pb, 0);     // vbr
    else
        put_be32(pb, track->enc->rc_max_rate);     // avg bitrate

    if (track->vosLen)
    {
        // DecoderSpecific info descriptor
        putDescr(pb, 0x05, track->vosLen);
        put_buffer(pb, track->vosData, track->vosLen);
    }


    // SL descriptor
    putDescr(pb, 0x06, 1);
    put_byte(pb, 0x02);
    return updateSize (pb, pos);
}

296 297
static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track)
{
298
    offset_t pos = url_ftell(pb);
299 300 301 302 303 304

    put_be32(pb, 0);     /* size */
    put_tag(pb, "wave");

    put_be32(pb, 12);    /* size */
    put_tag(pb, "frma");
305
    put_le32(pb, track->tag);
306

307
    if (track->enc->codec_id == CODEC_ID_AAC) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
308 309 310 311
        /* useless atom needed by mplayer, ipod, not needed by quicktime */
        put_be32(pb, 12); /* size */
        put_tag(pb, "mp4a");
        put_be32(pb, 0);
312 313 314 315
        mov_write_esds_tag(pb, track);
    } else if (track->enc->codec_id == CODEC_ID_PCM_S24LE ||
               track->enc->codec_id == CODEC_ID_PCM_S32LE) {
        mov_write_enda_tag(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
316
    } else if (track->enc->codec_id == CODEC_ID_AMR_NB) {
317
        mov_write_amr_tag(pb, track);
318
    }
319 320 321 322 323 324 325

    put_be32(pb, 8);     /* size */
    put_be32(pb, 0);     /* null tag */

    return updateSize (pb, pos);
}

326 327 328 329 330 331 332 333
static int mov_write_glbl_tag(ByteIOContext *pb, MOVTrack* track)
{
    put_be32(pb, track->vosLen+8);
    put_tag(pb, "glbl");
    put_buffer(pb, track->vosData, track->vosLen);
    return 8+track->vosLen;
}

334
static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
335
{
336
    offset_t pos = url_ftell(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
337 338 339 340
    int version = track->mode == MODE_MOV &&
        (track->audio_vbr ||
         track->enc->codec_id == CODEC_ID_PCM_S32LE ||
         track->enc->codec_id == CODEC_ID_PCM_S24LE);
341

342
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
343
    put_le32(pb, track->tag); // store it byteswapped
344 345 346
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index, XXX  == 1 */
347

348
    /* SoundDescription */
349
    put_be16(pb, version); /* Version */
350
    put_be16(pb, 0); /* Revision level */
351 352
    put_be32(pb, 0); /* Reserved */

353 354
    if (track->mode == MODE_MOV) {
        put_be16(pb, track->enc->channels);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
355 356 357 358 359
        if (track->enc->codec_id == CODEC_ID_PCM_U8 ||
            track->enc->codec_id == CODEC_ID_PCM_S8)
            put_be16(pb, 8); /* bits per sample */
        else
            put_be16(pb, 16);
360 361 362 363 364 365
        put_be16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
    } else { /* reserved for mp4/3gp */
        put_be16(pb, 2);
        put_be16(pb, 16);
        put_be16(pb, 0);
    }
366

367
    put_be16(pb, 0); /* packet size (= 0) */
368 369 370
    put_be16(pb, track->timescale); /* Time scale */
    put_be16(pb, 0); /* Reserved */

371 372
    if(version == 1) { /* SoundDescription V1 extended info */
        put_be32(pb, track->enc->frame_size); /* Samples per packet */
373
        put_be32(pb, track->sampleSize / track->enc->channels); /* Bytes per packet */
374
        put_be32(pb, track->sampleSize); /* Bytes per frame */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
375
        put_be32(pb, 2); /* Bytes per sample */
376 377
    }

378 379 380 381 382
    if(track->mode == MODE_MOV &&
       (track->enc->codec_id == CODEC_ID_AAC ||
        track->enc->codec_id == CODEC_ID_AMR_NB ||
        track->enc->codec_id == CODEC_ID_PCM_S24LE ||
        track->enc->codec_id == CODEC_ID_PCM_S32LE))
383
        mov_write_wave_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
384 385 386
    else if(track->enc->codec_id == CODEC_ID_AAC)
        mov_write_esds_tag(pb, track);
    else if(track->enc->codec_id == CODEC_ID_AMR_NB)
387
        mov_write_amr_tag(pb, track);
388 389
    else if(track->vosLen > 0)
        mov_write_glbl_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
390

391
    return updateSize (pb, pos);
392 393
}

394
static int mov_write_d263_tag(ByteIOContext *pb)
395 396 397 398
{
    put_be32(pb, 0xf); /* size */
    put_tag(pb, "d263");
    put_tag(pb, "FFMP");
399 400 401 402
    put_byte(pb, 0); /* decoder version */
    /* FIXME use AVCodecContext level/profile, when encoder will set values */
    put_byte(pb, 0xa); /* level */
    put_byte(pb, 0); /* profile */
403 404 405
    return 0xf;
}

406 407
/* TODO: No idea about these values */
static int mov_write_svq3_tag(ByteIOContext *pb)
408
{
409 410 411 412 413 414
    put_be32(pb, 0x15);
    put_tag(pb, "SMI ");
    put_tag(pb, "SEQH");
    put_be32(pb, 0x5);
    put_be32(pb, 0xe2c0211d);
    put_be32(pb, 0xc0000000);
415
    put_byte(pb, 0);
416
    return 0x15;
417 418
}

419 420 421 422 423 424
static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
{
    offset_t pos = url_ftell(pb);

    put_be32(pb, 0);
    put_tag(pb, "avcC");
425
    ff_isom_write_avcc(pb, track->vosData, track->vosLen);
426 427 428
    return updateSize(pb, pos);
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/* also used by all avid codecs (dv, imx, meridien) and their variants */
static int mov_write_avid_tag(ByteIOContext *pb, MOVTrack *track)
{
    int i;
    put_be32(pb, 24); /* size */
    put_tag(pb, "ACLR");
    put_tag(pb, "ACLR");
    put_tag(pb, "0001");
    put_be32(pb, 1); /* yuv 1 / rgb 2 ? */
    put_be32(pb, 0); /* unknown */

    put_be32(pb, 24); /* size */
    put_tag(pb, "APRG");
    put_tag(pb, "APRG");
    put_tag(pb, "0001");
    put_be32(pb, 1); /* unknown */
    put_be32(pb, 0); /* unknown */

    put_be32(pb, 120); /* size */
    put_tag(pb, "ARES");
    put_tag(pb, "ARES");
    put_tag(pb, "0001");
    put_be32(pb, AV_RB32(track->vosData + 0x28)); /* dnxhd cid, some id ? */
    put_be32(pb, track->enc->width);
    /* values below are based on samples created with quicktime and avid codecs */
    if (track->vosData[5] & 2) { // interlaced
        put_be32(pb, track->enc->height/2);
        put_be32(pb, 2); /* unknown */
        put_be32(pb, 0); /* unknown */
        put_be32(pb, 4); /* unknown */
    } else {
        put_be32(pb, track->enc->height);
        put_be32(pb, 1); /* unknown */
        put_be32(pb, 0); /* unknown */
463 464 465 466
        if (track->enc->height == 1080)
            put_be32(pb, 5); /* unknown */
        else
            put_be32(pb, 6); /* unknown */
467 468 469 470 471 472 473 474 475 476
    }
    /* padding */
    for (i = 0; i < 10; i++)
        put_be64(pb, 0);

    /* extra padding for stsd needed */
    put_be32(pb, 0);
    return 0;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
477
static int mov_find_video_codec_tag(AVFormatContext *s, MOVTrack *track)
478
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
479
    int tag = track->enc->codec_tag;
480 481 482 483 484 485 486 487 488 489 490 491
    if (!tag) {
        if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
            if (track->enc->height == 480) { /* NTSC */
                if (track->enc->pix_fmt == PIX_FMT_YUV422P)
                    tag = MKTAG('d', 'v', '5', 'n');
                else
                    tag = MKTAG('d', 'v', 'c', ' ');
            } else { /* assume PAL */
                if (track->enc->pix_fmt == PIX_FMT_YUV422P)
                    tag = MKTAG('d', 'v', '5', 'p');
                else if (track->enc->pix_fmt == PIX_FMT_YUV420P)
                    tag = MKTAG('d', 'v', 'c', 'p');
Baptiste Coudurier's avatar
Baptiste Coudurier committed
492 493
                else
                    tag = MKTAG('d', 'v', 'p', 'p');
494
            }
495 496 497 498 499
        } else if (track->enc->codec_id == CODEC_ID_H263) {
            if (track->mode == MODE_MOV)
                tag = MKTAG('h', '2', '6', '3');
            else
                tag = MKTAG('s', '2', '6', '3');
500 501 502 503 504
        } else {
            tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
        }
    }
    // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
505
    if (!tag) {
506
        tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
        if (tag) {
            av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n");
        }
    }
    assert(tag);
    return tag;
}

static int mov_find_audio_codec_tag(AVFormatContext *s, MOVTrack *track)
{
    int tag = track->enc->codec_tag;
    if (!tag) {
        tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
    }
    // if no mac fcc found, try with Microsoft tags
    if (!tag) {
        int ms_tag = codec_get_tag(codec_wav_tags, track->enc->codec_id);
        if (ms_tag) {
            tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff), (ms_tag & 0xff));
            av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n");
        }
    }
529 530 531 532
    assert(tag);
    return tag;
}

533
static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
534
{
535
    offset_t pos = url_ftell(pb);
536
    char compressor_name[32];
537

538
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
539
    put_le32(pb, track->tag); // store it byteswapped
540 541 542 543
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index */

544 545
    put_be16(pb, 0); /* Codec stream version */
    put_be16(pb, 0); /* Codec stream revision (=0) */
546
    if (track->mode == MODE_MOV) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
547 548 549 550 551 552 553 554
        put_tag(pb, "FFMP"); /* Vendor */
        if(track->enc->codec_id == CODEC_ID_RAWVIDEO) {
            put_be32(pb, 0); /* Temporal Quality */
            put_be32(pb, 0x400); /* Spatial Quality = lossless*/
        } else {
            put_be32(pb, 0x200); /* Temporal Quality = normal */
            put_be32(pb, 0x200); /* Spatial Quality = normal */
        }
555 556 557 558 559
    } else {
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
    }
560 561
    put_be16(pb, track->enc->width); /* Video width */
    put_be16(pb, track->enc->height); /* Video height */
562 563
    put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */
    put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */
564 565
    put_be32(pb, 0); /* Data size (= 0) */
    put_be16(pb, 1); /* Frame count (= 1) */
566

567
    memset(compressor_name,0,32);
568 569
    /* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */
    if (track->mode == MODE_MOV && track->enc->codec && track->enc->codec->name)
570
        strncpy(compressor_name,track->enc->codec->name,31);
571
    put_byte(pb, strlen(compressor_name));
572
    put_buffer(pb, compressor_name, 31);
573

574 575 576 577 578 579 580
    put_be16(pb, 0x18); /* Reserved */
    put_be16(pb, 0xffff); /* Reserved */
    if(track->enc->codec_id == CODEC_ID_MPEG4)
        mov_write_esds_tag(pb, track);
    else if(track->enc->codec_id == CODEC_ID_H263)
        mov_write_d263_tag(pb);
    else if(track->enc->codec_id == CODEC_ID_SVQ3)
581
        mov_write_svq3_tag(pb);
582 583
    else if(track->enc->codec_id == CODEC_ID_H264)
        mov_write_avcc_tag(pb, track);
584 585
    else if(track->enc->codec_id == CODEC_ID_DNXHD)
        mov_write_avid_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
586
    else if(track->vosLen > 0)
587
        mov_write_glbl_tag(pb, track);
588 589

    return updateSize (pb, pos);
590 591
}

592
static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
593
{
594
    offset_t pos = url_ftell(pb);
595 596 597 598
    put_be32(pb, 0); /* size */
    put_tag(pb, "stsd");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */
599 600 601 602
    if (track->enc->codec_type == CODEC_TYPE_VIDEO)
        mov_write_video_tag(pb, track);
    else if (track->enc->codec_type == CODEC_TYPE_AUDIO)
        mov_write_audio_tag(pb, track);
603
    return updateSize(pb, pos);
604 605
}

606 607
static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track)
{
608
    MOV_stts_t *ctts_entries;
609 610 611 612 613 614
    uint32_t entries = 0;
    uint32_t atom_size;
    int i;

    ctts_entries = av_malloc((track->entry + 1) * sizeof(*ctts_entries)); /* worst case */
    ctts_entries[0].count = 1;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
615
    ctts_entries[0].duration = track->cluster[0].cts;
616
    for (i=1; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
617
        if (track->cluster[i].cts == ctts_entries[entries].duration) {
618 619 620
            ctts_entries[entries].count++; /* compress */
        } else {
            entries++;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
621
            ctts_entries[entries].duration = track->cluster[i].cts;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
            ctts_entries[entries].count = 1;
        }
    }
    entries++; /* last one */
    atom_size = 16 + (entries * 8);
    put_be32(pb, atom_size); /* size */
    put_tag(pb, "ctts");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, entries); /* entry count */
    for (i=0; i<entries; i++) {
        put_be32(pb, ctts_entries[i].count);
        put_be32(pb, ctts_entries[i].duration);
    }
    av_free(ctts_entries);
    return atom_size;
}

639
/* Time to sample atom */
640
static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
641
{
642
    MOV_stts_t *stts_entries;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
    uint32_t entries = -1;
    uint32_t atom_size;
    int i;

    if (track->enc->codec_type == CODEC_TYPE_AUDIO && !track->audio_vbr) {
        stts_entries = av_malloc(sizeof(*stts_entries)); /* one entry */
        stts_entries[0].count = track->sampleCount;
        stts_entries[0].duration = 1;
        entries = 1;
    } else {
        stts_entries = av_malloc(track->entry * sizeof(*stts_entries)); /* worst case */
        for (i=0; i<track->entry; i++) {
            int64_t duration = i + 1 == track->entry ?
                track->trackDuration - track->cluster[i].dts + track->cluster[0].dts : /* readjusting */
                track->cluster[i+1].dts - track->cluster[i].dts;
            if (i && duration == stts_entries[entries].duration) {
                stts_entries[entries].count++; /* compress */
            } else {
                entries++;
                stts_entries[entries].duration = duration;
                stts_entries[entries].count = 1;
            }
        }
        entries++; /* last one */
    }
    atom_size = 16 + (entries * 8);
    put_be32(pb, atom_size); /* size */
670 671
    put_tag(pb, "stts");
    put_be32(pb, 0); /* version & flags */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
672 673 674 675 676 677 678
    put_be32(pb, entries); /* entry count */
    for (i=0; i<entries; i++) {
        put_be32(pb, stts_entries[i].count);
        put_be32(pb, stts_entries[i].duration);
    }
    av_free(stts_entries);
    return atom_size;
679 680
}

681
static int mov_write_dref_tag(ByteIOContext *pb)
682 683 684 685 686 687 688 689 690 691 692 693 694
{
    put_be32(pb, 28); /* size */
    put_tag(pb, "dref");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */

    put_be32(pb, 0xc); /* size */
    put_tag(pb, "url ");
    put_be32(pb, 1); /* version & flags */

    return 28;
}

695
static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
696
{
697
    offset_t pos = url_ftell(pb);
698 699
    put_be32(pb, 0); /* size */
    put_tag(pb, "stbl");
700 701
    mov_write_stsd_tag(pb, track);
    mov_write_stts_tag(pb, track);
702
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
703
        track->hasKeyframes < track->entry)
704
        mov_write_stss_tag(pb, track);
705 706 707
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
        track->hasBframes)
        mov_write_ctts_tag(pb, track);
708 709 710 711
    mov_write_stsc_tag(pb, track);
    mov_write_stsz_tag(pb, track);
    mov_write_stco_tag(pb, track);
    return updateSize(pb, pos);
712 713
}

714
static int mov_write_dinf_tag(ByteIOContext *pb)
715
{
716
    offset_t pos = url_ftell(pb);
717 718
    put_be32(pb, 0); /* size */
    put_tag(pb, "dinf");
719 720
    mov_write_dref_tag(pb);
    return updateSize(pb, pos);
721 722
}

723
static int mov_write_smhd_tag(ByteIOContext *pb)
724 725 726 727 728 729 730 731 732
{
    put_be32(pb, 16); /* size */
    put_tag(pb, "smhd");
    put_be32(pb, 0); /* version & flags */
    put_be16(pb, 0); /* reserved (balance, normally = 0) */
    put_be16(pb, 0); /* reserved */
    return 16;
}

733
static int mov_write_vmhd_tag(ByteIOContext *pb)
734 735 736 737 738 739 740 741
{
    put_be32(pb, 0x14); /* size (always 0x14) */
    put_tag(pb, "vmhd");
    put_be32(pb, 0x01); /* version & flags */
    put_be64(pb, 0); /* reserved (graphics mode = copy) */
    return 0x14;
}

742
static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
743
{
744
    const char *descr, *hdlr, *hdlr_type;
745
    offset_t pos = url_ftell(pb);
746

747
    if (!track) { /* no media --> data handler */
748 749 750
        hdlr = "dhlr";
        hdlr_type = "url ";
        descr = "DataHandler";
751
    } else {
752 753 754 755 756 757 758 759
        hdlr = (track->mode == MODE_MOV) ? "mhlr" : "\0\0\0\0";
        if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
            hdlr_type = "vide";
            descr = "VideoHandler";
        } else {
            hdlr_type = "soun";
            descr = "SoundHandler";
        }
760
    }
761

762
    put_be32(pb, 0); /* size */
763 764
    put_tag(pb, "hdlr");
    put_be32(pb, 0); /* Version & flags */
765
    put_buffer(pb, hdlr, 4); /* handler */
766
    put_tag(pb, hdlr_type); /* handler type */
767 768 769
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
770 771 772 773 774 775 776
    put_byte(pb, strlen(descr)); /* string counter */
    put_buffer(pb, descr, strlen(descr)); /* handler description */
    return updateSize(pb, pos);
}

static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track)
{
777
    offset_t pos = url_ftell(pb);
778 779
    put_be32(pb, 0); /* size */
    put_tag(pb, "minf");
780
    if(track->enc->codec_type == CODEC_TYPE_VIDEO)
781
        mov_write_vmhd_tag(pb);
782
    else
783 784 785 786 787
        mov_write_smhd_tag(pb);
    if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */
        mov_write_hdlr_tag(pb, NULL);
    mov_write_dinf_tag(pb);
    mov_write_stbl_tag(pb, track);
788
    return updateSize(pb, pos);
789 790
}

791
static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
792
{
793 794 795
    int version = track->trackDuration < INT32_MAX ? 0 : 1;

    (version == 1) ? put_be32(pb, 44) : put_be32(pb, 32); /* size */
796
    put_tag(pb, "mdhd");
797 798 799 800 801 802 803 804 805
    put_byte(pb, version);
    put_be24(pb, 0); /* flags */
    if (version == 1) {
        put_be64(pb, track->time);
        put_be64(pb, track->time);
    } else {
        put_be32(pb, track->time); /* creation time */
        put_be32(pb, track->time); /* modification time */
    }
806
    put_be32(pb, track->timescale); /* time scale (sample rate for audio) */
807
    (version == 1) ? put_be64(pb, track->trackDuration) : put_be32(pb, track->trackDuration); /* duration */
808
    put_be16(pb, track->language); /* language */
809
    put_be16(pb, 0); /* reserved (quality) */
810 811 812 813

    if(version!=0 && track->mode == MODE_MOV){
        av_log(NULL, AV_LOG_ERROR,
            "FATAL error, file duration too long for timebase, this file will not be\n"
Aurelien Jacobs's avatar
typo  
Aurelien Jacobs committed
814
            "playable with quicktime. Choose a different timebase or a different\n"
815 816 817
            "container format\n");
    }

818 819 820
    return 32;
}

821
static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track)
822
{
823
    offset_t pos = url_ftell(pb);
824 825
    put_be32(pb, 0); /* size */
    put_tag(pb, "mdia");
826 827 828 829
    mov_write_mdhd_tag(pb, track);
    mov_write_hdlr_tag(pb, track);
    mov_write_minf_tag(pb, track);
    return updateSize(pb, pos);
830 831
}