movenc.c 56.6 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 */
215

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 */
219 220 221
    return 0x11;
}

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
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) {
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);
325
    } else if (track->enc->codec_id == CODEC_ID_AMR_NB) {
326
        mov_write_amr_tag(pb, track);
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 */
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 ||
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'))
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);
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') },
};

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') },
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 &&
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');
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

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);
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);
779 780
}

781
static int mov_write_smhd_tag(ByteIOContext *pb)
782 783 784 785 786 787 788 789 790
{
    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;
}

791
static int mov_write_vmhd_tag(ByteIOContext *pb)
792 793 794 795 796 797 798 799
{
    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
800
static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack *track)
801
{
802
    const char *descr, *hdlr, *hdlr_type;
803
    offset_t pos = url_ftell(pb);
804

805
    if (!track) { /* no media --> data handler */
806 807 808
        hdlr = "dhlr";
        hdlr_type = "url ";
        descr = "DataHandler";
809
    } else {
810 811 812 813 814 815 816 817
        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";
        }
818
    }
819

820
    put_be32(pb, 0); /* size */
821 822
    put_tag(pb, "hdlr");
    put_be32(pb, 0); /* Version & flags */
823
    put_buffer(pb, hdlr, 4); /* handler */
824
    put_tag(pb, hdlr_type); /* handler type */
825 826 827
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
828 829 830 831 832
    put_byte(pb, strlen(descr)); /* string counter */
    put_buffer(pb, descr, strlen(descr)); /* handler description */
    return updateSize(pb, pos);
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
833
static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack *track)
834
{
835
    offset_t pos = url_ftell(pb);
836 837
    put_be32(pb, 0); /* size */
    put_tag(pb, "minf");
838
    if(track->enc->codec_type == CODEC_TYPE_VIDEO)
839
        mov_write_vmhd_tag(pb);
840
    else
841 842 843 844 845
        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);
846
    return updateSize(pb, pos);
847 848
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
849
static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack *track)
850
{
851 852 853
    int version = track->trackDuration < INT32_MAX ? 0 : 1;

    (version == 1) ? put_be32(pb, 44) : put_be32(pb, 32); /* size */
854
    put_tag(pb, "mdhd");
855 856 857 858 859 860 861 862 863
    put_byte(pb, version);
    put_be24(pb, 0); /* flags */
    if (version == 1) {
        put_be64(pb, track->time);
        put_be64(pb, track->time);
    } else {
        put_be32(pb, track->time); /* creation time */
        put_be32(pb, track->time); /* modification time */
    }
864
    put_be32(pb, track->timescale); /* time scale (sample rate for audio) */
865
    (version == 1) ? put_be64(pb, track->trackDuration) : put_be32(pb, track->trackDuration); /* duration */
866
    put_be16(pb, track->language); /* language */
867
    put_be16(pb, 0); /* reserved (quality) */
868 869 870 871

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

876 877 878
    return 32;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
879
static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack *track)
880
{
881
    offset_t pos = url_ftell(pb);
882 883
    put_be32(pb, 0); /* size */
    put_tag(pb, "mdia");
884 885 886 887
    mov_write_mdhd_tag(pb, track);
    mov_write_hdlr_tag(pb, track);
    mov_write_minf_tag(pb, track);
    return updateSize(pb, pos);
888 889
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
890
static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack *track)
891
{
892 893 894 895
    int64_t duration = av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP);
    int version = duration < INT32_MAX ? 0 : 1;

    (version == 1) ? put_be32(pb, 104) : put_be32(pb, 92); /* size */
896
    put_tag(pb, "tkhd");
897 898 899 900 901 902 903 904 905
    put_byte(pb, version);
    put_be24(pb, 0xf); /* flags (track enabled) */
    if (version == 1) {
        put_be64(pb, track->time);
        put_be64(pb, track->time);
    } else {
        put_be32(pb, track->time); /* creation time */
        put_be32(pb, track->time); /* modification time */
    }
906 907
    put_be32(pb, track->trackID); /* track-id */
    put_be32(pb, 0); /* reserved */
908
    (version == 1) ? put_be64(pb, duration) : put_be32(pb, duration);
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932

    put_be32(pb, 0); /* reserved */
    put_be32(pb, 0); /* reserved */
    put_be32(pb, 0x0); /* reserved (Layer & Alternate group) */
    /* Volume, only for audio */
    if(track->enc->codec_type == CODEC_TYPE_AUDIO)
        put_be16(pb, 0x0100);
    else
        put_be16(pb, 0);
    put_be16(pb, 0); /* reserved */

    /* Matrix structure */
    put_be32(pb, 0x00010000); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x00010000); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x40000000); /* reserved */

    /* Track width and height, for visual only */
    if(track->enc->codec_type == CODEC_TYPE_VIDEO) {
933
        double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio);
934
        if(!sample_aspect_ratio) sample_aspect_ratio = 1;
935
        put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000);
936
        put_be32(pb, track->enc->height*0x10000);
