movenc.c 55 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
#define MODE_IPOD 5
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
    }
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
        }
    }
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

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 205
    url_fseek(pb, curpos, SEEK_SET);
    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 302 303 304
        // 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);
}

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 334 335 336

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

    return updateSize (pb, pos);
}

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->vosLen > 0)
        mov_write_glbl_tag(pb, track);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
402

403
    return updateSize (pb, pos);
404 405
}

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

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

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

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

441 442 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
/* 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 */
475 476 477 478
        if (track->enc->height == 1080)
            put_be32(pb, 5); /* unknown */
        else
            put_be32(pb, 6); /* unknown */
479 480 481 482 483 484 485 486 487 488
    }
    /* padding */
    for (i = 0; i < 10; i++)
        put_be64(pb, 0);

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

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

506
static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track)
507
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
508
    int tag = track->enc->codec_tag;
509
    if (track->mode == MODE_MP4 || track->mode == MODE_PSP || track->mode == MODE_IPOD) {
510 511 512 513 514 515 516
        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);
517
    } else if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
Baptiste Coudurier's avatar
Baptiste Coudurier committed
518 519
                        (tag == MKTAG('d','v','c','p') ||
                         track->enc->codec_id == CODEC_ID_RAWVIDEO))) {
520
        if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
521 522 523 524 525 526
            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
527 528 529 530
        } 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;
531
        } else {
532
            if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
533
                tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
534
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
535
                    tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
536 537 538
                    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
539
                }
540
            } else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
541
                tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
542
                if (!tag) { // if no mac fcc found, try with Microsoft tags
Baptiste Coudurier's avatar
Baptiste Coudurier committed
543 544 545
                    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));
546 547
                        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
548 549
                    }
                }
550 551 552
            }
        }
    }
553 554 555
    return tag;
}

556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
/** 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
572
static int mov_write_video_tag(ByteIOContext *pb, MOVTrack *track)
573
{
574
    offset_t pos = url_ftell(pb);
575
    char compressor_name[32];
576

577
    put_be32(pb, 0); /* size */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
578
    put_le32(pb, track->tag); // store it byteswapped
579 580 581 582
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index */

583 584
    put_be16(pb, 0); /* Codec stream version */
    put_be16(pb, 0); /* Codec stream revision (=0) */
585
    if (track->mode == MODE_MOV) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
586 587 588 589 590 591 592 593
        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 */
        }
594 595 596 597 598
    } else {
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
        put_be32(pb, 0); /* Reserved */
    }
599 600
    put_be16(pb, track->enc->width); /* Video width */
    put_be16(pb, track->enc->height); /* Video height */
601 602
    put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */
    put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */
603 604
    put_be32(pb, 0); /* Data size (= 0) */
    put_be16(pb, 1); /* Frame count (= 1) */
605

606
    memset(compressor_name,0,32);
607 608
    /* 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)
609
        strncpy(compressor_name,track->enc->codec->name,31);
610
    put_byte(pb, strlen(compressor_name));
611
    put_buffer(pb, compressor_name, 31);
612

Baptiste Coudurier's avatar
Baptiste Coudurier committed
613 614 615 616
    if (track->mode == MODE_MOV && track->enc->bits_per_sample)
        put_be16(pb, track->enc->bits_per_sample);
    else
        put_be16(pb, 0x18); /* Reserved */
617
    put_be16(pb, 0xffff); /* Reserved */
618
    if(track->tag == MKTAG('m','p','4','v'))
619 620 621 622
        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)
623
        mov_write_svq3_tag(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
624 625
    else if(track->enc->codec_id == CODEC_ID_DNXHD)
        mov_write_avid_tag(pb, track);
626
    else if(track->enc->codec_id == CODEC_ID_H264) {
627
        mov_write_avcc_tag(pb, track);
628 629
        if(track->mode == MODE_IPOD)
            mov_write_uuid_tag_ipod(pb);
630
    } else if(track->vosLen > 0)
631
        mov_write_glbl_tag(pb, track);
632 633

    return updateSize (pb, pos);
634 635
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
636
static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack *track)
637
{
638
    offset_t pos = url_ftell(pb);
639 640 641 642
    put_be32(pb, 0); /* size */
    put_tag(pb, "stsd");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */
643 644 645 646
    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);
647
    return updateSize(pb, pos);
648 649
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
650
static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack *track)
651
{
652
    MOV_stts_t *ctts_entries;
653 654 655 656 657 658
    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
659
    ctts_entries[0].duration = track->cluster[0].cts;
660
    for (i=1; i<track->entry; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
661
        if (track->cluster[i].cts == ctts_entries[entries].duration) {
662 663 664
            ctts_entries[entries].count++; /* compress */
        } else {
            entries++;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
665
            ctts_entries[entries].duration = track->cluster[i].cts;
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
            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;
}

683
/* Time to sample atom */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
684
static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack *track)
685
{
686
    MOV_stts_t *stts_entries;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
    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 */
714 715
    put_tag(pb, "stts");
    put_be32(pb, 0); /* version & flags */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
716 717 718 719 720 721 722
    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;
723 724
}

725
static int mov_write_dref_tag(ByteIOContext *pb)
726 727 728 729 730 731 732 733 734 735 736 737 738
{
    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
739
static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack *track)
740
{
741
    offset_t pos = url_ftell(pb);
742 743
    put_be32(pb, 0); /* size */
    put_tag(pb, "stbl");
744 745
    mov_write_stsd_tag(pb, track);
    mov_write_stts_tag(pb, track);
746
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
747
        track->hasKeyframes && track->hasKeyframes < track->entry)
748
        mov_write_stss_tag(pb, track);
749 750 751
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
        track->hasBframes)
        mov_write_ctts_tag(pb, track);
752 753 754 755
    mov_write_stsc_tag(pb, track);
    mov_write_stsz_tag(pb, track);
    mov_write_stco_tag(pb, track);
    return updateSize(pb, pos);
756 757
}

758
static int mov_write_dinf_tag(ByteIOContext *pb)
759
{
760
    offset_t pos = url_ftell(pb);
761 762
    put_be32(pb, 0); /* size */
    put_tag(pb, "dinf");
763 764
    mov_write_dref_tag(pb);
    return updateSize(pb, pos);
765 766
}

767
static int mov_write_smhd_tag(ByteIOContext *pb)
768 769 770 771 772 773 774 775 776
{
    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;
}

777
static int mov_write_vmhd_tag(ByteIOContext *pb)
778 779 780 781 782 783 784 785
{
    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;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
786
static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack *track)
787
{