movenc.c 54 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);
384
    else if(track->tag == MKTAG('m','p','4','a'))
Baptiste Coudurier's avatar
Baptiste Coudurier committed
385 386
        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;
}

477 478 479 480 481 482 483 484 485
static const AVCodecTag codec_3gp_tags[] = {
    { CODEC_ID_H263,   MKTAG('s','2','6','3') },
    { CODEC_ID_H264,   MKTAG('a','v','c','1') },
    { CODEC_ID_MPEG4,  MKTAG('m','p','4','v') },
    { CODEC_ID_AAC,    MKTAG('m','p','4','a') },
    { CODEC_ID_AMR_NB, MKTAG('s','a','m','r') },
    { CODEC_ID_AMR_WB, MKTAG('s','a','w','b') },
};

Baptiste Coudurier's avatar
Baptiste Coudurier committed
486 487
static const AVCodecTag mov_pix_fmt_tags[] = {
    { PIX_FMT_YUYV422, MKTAG('y','u','v','s') },
Baptiste Coudurier's avatar
Baptiste Coudurier committed
488
    { PIX_FMT_UYVY422, MKTAG('2','v','u','y') },
Baptiste Coudurier's avatar
Baptiste Coudurier committed
489 490 491 492 493
    { PIX_FMT_BGR555,  MKTAG('r','a','w',' ') },
    { PIX_FMT_RGB24,   MKTAG('r','a','w',' ') },
    { PIX_FMT_BGR32_1, MKTAG('r','a','w',' ') },
};

494
static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
495
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
496
    int tag = track->enc->codec_tag;
497 498 499 500 501 502 503 504
    if (track->mode == MODE_MP4 || track->mode == MODE_PSP) {
        if (!codec_get_tag(ff_mp4_obj_type, track->enc->codec_id))
            return 0;
        if (track->enc->codec_id == CODEC_ID_H264)           tag = MKTAG('a','v','c','1');
        else if (track->enc->codec_type == CODEC_TYPE_VIDEO) tag = MKTAG('m','p','4','v');
        else if (track->enc->codec_type == CODEC_TYPE_AUDIO) tag = MKTAG('m','p','4','a');
    } else if (track->mode == MODE_3GP || track->mode == MODE_3G2) {
        tag = codec_get_tag(codec_3gp_tags, track->enc->codec_id);
505
    } else if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
Baptiste Coudurier's avatar
Baptiste Coudurier committed
506 507
                        (tag == MKTAG('d','v','c','p') ||
                         track->enc->codec_id == CODEC_ID_RAWVIDEO))) {
508
        if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
509 510 511 512 513 514
            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 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');
            else                                             tag = MKTAG('d','v','p','p');
Baptiste Coudurier's avatar
Baptiste Coudurier committed
515 516 517 518
        } else if (track->enc->codec_id == CODEC_ID_RAWVIDEO) {
            tag = codec_get_tag(mov_pix_fmt_tags, track->enc->pix_fmt);
            if (!tag) // restore tag
                tag = track->enc->codec_tag;
519
        } else {
520
            if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
521
                tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
522
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
523
                    tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
524 525 526
                    if (tag)
                        av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, "
                               "the file may be unplayable!\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
527
                }
528
            } else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
529
                tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
530
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
531 532 533
                    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));
534 535
                        av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, "
                               "the file may be unplayable!\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
536 537
                    }
                }
538 539 540
            }
        }
    }
541 542 543
    return tag;
}

544
static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
545
{
546
    offset_t pos = url_ftell(pb);
547
    char compressor_name[32];
548

549
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
550
    put_le32(pb, track->tag); // store it byteswapped
551 552 553 554
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index */

555 556
    put_be16(pb, 0); /* Codec stream version */
    put_be16(pb, 0); /* Codec stream revision (=0) */
557
    if (track->mode == MODE_MOV) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
558 559 560 561 562 563 564 565
        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 */
        }
566 567 568 569 570
    } else {
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
    }
571 572
    put_be16(pb, track->enc->width); /* Video width */
    put_be16(pb, track->enc->height); /* Video height */
573 574
    put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */
    put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */
575 576
    put_be32(pb, 0); /* Data size (= 0) */
    put_be16(pb, 1); /* Frame count (= 1) */
577

578
    memset(compressor_name,0,32);
