movenc.c 56.7 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 37
#define MODE_MP4  0x01
#define MODE_MOV  0x02
#define MODE_3GP  0x04
#define MODE_PSP  0x08 // 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 40
#define MODE_3G2  0x10
#define MODE_IPOD 0x20
41

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

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

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

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

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

    return curpos - pos;
92 93
}

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

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

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

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

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

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

Baptiste Coudurier's avatar
Baptiste Coudurier committed
181
    return updateSize(pb, pos);
182 183
}

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

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

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

Baptiste Coudurier's avatar
Baptiste Coudurier committed
222 223 224 225 226 227 228 229 230 231
/**
 * This function writes extradata "as is".
 * Extradata must be formated like a valid atom (with size and tag)
 */
static int mov_write_extradata_tag(ByteIOContext *pb, MOVTrack *track)
{
    put_buffer(pb, track->enc->extradata, track->enc->extradata_size);
    return track->enc->extradata_size;
}

232 233 234 235 236 237 238 239
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
240 241
static unsigned int descrLength(unsigned int len)
{
Michael Niedermayer's avatar
Michael Niedermayer committed
242 243 244
    int i;
    for(i=1; len>>(7*i); i++);
    return len + 1 + i;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
245 246
}

Michael Niedermayer's avatar
Michael Niedermayer committed
247
static void putDescr(ByteIOContext *pb, int tag, unsigned int size)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
248
{
Michael Niedermayer's avatar
Michael Niedermayer committed
249 250 251 252 253
    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
254 255
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
256
static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack *track) // Basic
Baptiste Coudurier's avatar
Baptiste Coudurier committed
257 258
{
    offset_t pos = url_ftell(pb);
259
    int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
260

261
    put_be32(pb, 0); // size
Baptiste Coudurier's avatar
Baptiste Coudurier committed
262
    put_tag(pb, "esds");
263
    put_be32(pb, 0); // Version
Baptiste Coudurier's avatar
Baptiste Coudurier committed
264 265 266 267 268

    // ES descriptor
    putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
             descrLength(1));
    put_be16(pb, track->trackID);
269
    put_byte(pb, 0x00); // flags (= no flags)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
270 271 272 273 274

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

    // Object type indication
Baptiste Coudurier's avatar
Baptiste Coudurier committed
275
    put_byte(pb, codec_get_tag(ff_mp4_obj_type, track->enc->codec_id));
Baptiste Coudurier's avatar
Baptiste Coudurier committed
276 277 278 279

    // 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)
280
        put_byte(pb, 0x15); // flags (= Audiostream)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
281
    else
282
        put_byte(pb, 0x11); // flags (= Visualstream)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
283

284 285
    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
Baptiste Coudurier's avatar
Baptiste Coudurier committed
286

287
    put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
288
    if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0)
289
        put_be32(pb, 0); // vbr
Baptiste Coudurier's avatar
Baptiste Coudurier committed
290
    else
291
        put_be32(pb, track->enc->rc_max_rate); // avg bitrate
Baptiste Coudurier's avatar
Baptiste Coudurier committed
292

293
    if (track->vosLen) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
294 295 296 297 298 299 300 301
        // 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);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
302
    return updateSize(pb, pos);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
303 304
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
305
static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack *track)
306
{
307
    offset_t pos = url_ftell(pb);
308 309 310 311 312 313

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

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

316
    if (track->enc->codec_id == CODEC_ID_AAC) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
317 318 319 320
        /* useless atom needed by mplayer, ipod, not needed by quicktime */
        put_be32(pb, 12); /* size */
        put_tag(pb, "mp4a");
        put_be32(pb, 0);
321 322 323 324
        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
325
    } else if (track->enc->codec_id == CODEC_ID_AMR_NB) {
326
        mov_write_amr_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
327 328
    } else if (track->enc->codec_id == CODEC_ID_ALAC) {
        mov_write_extradata_tag(pb, track);
329
    }
330 331 332 333

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

Baptiste Coudurier's avatar
Baptiste Coudurier committed
334
    return updateSize(pb, pos);
335 336
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
337
static int mov_write_glbl_tag(ByteIOContext *pb, MOVTrack *track)
338 339 340 341 342 343 344
{
    put_be32(pb, track->vosLen+8);
    put_tag(pb, "glbl");
    put_buffer(pb, track->vosData, track->vosLen);
    return 8+track->vosLen;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
345
static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack *track)
346
{
347
    offset_t pos = url_ftell(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
348 349 350 351
    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);
352

353
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
354
    put_le32(pb, track->tag); // store it byteswapped
355 356 357
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index, XXX  == 1 */
358

359
    /* SoundDescription */
360
    put_be16(pb, version); /* Version */
361
    put_be16(pb, 0); /* Revision level */
362 363
    put_be32(pb, 0); /* Reserved */

364 365
    if (track->mode == MODE_MOV) {
        put_be16(pb, track->enc->channels);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
366 367 368 369 370
        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);
371 372 373 374 375 376
        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);
    }
377

378
    put_be16(pb, 0); /* packet size (= 0) */