937 938 939 940 941 942 943 944
    }
    else {
        put_be32(pb, 0);
        put_be32(pb, 0);
    }
    return 0x5c;
}

945 946 947 948 949 950 951 952 953 954
// This box seems important for the psp playback ... without it the movie seems to hang
static int mov_write_edts_tag(ByteIOContext *pb, MOVTrack *track)
{
    put_be32(pb, 0x24); /* size  */
    put_tag(pb, "edts");
    put_be32(pb, 0x1c); /* size  */
    put_tag(pb, "elst");
    put_be32(pb, 0x0);
    put_be32(pb, 0x1);

955
    put_be32(pb, av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP)); /* duration   ... doesn't seem to effect psp */
956

957
    put_be32(pb, track->cluster[0].cts); /* first pts is cts since dts is 0 */
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
    put_be32(pb, 0x00010000);
    return 0x24;
}

// goes at the end of each track!  ... Critical for PSP playback ("Incompatible data" without it)
static int mov_write_uuid_tag_psp(ByteIOContext *pb, MOVTrack *mov)
{
    put_be32(pb, 0x34); /* size ... reports as 28 in mp4box! */
    put_tag(pb, "uuid");
    put_tag(pb, "USMT");
    put_be32(pb, 0x21d24fce);
    put_be32(pb, 0xbb88695c);
    put_be32(pb, 0xfac9c740);
    put_be32(pb, 0x1c);     // another size here!
    put_tag(pb, "MTDT");
    put_be32(pb, 0x00010012);
    put_be32(pb, 0x0a);
    put_be32(pb, 0x55c40000);
    put_be32(pb, 0x1);
    put_be32(pb, 0x0);
    return 0x34;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
981
static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack *track)
982
{
983
    offset_t pos = url_ftell(pb);
984 985
    put_be32(pb, 0); /* size */
    put_tag(pb, "trak");
986
    mov_write_tkhd_tag(pb, track);
987
    if (track->mode == MODE_PSP || track->hasBframes)
988
        mov_write_edts_tag(pb, track);  // PSP Movies require edts box
989
    mov_write_mdia_tag(pb, track);
990
    if (track->mode == MODE_PSP)
991
        mov_write_uuid_tag_psp(pb,track);  // PSP Movies require this uuid box
992
    return updateSize(pb, pos);
993 994
}

995
#if 0
996
/* TODO: Not sorted out, but not necessary either */
997
static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov)
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
{
    put_be32(pb, 0x15); /* size */
    put_tag(pb, "iods");
    put_be32(pb, 0);    /* version & flags */
    put_be16(pb, 0x1007);
    put_byte(pb, 0);
    put_be16(pb, 0x4fff);
    put_be16(pb, 0xfffe);
    put_be16(pb, 0x01ff);
    return 0x15;
}
1009
#endif
1010

1011
static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov)
1012
{
1013 1014
    int maxTrackID = 1, i;
    int64_t maxTrackLenTemp, maxTrackLen = 0;
1015
    int version;
1016

1017
    for (i=0; i<mov->nb_streams; i++) {
1018
        if(mov->tracks[i].entry > 0) {
1019
            maxTrackLenTemp = av_rescale_rnd(mov->tracks[i].trackDuration, globalTimescale, mov->tracks[i].timescale, AV_ROUND_UP);
1020 1021
            if(maxTrackLen < maxTrackLenTemp)
                maxTrackLen = maxTrackLenTemp;
1022 1023 1024 1025
            if(maxTrackID < mov->tracks[i].trackID)
                maxTrackID = mov->tracks[i].trackID;
        }
    }
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

    version = maxTrackLen < UINT32_MAX ? 0 : 1;
    (version == 1) ? put_be32(pb, 120) : put_be32(pb, 108); /* size */
    put_tag(pb, "mvhd");
    put_byte(pb, version);
    put_be24(pb, 0); /* flags */
    if (version == 1) {
        put_be64(pb, mov->time);
        put_be64(pb, mov->time);
    } else {
        put_be32(pb, mov->time); /* creation time */
        put_be32(pb, mov->time); /* modification time */
    }
    put_be32(pb, mov->timescale); /* timescale */
    (version == 1) ? put_be64(pb, maxTrackLen) : put_be32(pb, maxTrackLen); /* duration of longest track */
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068

    put_be32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */
    put_be16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */
    put_be16(pb, 0); /* reserved */
    put_be32(pb, 0); /* reserved */
    put_be32(pb, 0); /* reserved */

    /* Matrix structure */
    put_be32(pb, 0x00010000); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x00010000); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x0); /* reserved */
    put_be32(pb, 0x40000000); /* reserved */

    put_be32(pb, 0); /* reserved (preview time) */
    put_be32(pb, 0); /* reserved (preview duration) */
    put_be32(pb, 0); /* reserved (poster time) */
    put_be32(pb, 0); /* reserved (selection time) */
    put_be32(pb, 0); /* reserved (selection duration) */
    put_be32(pb, 0); /* reserved (current time) */
    put_be32(pb, maxTrackID+1); /* Next track id */
    return 0x6c;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1069