579 580
    /* 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)
581
        strncpy(compressor_name,track->enc->codec->name,31);
582
    put_byte(pb, strlen(compressor_name));
583
    put_buffer(pb, compressor_name, 31);
584

Baptiste Coudurier's avatar
Baptiste Coudurier committed
585 586 587 588
    if (track->mode == MODE_MOV && track->enc->bits_per_sample)
        put_be16(pb, track->enc->bits_per_sample);
    else
        put_be16(pb, 0x18); /* Reserved */
589
    put_be16(pb, 0xffff); /* Reserved */
590
    if(track->tag == MKTAG('m','p','4','v'))
591 592 593 594
        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)
595
        mov_write_svq3_tag(pb);
596 597
    else if(track->enc->codec_id == CODEC_ID_H264)
        mov_write_avcc_tag(pb, track);
598 599
    else if(track->enc->codec_id == CODEC_ID_DNXHD)
        mov_write_avid_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
600
    else if(track->vosLen > 0)
601
        mov_write_glbl_tag(pb, track);
602 603

    return updateSize (pb, pos);
604 605
}

606
static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
607
{
608
    offset_t pos = url_ftell(pb);
609 610 611 612
    put_be32(pb, 0); /* size */
    put_tag(pb, "stsd");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */
613 614 615 616
    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);
617
    return updateSize(pb, pos);
618 619
}

620 621
static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track)
{
622
    MOV_stts_t *ctts_entries;
623 624 625 626 627 628
    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
629
    ctts_entries[0].duration = track->cluster[0].cts;
630
    for (i=1; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
631
        if (track->cluster[i].cts == ctts_entries[entries].duration) {
632 633 634
            ctts_entries[entries].count++; /* compress */
        } else {
            entries++;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
635
            ctts_entries[entries].duration = track->cluster[i].cts;
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
            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;
}

653
/* Time to sample atom */
654
static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
655
{
656
    MOV_stts_t *stts_entries;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
    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 */
684 685
    put_tag(pb, "stts");
    put_be32(pb, 0); /* version & flags */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
686 687 688 689 690 691 692
    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;
693 694
}

695
static int mov_write_dref_tag(ByteIOContext *pb)
696 697 698 699 700 701 702 703 704 705 706 707 708
{
    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;
}

709
static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
710
{
711
    offset_t pos = url_ftell(pb);
712 713
    put_be32(pb, 0); /* size */
    put_tag(pb, "stbl");
714 715
    mov_write_stsd_tag(pb, track);
    mov_write_stts_tag(pb, track);
716
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
717
        track->hasKeyframes < track->entry)
718
        mov_write_stss_tag(pb, track);
719 720 721
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
        track->hasBframes)
        mov_write_ctts_tag(pb, track);
722 723 724 725
    mov_write_stsc_tag(pb, track);
    mov_write_stsz_tag(pb, track);
    mov_write_stco_tag(pb, track);
    return updateSize(pb, pos);
726 727
}

728
static int mov_write_dinf_tag(ByteIOContext *pb)
729
{
730
    offset_t pos = url_ftell(pb);
731 732
    put_be32(pb, 0); /* size */
    put_tag(pb, "dinf");
733 734
    mov_write_dref_tag(pb);
    return updateSize(pb, pos);
735 736
}

737
static int mov_write_smhd_tag(ByteIOContext *pb)
738 739 740 741 742 743 744 745 746
{
    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;
}

747
static int mov_write_vmhd_tag(ByteIOContext *pb)
748 749 750 751 752 753 754 755
{
    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;
}

756
static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
757
{
758
    const char *descr, *hdlr, *hdlr_type;
759
    offset_t pos = url_ftell(pb);
760

761
    if (!track) { /* no media --> data handler */
762 763 764
        hdlr = "dhlr";
        hdlr_type = "url ";
        descr = "DataHandler";
765
    } else {
766 767 768 769 770 771 772 773
        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";
        }
774
    }
775

776
    put_be32(pb, 0); /* size */
777 778
    put_tag(pb, "hdlr");
    put_be32(pb, 0); /* Version & flags */
779
    put_buffer(pb, hdlr, 4); /* handler */
780
    put_tag(pb, hdlr_type); /* handler type */
781 782 783
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
784 785 786 787 788 789 790
    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)
{
791
    offset_t pos = url_ftell(pb);
792 793
    put_be32(pb, 0); /* size */
    put_tag(pb, "minf");
794
    if(track->enc->codec_type == CODEC_TYPE_VIDEO)
795
        mov_write_vmhd_tag(pb);
796
    else
797 798 799 800 801
        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);
802
    return updateSize(pb, pos);
803 804
}

805
static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
806
{
807