379 380 381
    put_be16(pb, track->timescale); /* Time scale */
    put_be16(pb, 0); /* Reserved */

382 383
    if(version == 1) { /* SoundDescription V1 extended info */
        put_be32(pb, track->enc->frame_size); /* Samples per packet */
384
        put_be32(pb, track->sampleSize / track->enc->channels); /* Bytes per packet */
385
        put_be32(pb, track->sampleSize); /* Bytes per frame */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
386
        put_be32(pb, 2); /* Bytes per sample */
387 388
    }

389 390 391 392
    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 ||
Baptiste Coudurier's avatar
Baptiste Coudurier committed
393 394
        track->enc->codec_id == CODEC_ID_PCM_S32LE ||
        track->enc->codec_id == CODEC_ID_ALAC))
395
        mov_write_wave_tag(pb, track);
396
    else if(track->tag == MKTAG('m','p','4','a'))
Baptiste Coudurier's avatar
Baptiste Coudurier committed
397 398
        mov_write_esds_tag(pb, track);
    else if(track->enc->codec_id == CODEC_ID_AMR_NB)
399
        mov_write_amr_tag(pb, track);
400 401
    else if (track->enc->codec_id == CODEC_ID_ALAC)
        mov_write_extradata_tag(pb, track);
402 403
    else if(track->vosLen > 0)
        mov_write_glbl_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
404

Baptiste Coudurier's avatar
Baptiste Coudurier committed
405
    return updateSize(pb, pos);
406 407
}

408
static int mov_write_d263_tag(ByteIOContext *pb)
409 410 411 412
{
    put_be32(pb, 0xf); /* size */
    put_tag(pb, "d263");
    put_tag(pb, "FFMP");
413 414 415 416
    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 */
417 418 419
    return 0xf;
}

420 421
/* TODO: No idea about these values */
static int mov_write_svq3_tag(ByteIOContext *pb)
422
{
423 424 425 426 427 428
    put_be32(pb, 0x15);
    put_tag(pb, "SMI ");
    put_tag(pb, "SEQH");
    put_be32(pb, 0x5);
    put_be32(pb, 0xe2c0211d);
    put_be32(pb, 0xc0000000);
429
    put_byte(pb, 0);
430
    return 0x15;
431 432
}

433 434 435 436 437 438
static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
{
    offset_t pos = url_ftell(pb);

    put_be32(pb, 0);
    put_tag(pb, "avcC");
439
    ff_isom_write_avcc(pb, track->vosData, track->vosLen);
440 441 442
    return updateSize(pb, pos);
}

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
/* 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 */
477 478 479 480
        if (track->enc->height == 1080)
            put_be32(pb, 5); /* unknown */
        else
            put_be32(pb, 6); /* unknown */
481 482 483 484 485 486 487 488 489 490
    }
    /* padding */
    for (i = 0; i < 10; i++)
        put_be64(pb, 0);

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

491 492 493 494 495 496 497 498 499
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
500 501
static const AVCodecTag mov_pix_fmt_tags[] = {
    { PIX_FMT_YUYV422, MKTAG('y','u','v','s') },
Baptiste Coudurier's avatar
Baptiste Coudurier committed
502
    { PIX_FMT_UYVY422, MKTAG('2','v','u','y') },
Baptiste Coudurier's avatar
Baptiste Coudurier committed
503 504 505 506 507
    { PIX_FMT_BGR555,  MKTAG('r','a','w',' ') },
    { PIX_FMT_RGB24,   MKTAG('r','a','w',' ') },
    { PIX_FMT_BGR32_1, MKTAG('r','a','w',' ') },
};

508 509 510 511 512 513 514
static const AVCodecTag codec_ipod_tags[] = {
    { 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_ALAC,   MKTAG('a','l','a','c') },
};

515
static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
516
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
517
    int tag = track->enc->codec_tag;
518
    if (track->mode == MODE_MP4 || track->mode == MODE_PSP) {
519 520 521 522 523
        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');
524 525 526 527 528
    } else if (track->mode == MODE_IPOD) {
        tag = codec_get_tag(codec_ipod_tags, track->enc->codec_id);
        if (!match_ext(s->filename, "m4a") && !match_ext(s->filename, "m4v"))
            av_log(s, AV_LOG_WARNING, "Warning, extension is not .m4a nor .m4v "
                   "Quicktime/Ipod might not play the file\n");
Michael Niedermayer's avatar
Michael Niedermayer committed
529
    } else if (track->mode & MODE_3GP) {
530
        tag = codec_get_tag(codec_3gp_tags, track->enc->codec_id);
531
    } else if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
Baptiste Coudurier's avatar
Baptiste Coudurier committed
532 533
                        (tag == MKTAG('d','v','c','p') ||
                         track->enc->codec_id == CODEC_ID_RAWVIDEO))) {
534
        if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
535 536 537 538 539 540
            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
541 542 543 544
        } 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;
545
        } else {
546
            if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
547
                tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
548
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
549
                    tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
550 551 552
                    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
553
                }
554
            } else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
555
                tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
556
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
557 558 559
                    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));
560 561
                        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
562 563
                    }
                }
564 565 566
            }
        }
    }