static int mov_write_itunes_hdlr_tag(ByteIOContext *pb, MOVContext *mov,
1070 1071
                                     AVFormatContext *s)
{
1072
    offset_t pos = url_ftell(pb);
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
    put_be32(pb, 0); /* size */
    put_tag(pb, "hdlr");
    put_be32(pb, 0);
    put_be32(pb, 0);
    put_tag(pb, "mdir");
    put_tag(pb, "appl");
    put_be32(pb, 0);
    put_be32(pb, 0);
    put_be16(pb, 0);
    return updateSize(pb, pos);
}

/* helper function to write a data tag with the specified string as data */
1086
static int mov_write_string_data_tag(ByteIOContext *pb, const char *data, int long_style)
1087
{
1088
    if(long_style){
1089
        offset_t pos = url_ftell(pb);
1090
        put_be32(pb, 0); /* size */
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
        put_tag(pb, "data");
        put_be32(pb, 1);
        put_be32(pb, 0);
        put_buffer(pb, data, strlen(data));
        return updateSize(pb, pos);
    }else{
        put_be16(pb, strlen(data)); /* string length */
        put_be16(pb, 0);
        put_buffer(pb, data, strlen(data));
        return strlen(data) + 4;
1101 1102 1103
    }
}

1104
static int mov_write_string_tag(ByteIOContext *pb, const char *name, const char *value, int long_style){
1105
    int size = 0;
1106
    if (value && value[0]) {
1107
        offset_t pos = url_ftell(pb);
1108
        put_be32(pb, 0); /* size */
1109 1110 1111
        put_tag(pb, name);
        mov_write_string_data_tag(pb, value, long_style);
        size= updateSize(pb, pos);
1112 1113 1114 1115 1116
    }
    return size;
}

/* iTunes year */
1117
static int mov_write_day_tag(ByteIOContext *pb, int year, int long_style)
1118
{
1119 1120 1121 1122 1123 1124
    if(year){
        char year_str[5];
        snprintf(year_str, sizeof(year_str), "%04d", year);
        return mov_write_string_tag(pb, "\251day", year_str, long_style);
    }else
        return 0;
1125 1126 1127
}

/* iTunes track number */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1128
static int mov_write_trkn_tag(ByteIOContext *pb, MOVContext *mov,
1129 1130 1131
                              AVFormatContext *s)
{
    int size = 0;
1132
    if (s->track) {
1133
        offset_t pos = url_ftell(pb);
1134 1135 1136
        put_be32(pb, 0); /* size */
        put_tag(pb, "trkn");
        {
1137
            offset_t pos = url_ftell(pb);
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
            put_be32(pb, 0); /* size */
            put_tag(pb, "data");
            put_be32(pb, 0);        // 8 bytes empty
            put_be32(pb, 0);
            put_be16(pb, 0);        // empty
            put_be16(pb, s->track); // track number
            put_be16(pb, 0);        // total track number
            put_be16(pb, 0);        // empty
            updateSize(pb, pos);
        }
        size = updateSize(pb, pos);
    }
    return size;
}

/* iTunes meta data list */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1154
static int mov_write_ilst_tag(ByteIOContext *pb, MOVContext *mov,
1155 1156
                              AVFormatContext *s)
{
1157
    offset_t pos = url_ftell(pb);
1158 1159
    put_be32(pb, 0); /* size */
    put_tag(pb, "ilst");
1160 1161 1162 1163 1164
    mov_write_string_tag(pb, "\251nam", s->title         , 1);
    mov_write_string_tag(pb, "\251ART", s->author        , 1);
    mov_write_string_tag(pb, "\251wrt", s->author        , 1);
    mov_write_string_tag(pb, "\251alb", s->album         , 1);
    mov_write_day_tag(pb, s->year ,1);
1165
    mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 1);
1166 1167
    mov_write_string_tag(pb, "\251cmt", s->comment       , 1);
    mov_write_string_tag(pb, "\251gen", s->genre         , 1);
1168 1169 1170 1171 1172
    mov_write_trkn_tag(pb, mov, s);
    return updateSize(pb, pos);
}

/* iTunes meta data tag */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1173
static int mov_write_meta_tag(ByteIOContext *pb, MOVContext *mov,
1174 1175 1176 1177 1178
                              AVFormatContext *s)
{
    int size = 0;

    // only save meta tag if required
1179
    if (s->title[0] || s->author[0] || s->album[0] || s->year ||
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1180
        s->comment[0] || s->genre[0] || s->track) {
1181
        offset_t pos = url_ftell(pb);
1182 1183 1184 1185 1186 1187 1188 1189 1190
        put_be32(pb, 0); /* size */
        put_tag(pb, "meta");
        put_be32(pb, 0);
        mov_write_itunes_hdlr_tag(pb, mov, s);
        mov_write_ilst_tag(pb, mov, s);
        size = updateSize(pb, pos);
    }
    return size;
}
1191

1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
static int utf8len(const uint8_t *b)
{
    int len=0;
    int val;
    while(*b){
        GET_UTF8(val, *b++, return -1;)
        len++;
    }
    return len;
}

static int ascii_to_wc(ByteIOContext *pb, const uint8_t *b)
{
    int val;
    while(*b){
        GET_UTF8(val, *b++, return -1;)
        put_be16(pb, val);
    }
    put_be16(pb, 0x00);
    return 0;
}

static uint16_t language_code(const char *str)
{
    return (((str[0]-0x60) & 0x1F) << 10) + (((str[1]-0x60) & 0x1F) << 5) + ((str[2]-0x60) & 0x1F);
}

1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
static int mov_write_3gp_udta_tag(ByteIOContext *pb, AVFormatContext *s,
                                  const char *tag, const char *str)
{
    offset_t pos = url_ftell(pb);
    if (!utf8len(str))
        return 0;
    put_be32(pb, 0);   /* size */
    put_tag (pb, tag); /* type */
    put_be32(pb, 0);   /* version + flags */
    if (!strcmp(tag, "yrrc"))
        put_be16(pb, s->year);
    else {
        put_be16(pb, language_code("eng")); /* language */
        ascii_to_wc(pb, str);
        if (!strcmp(tag, "albm") && s->year)
            put_byte(pb, s->year);
    }
    return updateSize(pb, pos);
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1239
static int mov_write_udta_tag(ByteIOContext *pb, MOVContext *mov,
1240 1241
                              AVFormatContext *s)
{
1242
    int i;
1243
    int bitexact = 0;
1244

1245 1246 1247 1248 1249 1250 1251 1252
    for (i = 0; i < s->nb_streams; i++)
        if (mov->tracks[i].enc->flags & CODEC_FLAG_BITEXACT) {
            bitexact = 1;
            break;
        }

    if (!bitexact && (s->title[0] || s->author[0] || s->album[0] || s->year ||
                      s->comment[0] || s->genre[0]  || s->track)) {
Benoit Fouet's avatar
Benoit Fouet committed
1253
        offset_t pos = url_ftell(pb);
1254

Benoit Fouet's avatar
Benoit Fouet committed
1255 1256
        put_be32(pb, 0); /* size */
        put_tag(pb, "udta");
1257

Michael Niedermayer's avatar
Michael Niedermayer committed
1258
        if (mov->mode & MODE_3GP) {
1259 1260 1261 1262 1263 1264
            mov_write_3gp_udta_tag(pb, s, "titl", s->title);
            mov_write_3gp_udta_tag(pb, s, "auth", s->author);
            mov_write_3gp_udta_tag(pb, s, "gnre", s->genre);
            mov_write_3gp_udta_tag(pb, s, "dscp", s->comment);
            mov_write_3gp_udta_tag(pb, s, "albm", s->album);
            mov_write_3gp_udta_tag(pb, s, "yrrc", "nil");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1265 1266 1267 1268 1269 1270 1271 1272
        } else if (mov->mode == MODE_MOV) { // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
            mov_write_string_tag(pb, "\251nam", s->title         , 0);
            mov_write_string_tag(pb, "\251aut", s->author        , 0);
            mov_write_string_tag(pb, "\251alb", s->album         , 0);
            mov_write_day_tag(pb, s->year, 0);
            mov_write_string_tag(pb, "\251enc", LIBAVFORMAT_IDENT, 0);
            mov_write_string_tag(pb, "\251des", s->comment       , 0);
            mov_write_string_tag(pb, "\251gen", s->genre         , 0);
1273
        } else {
1274 1275
            /* iTunes meta data */
            mov_write_meta_tag(pb, mov, s);
1276
        }
Benoit Fouet's avatar
Benoit Fouet committed
1277
        return updateSize(pb, pos);
1278 1279 1280
    }

    return 0;
1281 1282
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1283
static void mov_write_psp_udta_tag(ByteIOContext *pb,
1284 1285 1286 1287
                                  const char *str, const char *lang, int type)
{
    int len = utf8len(str)+1;
    if(len<=0)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1288
        return;
1289 1290 1291 1292 1293 1294 1295
    put_be16(pb, len*2+10);            /* size */
    put_be32(pb, type);                /* type */
    put_be16(pb, language_code(lang)); /* language */
    put_be16(pb, 0x01);                /* ? */
    ascii_to_wc(pb, str);
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1296
static int mov_write_uuidusmt_tag(ByteIOContext *pb, AVFormatContext *s)
1297
{
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1298
    offset_t pos, pos2;
1299 1300 1301 1302 1303 1304

    if (s->title[0]) {
        pos = url_ftell(pb);
        put_be32(pb, 0); /* size placeholder*/
        put_tag(pb, "uuid");
        put_tag(pb, "USMT");
1305 1306 1307
        put_be32(pb, 0x21d24fce); /* 96 bit UUID */
        put_be32(pb, 0xbb88695c);
        put_be32(pb, 0xfac9c740);
1308

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1309
        pos2 = url_ftell(pb);
1310 1311
        put_be32(pb, 0); /* size placeholder*/
        put_tag(pb, "MTDT");
1312
        put_be16(pb, 4);
1313

1314 1315 1316 1317 1318 1319 1320
        // ?
        put_be16(pb, 0x0C);                 /* size */
        put_be32(pb, 0x0B);                 /* type */
        put_be16(pb, language_code("und")); /* language */
        put_be16(pb, 0x0);                  /* ? */
        put_be16(pb, 0x021C);               /* data */

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1321 1322
        mov_write_psp_udta_tag(pb, LIBAVCODEC_IDENT,      "eng", 0x04);
        mov_write_psp_udta_tag(pb, s->title,              "eng", 0x01);
1323
//        snprintf(dt,32,"%04d/%02d/%02d %02d:%02d:%02d",t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min,t_st->tm_sec);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1324 1325 1326 1327
        mov_write_psp_udta_tag(pb, "2006/04/01 11:11:11", "und", 0x03);

        updateSize(pb, pos2);
        return updateSize(pb, pos);
1328 1329
    }

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1330
    return 0;
1331 1332
}

1333 1334
static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
                              AVFormatContext *s)
1335
{
1336 1337
    int i;
    offset_t pos = url_ftell(pb);
1338 1339 1340 1341
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "moov");
    mov->timescale = globalTimescale;

1342
    for (i=0; i<mov->nb_streams; i++) {
1343 1344 1345 1346
        if(mov->tracks[i].entry <= 0) continue;

        mov->tracks[i].time = mov->time;
        mov->tracks[i].trackID = i+1;
1347 1348
    }

1349 1350
    mov_write_mvhd_tag(pb, mov);
    //mov_write_iods_tag(pb, mov);
1351
    for (i=0; i<mov->nb_streams; i++) {
1352
        if(mov->tracks[i].entry > 0) {
1353
            mov_write_trak_tag(pb, &(mov->tracks[i]));
1354 1355 1356
        }
    }

1357 1358
    if (mov->mode == MODE_PSP)
        mov_write_uuidusmt_tag(pb, s);
1359
    else
1360
        mov_write_udta_tag(pb, mov, s);
1361

1362
    return updateSize(pb, pos);
1363 1364
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1365
static int mov_write_mdat_tag(ByteIOContext *pb, MOVContext *mov)
1366
{
1367
    put_be32(pb, 8);    // placeholder for extended size field (64 bit)
1368
    put_tag(pb, mov->mode == MODE_MOV ? "wide" : "free");
1369

1370
    mov->mdat_pos = url_ftell(pb);
1371 1372 1373 1374 1375 1376
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "mdat");
    return 0;
}

/* TODO: This needs to be more general */
1377
static int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s)
1378
{
1379
    MOVContext *mov = s->priv_data;
1380
    offset_t pos = url_ftell(pb);
1381
    int has_h264 = 0, has_video = 0;
1382
    int minor = 0x200;
1383
    int i;
1384

1385 1386
    for (i = 0; i < s->nb_streams; i++) {
        AVStream *st = s->streams[i];
1387 1388
        if (st->codec->codec_type == CODEC_TYPE_VIDEO)
            has_video = 1;
1389 1390 1391 1392
        if (st->codec->codec_id == CODEC_ID_H264)
            has_h264 = 1;
    }

1393
    put_be32(pb, 0); /* size */
1394
    put_tag(pb, "ftyp");
1395

1396 1397 1398 1399 1400 1401 1402
    if (mov->mode == MODE_3GP) {
        put_tag(pb, has_h264 ? "3gp6"  : "3gp4");
        minor =     has_h264 ?   0x100 :   0x200;
    } else if (mov->mode & MODE_3G2) {
        put_tag(pb, has_h264 ? "3g2b"  : "3g2a");
        minor =     has_h264 ? 0x20000 : 0x10000;
    }else if (mov->mode == MODE_PSP)
1403
        put_tag(pb, "MSNV");
1404
    else if (mov->mode == MODE_MP4)
1405
        put_tag(pb, "isom");
1406 1407 1408
    else if (mov->mode == MODE_IPOD)
        put_tag(pb, has_video ? "M4V ":"M4A ");
    else
1409
        put_tag(pb, "qt  ");
1410

1411
    put_be32(pb, minor);
1412

1413 1414 1415
    if(mov->mode == MODE_MOV)
        put_tag(pb, "qt  ");
    else{
1416
        put_tag(pb, "isom");
1417
        put_tag(pb, "iso2");
1418 1419
        if(has_h264)
            put_tag(pb, "avc1");
1420
    }
1421

1422
    if (mov->mode == MODE_3GP)
1423
        put_tag(pb, has_h264 ? "3gp6":"3gp4");
Michael Niedermayer's avatar
Michael Niedermayer committed
1424
    else if (mov->mode & MODE_3G2)
1425
        put_tag(pb, has_h264 ? "3g2b":"3g2a");
1426
    else if (mov->mode == MODE_PSP)
1427
        put_tag(pb, "MSNV");
1428
    else if (mov->mode == MODE_MP4)
1429
        put_tag(pb, "mp41");
1430
    return updateSize(pb, pos);
1431 1432
}