567 568 569
    return tag;
}

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
/** Write uuid atom.
 * Needed to make file play in iPods running newest firmware
 * goes after avcC atom in moov.trak.mdia.minf.stbl.stsd.avc1
 */
static int mov_write_uuid_tag_ipod(ByteIOContext *pb)
{
    put_be32(pb, 28);
    put_tag(pb, "uuid");
    put_be32(pb, 0x6b6840f2);
    put_be32(pb, 0x5f244fc5);
    put_be32(pb, 0xba39a51b);
    put_be32(pb, 0xcf0323f3);
    put_be32(pb, 0x0);
    return 28;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
586
static int mov_write_video_tag(ByteIOContext *pb, MOVTrack *track)
587
{
588
    offset_t pos = url_ftell(pb);
589
    char compressor_name[32];
590

591
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
592
    put_le32(pb, track->tag); // store it byteswapped
593 594 595 596
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index */

597 598
    put_be16(pb, 0); /* Codec stream version */
    put_be16(pb, 0); /* Codec stream revision (=0) */
599
    if (track->mode == MODE_MOV) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
600 601 602 603 604 605 606 607
        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 */
        }
608 609 610 611 612
    } else {
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
    }
613 614
    put_be16(pb, track->enc->width); /* Video width */
    put_be16(pb, track->enc->height); /* Video height */
615 616
    put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */
    put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */
617 618
    put_be32(pb, 0); /* Data size (= 0) */
    put_be16(pb, 1); /* Frame count (= 1) */
619

620
    memset(compressor_name,0,32);
621 622
    /* 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)
623
        strncpy(compressor_name,track->enc->codec->name,31);
624
    put_byte(pb, strlen(compressor_name));
625
    put_buffer(pb, compressor_name, 31);
626

Baptiste Coudurier's avatar
Baptiste Coudurier committed
627 628 629 630
    if (track->mode == MODE_MOV && track->enc->bits_per_sample)
        put_be16(pb, track->enc->bits_per_sample);
    else
        put_be16(pb, 0x18); /* Reserved */
631
    put_be16(pb, 0xffff); /* Reserved */
632
    if(track->tag == MKTAG('m','p','4','v'))
633 634 635 636
        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)
637
        mov_write_svq3_tag(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
638 639
    else if(track->enc->codec_id == CODEC_ID_DNXHD)
        mov_write_avid_tag(pb, track);
640
    else if(track->enc->codec_id == CODEC_ID_H264) {
641
        mov_write_avcc_tag(pb, track);
642 643
        if(track->mode == MODE_IPOD)
            mov_write_uuid_tag_ipod(pb);
644
    } else if(track->vosLen > 0)
645
        mov_write_glbl_tag(pb, track);
646

Baptiste Coudurier's avatar
Baptiste Coudurier committed
647
    return updateSize(pb, pos);
648 649
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
650
static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack *track)
651
{
652
    offset_t pos = url_ftell(pb);
653 654 655 656
    put_be32(pb, 0); /* size */
    put_tag(pb, "stsd");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */
657 658 659 660
    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);
661
    return updateSize(pb, pos);
662 663
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
664
static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack *track)
665
{
666
    MOV_stts_t *ctts_entries;
667 668 669 670 671 672
    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
673
    ctts_entries[0].duration = track->cluster[0].cts;
674
    for (i=1; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
675
        if (track->cluster[i].cts == ctts_entries[entries].duration) {
676 677 678
            ctts_entries[entries].count++; /* compress */
        } else {
            entries++;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
679
            ctts_entries[entries].duration = track->cluster[i].cts;
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
            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;
}

697
/* Time to sample atom */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
698
static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack *track)
699
{
700
    MOV_stts_t *stts_entries;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
    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 */
728 729
    put_tag(pb, "stts");
    put_be32(pb, 0); /* version & flags */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
730 731 732 733 734 735 736
    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;
737 738
}

739
static int mov_write_dref_tag(ByteIOContext *pb)
740 741 742 743 744 745 746 747 748 749 750 751 752
{
    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;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
753
static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack *track)
754
{
755
    offset_t pos = url_ftell(pb);
756 757
    put_be32(pb, 0); /* size */
    put_tag(pb, "stbl");
758 759
    mov_write_stsd_tag(pb, track);
    mov_write_stts_tag(pb, track);
760
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
761
        track->hasKeyframes && track->hasKeyframes < track->entry)
762
        mov_write_stss_tag(pb, track);
763 764 765
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
        track->hasBframes)
        mov_write_ctts_tag(pb, track);
766 767 768 769
    mov_write_stsc_tag(pb, track);
    mov_write_stsz_tag(pb, track);
    mov_write_stco_tag(pb, track);
    return updateSize(pb, pos);
770 771
}

772
static int mov_write_dinf_tag(ByteIOContext *pb)
773
{
774
    offset_t pos = url_ftell(pb);
775 776
    put_be32(pb, 0); /* size */
    put_tag(pb, "dinf");
777 778
    mov_write_dref_tag(pb);
    return updateSize(pb, pos);
Michael Niedermayer's avatar