1433
static void mov_write_uuidprof_tag(ByteIOContext *pb, AVFormatContext *s)
1434
{
1435 1436 1437 1438
    AVCodecContext *VideoCodec = s->streams[0]->codec;
    AVCodecContext *AudioCodec = s->streams[1]->codec;
    int AudioRate = AudioCodec->sample_rate;
    int FrameRate = ((VideoCodec->time_base.den) * (0x10000))/ (VideoCodec->time_base.num);
1439 1440
    int audio_kbitrate= AudioCodec->bit_rate / 1000;
    int video_kbitrate= FFMIN(VideoCodec->bit_rate / 1000, 800 - audio_kbitrate);
1441

1442
    put_be32(pb, 0x94); /* size */
1443 1444 1445
    put_tag(pb, "uuid");
    put_tag(pb, "PROF");

1446 1447 1448
    put_be32(pb, 0x21d24fce); /* 96 bit UUID */
    put_be32(pb, 0xbb88695c);
    put_be32(pb, 0xfac9c740);
1449

1450 1451
    put_be32(pb, 0x0);  /* ? */
    put_be32(pb, 0x3);  /* 3 sections ? */
1452

1453
    put_be32(pb, 0x14); /* size */
1454
    put_tag(pb, "FPRF");
1455 1456 1457
    put_be32(pb, 0x0);  /* ? */
    put_be32(pb, 0x0);  /* ? */
    put_be32(pb, 0x0);  /* ? */
1458

1459
    put_be32(pb, 0x2c);  /* size */
1460
    put_tag(pb, "APRF");   /* audio */
1461 1462
    put_be32(pb, 0x0);
    put_be32(pb, 0x2);   /* TrackID */
1463
    put_tag(pb, "mp4a");
1464 1465
    put_be32(pb, 0x20f);
    put_be32(pb, 0x0);
1466 1467
    put_be32(pb, audio_kbitrate);
    put_be32(pb, audio_kbitrate);
1468 1469
    put_be32(pb, AudioRate);
    put_be32(pb, AudioCodec->channels);
1470

1471
    put_be32(pb, 0x34);  /* size */
1472
    put_tag(pb, "VPRF");   /* video */
1473 1474
    put_be32(pb, 0x0);
    put_be32(pb, 0x1);    /* TrackID */
1475 1476
    if (VideoCodec->codec_id == CODEC_ID_H264) {
        put_tag(pb, "avc1");
1477 1478
        put_be16(pb, 0x014D);
        put_be16(pb, 0x0015);
1479 1480
    } else {
        put_tag(pb, "mp4v");
1481 1482
        put_be16(pb, 0x0000);
        put_be16(pb, 0x0103);
1483
    }
1484
    put_be32(pb, 0x0);
1485 1486
    put_be32(pb, video_kbitrate);
    put_be32(pb, video_kbitrate);
1487 1488 1489 1490 1491
    put_be32(pb, FrameRate);
    put_be32(pb, FrameRate);
    put_be16(pb, VideoCodec->width);
    put_be16(pb, VideoCodec->height);
    put_be32(pb, 0x010001); /* ? */
1492 1493
}

1494 1495
static int mov_write_header(AVFormatContext *s)
{
1496
    ByteIOContext *pb = s->pb;
1497 1498
    MOVContext *mov = s->priv_data;
    int i;
1499

1500
    if (url_is_streamed(s->pb)) {
1501 1502 1503 1504
        av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n");
        return -1;
    }

1505 1506 1507 1508 1509
    /* Default mode == MP4 */
    mov->mode = MODE_MP4;

    if (s->oformat != NULL) {
        if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
Michael Niedermayer's avatar
Michael Niedermayer committed
1510
        else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3GP|MODE_3G2;
1511 1512
        else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
        else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;
1513
        else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD;
1514

1515
        mov_write_ftyp_tag(pb,s);
1516 1517
        if (mov->mode == MODE_PSP) {
            if (s->nb_streams != 2) {
1518 1519 1520 1521 1522 1523 1524
                av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n");
                return -1;
            }
            mov_write_uuidprof_tag(pb,s);
        }
    }

1525
    for(i=0; i<s->nb_streams; i++){
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1526 1527 1528 1529
        AVStream *st= s->streams[i];
        MOVTrack *track= &mov->tracks[i];

        track->enc = st->codec;
1530 1531
        track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV);
        track->mode = mov->mode;
1532 1533
        track->tag = mov_find_codec_tag(s, track);
        if (!track->tag) {
1534 1535
            av_log(s, AV_LOG_ERROR, "track %d: could not find tag, "
                   "codec not currently supported in container\n", i);
1536 1537
            return -1;
        }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1538
        if(st->codec->codec_type == CODEC_TYPE_VIDEO){
1539
            track->timescale = st->codec->time_base.den;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1540
            av_set_pts_info(st, 64, 1, st->codec->time_base.den);
1541
            if (track->mode == MODE_MOV && track->timescale > 100000)
1542
                av_log(s, AV_LOG_WARNING,
1543 1544 1545
                       "WARNING codec timebase is very high. If duration is too long,\n"
                       "file may not be playable by quicktime. Specify a shorter timebase\n"
                       "or choose different container.\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1546
        }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){
1547
            track->timescale = st->codec->sample_rate;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1548
            av_set_pts_info(st, 64, 1, st->codec->sample_rate);
1549
            if(!st->codec->frame_size){
1550 1551
                av_log(s, AV_LOG_ERROR, "track %d: codec frame size is not set\n", i);
                return -1;
1552 1553 1554 1555
            }else if(st->codec->frame_size > 1){ /* assume compressed audio */
                track->audio_vbr = 1;
            }else{
                track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels;
1556
            }
1557
        }
1558 1559
    }

1560 1561
    mov_write_mdat_tag(pb, mov);
    mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
1562
    mov->nb_streams = s->nb_streams;
1563

1564 1565 1566 1567 1568
    put_flush_packet(pb);

    return 0;
}

1569
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
1570 1571
{
    MOVContext *mov = s->priv_data;
1572
    ByteIOContext *pb = s->pb;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1573 1574
    MOVTrack *trk = &mov->tracks[pkt->stream_index];
    AVCodecContext *enc = trk->enc;
1575
    unsigned int samplesInChunk = 0;
1576
    int size= pkt->size;
1577

1578
    if (url_is_streamed(s->pb)) return 0; /* Can't handle that */
1579
    if (!size) return 0; /* Discard 0 sized packets */
1580

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1581
    if (enc->codec_id == CODEC_ID_AMR_NB) {
1582 1583 1584 1585 1586 1587 1588 1589
        /* We must find out how many AMR blocks there are in one packet */
        static uint16_t packed_size[16] =
            {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0};
        int len = 0;

        while (len < size && samplesInChunk < 100) {
            len += packed_size[(pkt->data[len] >> 3) & 0x0F];
            samplesInChunk++;
1590
        }
1591
        if(samplesInChunk > 1){
Ramiro Polla's avatar
Ramiro Polla committed
1592
            av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, implement a AVParser for it\n");
1593 1594
            return -1;
        }
1595 1596 1597
    } else if (trk->sampleSize)
        samplesInChunk = size/trk->sampleSize;
    else
1598
        samplesInChunk = 1;
1599

1600 1601
    /* copy extradata if it exists */
    if (trk->vosLen == 0 && enc->extradata_size > 0) {
1602 1603 1604 1605 1606
        trk->vosLen = enc->extradata_size;
        trk->vosData = av_malloc(trk->vosLen);
        memcpy(trk->vosData, enc->extradata, trk->vosLen);
    }

1607 1608 1609
    if (enc->codec_id == CODEC_ID_H264 && trk->vosLen > 0 && *(uint8_t *)trk->vosData != 1) {
        /* from x264 or from bytestream h264 */
        /* nal reformating needed */
1610
        int ret = ff_avc_parse_nal_units(pkt->data, &pkt->data, &pkt->size);
1611 1612
        if (ret < 0)
            return ret;
1613 1614
        assert(pkt->size);
        size = pkt->size;
1615 1616 1617 1618 1619 1620 1621
    } else if (enc->codec_id == CODEC_ID_DNXHD && !trk->vosLen) {
        /* copy frame header to create needed atoms */
        if (size < 640)
            return -1;
        trk->vosLen = 640;
        trk->vosData = av_malloc(trk->vosLen);
        memcpy(trk->vosData, pkt->data, 640);
1622 1623
    }

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1624 1625
    if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) {
        trk->cluster = av_realloc(trk->cluster, (trk->entry + MOV_INDEX_CLUSTER_SIZE) * sizeof(*trk->cluster));
1626 1627 1628 1629
        if (!trk->cluster)
            return -1;
    }

Baptiste Coudurier's avatar
Baptiste Coudurier committed
1630 1631 1632 1633
    trk->cluster[trk->entry].pos = url_ftell(pb);
    trk->cluster[trk->entry].samplesInChunk = samplesInChunk;
    trk->cluster[trk->entry].size = size;
    trk->cluster[trk->entry].entries = samplesInChunk;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1634 1635 1636
    trk->cluster[trk->entry].dts = pkt->dts;
    trk->trackDuration = pkt->dts - trk->cluster[0].dts + pkt->duration;

1637 1638 1639 1640
    if (pkt->pts == AV_NOPTS_VALUE) {
        av_log(s, AV_LOG_WARNING, "pts has no value\n");
        pkt->pts = pkt->dts;
    }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1641 1642 1643 1644 1645 1646
    if (pkt->dts != pkt->pts)
        trk->hasBframes = 1;
    trk->cluster[trk->entry].cts = pkt->pts - pkt->dts;
    trk->cluster[trk->entry].key_frame = !!(pkt->flags & PKT_FLAG_KEY);
    if(trk->cluster[trk->entry].key_frame)
        trk->hasKeyframes++;
1647 1648
    trk->entry++;
    trk->sampleCount += samplesInChunk;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1649
    mov->mdat_size += size;
1650

1651
    put_buffer(pb, pkt->data, size);
1652 1653 1654 1655 1656 1657 1658 1659

    put_flush_packet(pb);
    return 0;
}

static int mov_write_trailer(AVFormatContext *s)
{
    MOVContext *mov = s->priv_data;
1660
    ByteIOContext *pb = s->pb;
1661
    int res = 0;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1662
    int i;
1663

1664
    offset_t moov_pos = url_ftell(pb);
1665 1666

    /* Write size of mdat tag */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1667
    if (mov->mdat_size+8 <= UINT32_MAX) {
1668
        url_fseek(pb, mov->mdat_pos, SEEK_SET);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1669
        put_be32(pb, mov->mdat_size+8);
1670 1671 1672 1673 1674
    } else {
        /* overwrite 'wide' placeholder atom */
        url_fseek(pb, mov->mdat_pos - 8, SEEK_SET);
        put_be32(pb, 1); /* special value: real atom size will be 64 bit value after tag field */
        put_tag(pb, "mdat");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1675
        put_be64(pb, mov->mdat_size+16);
1676
    }
1677
    url_fseek(pb, moov_pos, SEEK_SET);
1678

1679
    mov_write_moov_tag(pb, mov, s);
1680

1681
    for (i=0; i<mov->nb_streams; i++) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1682 1683
        av_freep(&mov->tracks[i].cluster);

1684
        if(mov->tracks[i].vosLen) av_free(mov->tracks[i].vosData);
1685

1686
    }
1687

1688 1689 1690 1691 1692
    put_flush_packet(pb);

    return res;
}

1693 1694
#ifdef CONFIG_MOV_MUXER
AVOutputFormat mov_muxer = {
1695
    "mov",
1696
    NULL_IF_CONFIG_SMALL("MOV format"),
1697 1698 1699
    NULL,
    "mov",
    sizeof(MOVContext),
1700
    CODEC_ID_AAC,
1701
    CODEC_ID_MPEG4,
1702 1703 1704
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1705
    .flags = AVFMT_GLOBALHEADER,
1706
    .codec_tag = (const AVCodecTag*[]){codec_movvideo_tags, codec_movaudio_tags, 0},
1707
};
1708 1709 1710
#endif
#ifdef CONFIG_TGP_MUXER
AVOutputFormat tgp_muxer = {
1711
    "3gp",
1712
    NULL_IF_CONFIG_SMALL("3GP format"),
1713 1714 1715 1716 1717 1718 1719 1720
    NULL,
    "3gp",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1721
    .flags = AVFMT_GLOBALHEADER,
1722
    .codec_tag = (const AVCodecTag*[]){codec_3gp_tags, 0},
1723
};
1724 1725 1726
#endif
#ifdef CONFIG_MP4_MUXER
AVOutputFormat mp4_muxer = {
1727
    "mp4",
1728
    NULL_IF_CONFIG_SMALL("MP4 format"),
1729
    "application/mp4",
1730
    "mp4",
1731 1732 1733 1734 1735 1736
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1737
    .flags = AVFMT_GLOBALHEADER,
1738
    .codec_tag = (const AVCodecTag*[]){ff_mp4_obj_type, 0},
1739
};
1740 1741 1742
#endif
#ifdef CONFIG_PSP_MUXER
AVOutputFormat psp_muxer = {
1743
    "psp",
1744
    NULL_IF_CONFIG_SMALL("PSP MP4 format"),
1745 1746 1747 1748 1749 1750 1751 1752
    NULL,
    "mp4,psp",
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1753
    .flags = AVFMT_GLOBALHEADER,
1754
    .codec_tag = (const AVCodecTag*[]){ff_mp4_obj_type, 0},
1755
};
1756 1757 1758
#endif
#ifdef CONFIG_TG2_MUXER
AVOutputFormat tg2_muxer = {
1759
    "3g2",
1760
    NULL_IF_CONFIG_SMALL("3GP2 format"),
1761 1762 1763 1764 1765 1766 1767 1768
    NULL,
    "3g2",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1769
    .flags = AVFMT_GLOBALHEADER,
1770
    .codec_tag = (const AVCodecTag*[]){codec_3gp_tags, 0},
1771
};
1772
#endif
1773 1774 1775
#ifdef CONFIG_IPOD_MUXER
AVOutputFormat ipod_muxer = {
    "ipod",
1776
    NULL_IF_CONFIG_SMALL("iPod H.264 MP4 format"),
1777
    "application/mp4",
1778
    "m4v,m4a",
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_H264,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
    .flags = AVFMT_GLOBALHEADER,
    .codec_tag = (const AVCodecTag*[]){ff_mp4_obj_type, 0},
};
#endif