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

25 26 27
#undef NDEBUG
#include <assert.h>

28 29 30
#define MOV_INDEX_CLUSTER_SIZE 16384
#define globalTimescale 1000

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

38
typedef struct MOVIentry {
39 40
    unsigned int flags, size;
    uint64_t     pos;
41
    unsigned int samplesInChunk;
42
    char         key_frame;
43
    unsigned int entries;
44
    int64_t      cts;
45 46 47
} MOVIentry;

typedef struct MOVIndex {
48
    int         mode;
49 50 51
    int         entry;
    long        timescale;
    long        time;
52
    int64_t     trackDuration;
53 54
    long        sampleCount;
    long        sampleDuration;
55
    long        sampleSize;
56
    int         hasKeyframes;
57
    int         hasBframes;
58
    int         language;
59
    int         trackID;
bcoudurier's avatar
bcoudurier committed
60
    int         tag;
61 62 63
    AVCodecContext *enc;

    int         vosLen;
64
    uint8_t     *vosData;
bcoudurier's avatar
bcoudurier committed
65
    MOVIentry   *cluster;
66 67
} MOVTrack;

68
typedef struct MOVContext {
69
    int     mode;
70
    int64_t time;
71
    int     nb_streams;
72
    offset_t mdat_pos;
bcoudurier's avatar
bcoudurier committed
73
    uint64_t mdat_size;
74 75 76 77
    long    timescale;
    MOVTrack tracks[MAX_STREAMS];
} MOVContext;

78
//FIXME supprt 64bit varaint with wide placeholders
79
static offset_t updateSize (ByteIOContext *pb, offset_t pos)
80
{
81
    offset_t curpos = url_ftell(pb);
82
    url_fseek(pb, pos, SEEK_SET);
83
    put_be32(pb, curpos - pos); /* rewrite size */
84
    url_fseek(pb, curpos, SEEK_SET);
85 86

    return curpos - pos;
87 88
}

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

112
/* Sample size atom */
113
static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track)
114
{
115
    int equalChunks = 1;
116
    int i, j, entries = 0, tst = -1, oldtst = -1;
117

118
    offset_t pos = url_ftell(pb);
119
    put_be32(pb, 0); /* size */
120 121 122
    put_tag(pb, "stsz");
    put_be32(pb, 0); /* version & flags */

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

149
/* Sample to chunk atom */
150
static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track)
151
{
152 153
    int index = 0, oldval = -1, i;
    offset_t entryPos, curpos;
154

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

176
    return updateSize (pb, pos);
177 178
}

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

203
static int mov_write_damr_tag(ByteIOContext *pb)
204 205 206 207 208
{
    put_be32(pb, 0x11); /* size */
    put_tag(pb, "damr");
    put_tag(pb, "FFMP");
    put_byte(pb, 0);
209

210 211 212 213
    //put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */
    //put_be16(pb, 0xa); /* Mode change period (no restriction) */
    put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */
    put_be16(pb, 1); /* Mode change period (no restriction) */
214 215 216
    return 0x11;
}

bcoudurier's avatar
bcoudurier committed
217 218 219 220 221 222 223 224 225 226 227 228
static int mov_write_samr_tag(ByteIOContext *pb)
{
    put_be32(pb, 0x11); /* size */
    put_tag(pb, "samr");
    put_tag(pb, "FFMP");
    put_byte(pb, 1);

    put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */
    put_be16(pb, 0x5); /* Mode change period (no restriction) */
    return 0x11;
}

bcoudurier's avatar
bcoudurier committed
229 230 231 232 233 234 235 236
static int mov_write_enda_tag(ByteIOContext *pb)
{
    put_be32(pb, 10);
    put_tag(pb, "enda");
    put_be16(pb, 1); /* little endian */
    return 10;
}

bcoudurier's avatar
bcoudurier committed
237 238
static unsigned int descrLength(unsigned int len)
{
michael's avatar
michael committed
239 240 241
    int i;
    for(i=1; len>>(7*i); i++);
    return len + 1 + i;
bcoudurier's avatar
bcoudurier committed
242 243
}

michael's avatar
michael committed
244
static void putDescr(ByteIOContext *pb, int tag, unsigned int size)
bcoudurier's avatar
bcoudurier committed
245
{
michael's avatar
michael committed
246 247 248 249 250
    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);
bcoudurier's avatar
bcoudurier committed
251 252 253 254 255
}

static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
{
    offset_t pos = url_ftell(pb);
bcoudurier's avatar
bcoudurier committed
256
    int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
bcoudurier's avatar
bcoudurier committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

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

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

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

    // Object type indication
    put_byte(pb, codec_get_tag(ff_mov_obj_type, track->enc->codec_id));

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

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

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

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


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

304 305
static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track)
{
306
    offset_t pos = url_ftell(pb);
307 308 309 310 311 312

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

    put_be32(pb, 12);    /* size */
    put_tag(pb, "frma");
bcoudurier's avatar
bcoudurier committed
313
    put_le32(pb, track->tag);
314

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

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

    return updateSize (pb, pos);
}

334
static const CodecTag codec_movaudio_tags[] = {
alex's avatar
alex committed
335 336 337 338 339 340 341
    { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') },
    { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') },
    { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') },
    { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') },
    { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') },
    { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') },
    { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') },
342
    { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') },
alex's avatar
alex committed
343 344
    { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') },
    { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') },
bcoudurier's avatar
bcoudurier committed
345 346 347 348
    { CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') },
    { CODEC_ID_PCM_S24LE, MKTAG('i', 'n', '2', '4') },
    { CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2') },
    { CODEC_ID_PCM_S32LE, MKTAG('i', 'n', '3', '2') },
alex's avatar
alex committed
349
    { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') },
350
    { CODEC_ID_NONE, 0 },
alex's avatar
alex committed
351 352
};

353
static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
354
{
355
    offset_t pos = url_ftell(pb);
356 357 358
    int vbr=  track->enc->codec_id == CODEC_ID_AAC ||
              track->enc->codec_id == CODEC_ID_MP3 ||
              track->enc->codec_id == CODEC_ID_AMR_NB;
bcoudurier's avatar
bcoudurier committed
359
    int version = track->mode == MODE_MOV &&
360
        (vbr ||
bcoudurier's avatar
bcoudurier committed
361 362
         track->enc->codec_id == CODEC_ID_PCM_S32LE ||
         track->enc->codec_id == CODEC_ID_PCM_S24LE);
363

364
    put_be32(pb, 0); /* size */
bcoudurier's avatar
bcoudurier committed
365
    put_le32(pb, track->tag); // store it byteswapped
366 367 368
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index, XXX  == 1 */
369

370
    /* SoundDescription */
bcoudurier's avatar
bcoudurier committed
371
    put_be16(pb, version); /* Version */
372
    put_be16(pb, 0); /* Revision level */
373 374
    put_be32(pb, 0); /* Reserved */

375 376
    put_be16(pb, track->enc->channels); /* Number of channels */
    /* TODO: Currently hard-coded to 16-bit, there doesn't seem
377
                 to be a good way to get number of bits of audio */
378
    put_be16(pb, 0x10); /* Reserved */
379

380
    if(vbr) {
381
        put_be16(pb, 0xfffe); /* compression ID (vbr)*/
bcoudurier's avatar
bcoudurier committed
382
    } else {
383 384
        put_be16(pb, 0); /* compression ID (= 0) */
    }
romansh's avatar
 
romansh committed
385
    put_be16(pb, 0); /* packet size (= 0) */
386 387 388
    put_be16(pb, track->timescale); /* Time scale */
    put_be16(pb, 0); /* Reserved */

389
    if(version == 1) { /* SoundDescription V1 extended info */
390 391
        if (vbr)
            track->sampleSize = 0;
392
        put_be32(pb, track->enc->frame_size); /* Samples per packet */
393
        put_be32(pb, track->sampleSize / track->enc->channels); /* Bytes per packet */
394
        put_be32(pb, track->sampleSize); /* Bytes per frame */
bcoudurier's avatar
bcoudurier committed
395
        put_be32(pb, 2); /* Bytes per sample */
396 397
    }

bcoudurier's avatar
bcoudurier committed
398 399 400 401 402
    if(track->mode == MODE_MOV &&
       (track->enc->codec_id == CODEC_ID_AAC ||
        track->enc->codec_id == CODEC_ID_AMR_NB ||
        track->enc->codec_id == CODEC_ID_PCM_S24LE ||
        track->enc->codec_id == CODEC_ID_PCM_S32LE))
bcoudurier's avatar
bcoudurier committed
403
        mov_write_wave_tag(pb, track);
bcoudurier's avatar
bcoudurier committed
404 405 406 407 408
    else if(track->enc->codec_id == CODEC_ID_AAC)
        mov_write_esds_tag(pb, track);
    else if(track->enc->codec_id == CODEC_ID_AMR_NB)
        mov_write_damr_tag(pb);

409
    return updateSize (pb, pos);
410 411
}

412
static int mov_write_d263_tag(ByteIOContext *pb)
413 414 415 416 417 418 419 420 421
{
    put_be32(pb, 0xf); /* size */
    put_tag(pb, "d263");
    put_tag(pb, "FFMP");
    put_be16(pb, 0x0a);
    put_byte(pb, 0);
    return 0xf;
}

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

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
static uint8_t *avc_find_startcode( uint8_t *p, uint8_t *end )
{
    uint8_t *a = p + 4 - ((int)p & 3);

    for( end -= 3; p < a && p < end; p++ ) {
        if( p[0] == 0 && p[1] == 0 && p[2] == 1 )
            return p;
    }

    for( end -= 3; p < end; p += 4 ) {
        uint32_t x = *(uint32_t*)p;
//      if( (x - 0x01000100) & (~x) & 0x80008000 ) // little endian
//      if( (x - 0x00010001) & (~x) & 0x00800080 ) // big endian
        if( (x - 0x01010101) & (~x) & 0x80808080 ) { // generic
            if( p[1] == 0 ) {
                if( p[0] == 0 && p[2] == 1 )
bcoudurier's avatar
bcoudurier committed
451
                    return p-1;
452
                if( p[2] == 0 && p[3] == 1 )
bcoudurier's avatar
bcoudurier committed
453
                    return p;
454 455 456
            }
            if( p[3] == 0 ) {
                if( p[2] == 0 && p[4] == 1 )
bcoudurier's avatar
bcoudurier committed
457
                    return p+1;
458
                if( p[4] == 0 && p[5] == 1 )
bcoudurier's avatar
bcoudurier committed
459
                    return p+2;
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
            }
        }
    }

    for( end += 3; p < end; p++ ) {
        if( p[0] == 0 && p[1] == 0 && p[2] == 1 )
            return p;
    }

    return end + 3;
}

static void avc_parse_nal_units(uint8_t **buf, int *size)
{
    ByteIOContext pb;
    uint8_t *p = *buf;
    uint8_t *end = p + *size;
    uint8_t *nal_start, *nal_end;

    url_open_dyn_buf(&pb);
    nal_start = avc_find_startcode(p, end);
    while (nal_start < end) {
        while(!*(nal_start++));
        nal_end = avc_find_startcode(nal_start, end);
        put_be32(&pb, nal_end - nal_start);
        put_buffer(&pb, nal_start, nal_end - nal_start);
        nal_start = nal_end;
    }
    av_freep(buf);
    *size = url_close_dyn_buf(&pb, buf);
}

static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
{
    offset_t pos = url_ftell(pb);

    put_be32(pb, 0);
    put_tag(pb, "avcC");
    if (track->vosLen > 6) {
        /* check for h264 start code */
        if (BE_32(track->vosData) == 0x00000001) {
            uint8_t *buf, *end;
            uint32_t sps_size=0, pps_size=0;
            uint8_t *sps=0, *pps=0;

            avc_parse_nal_units(&track->vosData, &track->vosLen);
            buf = track->vosData;
            end = track->vosData + track->vosLen;

            /* look for sps and pps */
            while (buf < end) {
                unsigned int size;
                uint8_t nal_type;
                size = BE_32(buf);
                nal_type = buf[4] & 0x1f;
                if (nal_type == 7) { /* SPS */
                    sps = buf + 4;
                    sps_size = size;
                } else if (nal_type == 8) { /* PPS */
                    pps = buf + 4;
                    pps_size = size;
                }
                buf += size + 4;
            }
            assert(sps);
            assert(pps);
526 527 528 529 530 531 532 533

            put_byte(pb, 1); /* version */
            put_byte(pb, sps[1]); /* profile */
            put_byte(pb, sps[2]); /* profile compat */
            put_byte(pb, sps[3]); /* level */
            put_byte(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
            put_byte(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */

534 535 536 537 538 539 540 541 542 543 544 545
            put_be16(pb, sps_size);
            put_buffer(pb, sps, sps_size);
            put_byte(pb, 1); /* number of pps */
            put_be16(pb, pps_size);
            put_buffer(pb, pps, pps_size);
        } else {
            put_buffer(pb, track->vosData, track->vosLen);
        }
    }
    return updateSize(pb, pos);
}

546
static const CodecTag codec_movvideo_tags[] = {
alex's avatar
alex committed
547 548 549 550
    { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') },
    { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') },
    { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
    { CODEC_ID_H263, MKTAG('s', '2', '6', '3') },
551
    { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') },
552 553 554 555 556 557 558
    /* special handling in mov_find_video_codec_tag */
    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */
    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */
    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'p', 'p') }, /* DVCPRO PAL */
    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'n') }, /* DVCPRO50 NTSC */
    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'p') }, /* DVCPRO50 PAL */
    { CODEC_ID_NONE, 0 },
alex's avatar
alex committed
559 560
};

bcoudurier's avatar
bcoudurier committed
561
static int mov_find_video_codec_tag(AVFormatContext *s, MOVTrack *track)
562
{
bcoudurier's avatar
bcoudurier committed
563
    int tag = track->enc->codec_tag;
564 565 566 567 568 569 570 571 572 573 574 575
    if (!tag) {
        if (track->enc->codec_id == CODEC_ID_DVVIDEO) {
            if (track->enc->height == 480) { /* NTSC */
                if (track->enc->pix_fmt == PIX_FMT_YUV422P)
                    tag = MKTAG('d', 'v', '5', 'n');
                else
                    tag = MKTAG('d', 'v', 'c', ' ');
            } else { /* assume PAL */
                if (track->enc->pix_fmt == PIX_FMT_YUV422P)
                    tag = MKTAG('d', 'v', '5', 'p');
                else if (track->enc->pix_fmt == PIX_FMT_YUV420P)
                    tag = MKTAG('d', 'v', 'c', 'p');
bcoudurier's avatar
bcoudurier committed
576 577
                else
                    tag = MKTAG('d', 'v', 'p', 'p');
578 579 580 581 582 583
            }
        } else {
            tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
        }
    }
    // if no mac fcc found, try with Microsoft tags
bcoudurier's avatar
bcoudurier committed
584
    if (!tag) {
585
        tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
bcoudurier's avatar
bcoudurier committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        if (tag) {
            av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n");
        }
    }
    assert(tag);
    return tag;
}

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

612
static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
613
{
614
    offset_t pos = url_ftell(pb);
615
    char compressor_name[32];
alex's avatar
alex committed
616

617
    put_be32(pb, 0); /* size */
bcoudurier's avatar
bcoudurier committed
618
    put_le32(pb, track->tag); // store it byteswapped
619 620 621 622
    put_be32(pb, 0); /* Reserved */
    put_be16(pb, 0); /* Reserved */
    put_be16(pb, 1); /* Data-reference index */

623 624 625 626 627 628 629 630 631 632
    put_be16(pb, 0); /* Codec stream version */
    put_be16(pb, 0); /* Codec stream revision (=0) */
    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 */
    }
633 634
    put_be16(pb, track->enc->width); /* Video width */
    put_be16(pb, track->enc->height); /* Video height */
635 636
    put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */
    put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */
romansh's avatar
 
romansh committed
637 638
    put_be32(pb, 0); /* Data size (= 0) */
    put_be16(pb, 1); /* Frame count (= 1) */
639

640
    memset(compressor_name,0,32);
641
    if (track->enc->codec && track->enc->codec->name)
642
        strncpy(compressor_name,track->enc->codec->name,31);
643
    put_byte(pb, strlen(compressor_name));
644
    put_buffer(pb, compressor_name, 31);
645

646 647 648 649 650 651 652
    put_be16(pb, 0x18); /* Reserved */
    put_be16(pb, 0xffff); /* Reserved */
    if(track->enc->codec_id == CODEC_ID_MPEG4)
        mov_write_esds_tag(pb, track);
    else if(track->enc->codec_id == CODEC_ID_H263)
        mov_write_d263_tag(pb);
    else if(track->enc->codec_id == CODEC_ID_SVQ3)
653
        mov_write_svq3_tag(pb);
654 655
    else if(track->enc->codec_id == CODEC_ID_H264)
        mov_write_avcc_tag(pb, track);
656 657

    return updateSize (pb, pos);
658 659
}

660
static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
661
{
662
    offset_t pos = url_ftell(pb);
663 664 665 666
    put_be32(pb, 0); /* size */
    put_tag(pb, "stsd");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */
667 668 669 670
    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);
671
    return updateSize(pb, pos);
672 673
}

674 675 676 677 678 679 680 681 682
static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track)
{
    Time2Sample *ctts_entries;
    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;
bcoudurier's avatar
bcoudurier committed
683
    ctts_entries[0].duration = track->cluster[0].cts;
684
    for (i=1; i<track->entry; i++) {
bcoudurier's avatar
bcoudurier committed
685
        if (track->cluster[i].cts == ctts_entries[entries].duration) {
686 687 688
            ctts_entries[entries].count++; /* compress */
        } else {
            entries++;
bcoudurier's avatar
bcoudurier committed
689
            ctts_entries[entries].duration = track->cluster[i].cts;
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
            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;
}

michael's avatar
michael committed
707
/* TODO: */
708
/* Time to sample atom */
709
static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
710 711 712 713 714 715
{
    put_be32(pb, 0x18); /* size */
    put_tag(pb, "stts");
    put_be32(pb, 0); /* version & flags */
    put_be32(pb, 1); /* entry count */

716 717
    put_be32(pb, track->sampleCount); /* sample count */
    put_be32(pb, track->sampleDuration); /* sample duration */
718 719 720
    return 0x18;
}

721
static int mov_write_dref_tag(ByteIOContext *pb)
722 723 724 725 726 727 728 729 730 731 732 733 734
{
    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;
}

735
static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
736
{
737
    offset_t pos = url_ftell(pb);
738 739
    put_be32(pb, 0); /* size */
    put_tag(pb, "stbl");
740 741
    mov_write_stsd_tag(pb, track);
    mov_write_stts_tag(pb, track);
742
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
743
        track->hasKeyframes < track->entry)
744
        mov_write_stss_tag(pb, track);
745 746 747
    if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
        track->hasBframes)
        mov_write_ctts_tag(pb, track);
748 749 750 751
    mov_write_stsc_tag(pb, track);
    mov_write_stsz_tag(pb, track);
    mov_write_stco_tag(pb, track);
    return updateSize(pb, pos);
752 753
}

754
static int mov_write_dinf_tag(ByteIOContext *pb)
755
{
756
    offset_t pos = url_ftell(pb);
757 758
    put_be32(pb, 0); /* size */
    put_tag(pb, "dinf");
759 760
    mov_write_dref_tag(pb);
    return updateSize(pb, pos);
761 762
}

763
static int mov_write_smhd_tag(ByteIOContext *pb)
764 765 766 767 768 769 770 771 772
{
    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;
}

773
static int mov_write_vmhd_tag(ByteIOContext *pb)
774 775 776 777 778 779 780 781
{
    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;
}

782
static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
783
{
784
    const char *descr, *hdlr, *hdlr_type;
785
    offset_t pos = url_ftell(pb);
786

romansh's avatar
 
romansh committed
787
    if (!track) { /* no media --> data handler */
788 789 790
        hdlr = "dhlr";
        hdlr_type = "url ";
        descr = "DataHandler";
romansh's avatar
 
romansh committed
791
    } else {
792 793 794 795 796 797 798 799
        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";
        }
romansh's avatar
 
romansh committed
800
    }
801

802
    put_be32(pb, 0); /* size */
803 804
    put_tag(pb, "hdlr");
    put_be32(pb, 0); /* Version & flags */
romansh's avatar
 
romansh committed
805
    put_buffer(pb, hdlr, 4); /* handler */
romansh's avatar
 
romansh committed
806
    put_tag(pb, hdlr_type); /* handler type */
807 808 809
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
    put_be32(pb ,0); /* reserved */
romansh's avatar
 
romansh committed
810 811 812 813 814 815 816
    put_byte(pb, strlen(descr)); /* string counter */
    put_buffer(pb, descr, strlen(descr)); /* handler description */
    return updateSize(pb, pos);
}

static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track)
{
817
    offset_t pos = url_ftell(pb);
romansh's avatar
 
romansh committed
818 819
    put_be32(pb, 0); /* size */
    put_tag(pb, "minf");
820
    if(track->enc->codec_type == CODEC_TYPE_VIDEO)
romansh's avatar
 
romansh committed
821
        mov_write_vmhd_tag(pb);
822
    else
romansh's avatar
 
romansh committed
823 824 825 826 827
        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);
828
    return updateSize(pb, pos);
829 830
}

831
static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
832
{
833 834 835
    int version = track->trackDuration < INT32_MAX ? 0 : 1;

    (version == 1) ? put_be32(pb, 44) : put_be32(pb, 32); /* size */
836
    put_tag(pb, "mdhd");
837 838 839 840 841 842 843 844 845
    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 */
    }
846
    put_be32(pb, track->timescale); /* time scale (sample rate for audio) */
847
    (version == 1) ? put_be64(pb, track->trackDuration) : put_be32(pb, track->trackDuration); /* duration */
848
    put_be16(pb, track->language); /* language */
849 850 851 852
    put_be16(pb, 0); /* reserved (quality) */
    return 32;
}

853
static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track)
854
{
855
    offset_t pos = url_ftell(pb);
856 857
    put_be32(pb, 0); /* size */
    put_tag(pb, "mdia");
858 859 860 861
    mov_write_mdhd_tag(pb, track);
    mov_write_hdlr_tag(pb, track);
    mov_write_minf_tag(pb, track);
    return updateSize(pb, pos);
862 863
}

864
static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track)
865
{
866 867 868 869
    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 */
870
    put_tag(pb, "tkhd");
871 872 873 874 875 876 877 878 879
    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 */
    }
880 881
    put_be32(pb, track->trackID); /* track-id */
    put_be32(pb, 0); /* reserved */
882
    (version == 1) ? put_be64(pb, duration) : put_be32(pb, duration);
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906

    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) {
907 908 909
        double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio);
        if( !sample_aspect_ratio ) sample_aspect_ratio = 1;
        put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000);
910
        put_be32(pb, track->enc->height*0x10000);
911 912 913 914 915 916 917 918
    }
    else {
        put_be32(pb, 0);
        put_be32(pb, 0);
    }
    return 0x5c;
}

919 920 921 922 923 924 925 926 927 928
// 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);

michael's avatar
michael committed
929
    put_be32(pb, av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP)); /* duration   ... doesn't seem to effect psp */
930

931 932 933 934
    if (track->hasBframes)
        put_be32(pb, track->sampleDuration); /* first pts is 1 */
    else
        put_be32(pb, 0);
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
    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;
}

958
static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack* track)
959
{
960
    offset_t pos = url_ftell(pb);
961 962
    put_be32(pb, 0); /* size */
    put_tag(pb, "trak");
963
    mov_write_tkhd_tag(pb, track);
964
    if (track->mode == MODE_PSP || track->hasBframes)
965
        mov_write_edts_tag(pb, track);  // PSP Movies require edts box
966
    mov_write_mdia_tag(pb, track);
967
    if (track->mode == MODE_PSP)
968
        mov_write_uuid_tag_psp(pb,track);  // PSP Movies require this uuid box
969
    return updateSize(pb, pos);
970 971
}

972
#if 0
973
/* TODO: Not sorted out, but not necessary either */
974
static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov)
975 976 977 978 979 980 981 982 983 984 985
{
    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;
}
986
#endif
987

988
static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov)
989
{
990 991
    int maxTrackID = 1, i;
    int64_t maxTrackLenTemp, maxTrackLen = 0;
992
    int version;
993

bcoudurier's avatar
bcoudurier committed
994
    for (i=0; i<mov->nb_streams; i++) {
995
        if(mov->tracks[i].entry > 0) {
michael's avatar
michael committed
996
            maxTrackLenTemp = av_rescale_rnd(mov->tracks[i].trackDuration, globalTimescale, mov->tracks[i].timescale, AV_ROUND_UP);
997 998
            if(maxTrackLen < maxTrackLenTemp)
                maxTrackLen = maxTrackLenTemp;
999 1000 1001 1002
            if(maxTrackID < mov->tracks[i].trackID)
                maxTrackID = mov->tracks[i].trackID;
        }
    }
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

    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 */
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

    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;
}

1046 1047 1048
static int mov_write_itunes_hdlr_tag(ByteIOContext *pb, MOVContext* mov,
                                     AVFormatContext *s)
{
1049
    offset_t pos = url_ftell(pb);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
    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 */
michael's avatar
michael committed
1063
static int mov_write_string_data_tag(ByteIOContext *pb, const char *data, int long_style)
1064
{
michael's avatar
michael committed
1065
    if(long_style){
1066
        offset_t pos = url_ftell(pb);
1067
        put_be32(pb, 0); /* size */
michael's avatar
michael committed
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
        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;
1078 1079 1080
    }
}

1081
static int mov_write_string_tag(ByteIOContext *pb, const char *name, const char *value, int long_style){
1082
    int size = 0;
michael's avatar
michael committed
1083
    if ( value && value[0] ) {
1084
        offset_t pos = url_ftell(pb);
1085
        put_be32(pb, 0); /* size */
michael's avatar
michael committed
1086 1087 1088
        put_tag(pb, name);
        mov_write_string_data_tag(pb, value, long_style);
        size= updateSize(pb, pos);
1089 1090 1091 1092 1093
    }
    return size;
}

/* iTunes year */
michael's avatar
michael committed
1094
static int mov_write_day_tag(ByteIOContext *pb, int year, int long_style)
1095
{
michael's avatar
michael committed
1096 1097 1098 1099 1100 1101
    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;
1102 1103 1104 1105 1106 1107 1108 1109
}

/* iTunes track number */
static int mov_write_trkn_tag(ByteIOContext *pb, MOVContext* mov,
                              AVFormatContext *s)
{
    int size = 0;
    if ( s->track ) {
1110
        offset_t pos = url_ftell(pb);
1111 1112 1113
        put_be32(pb, 0); /* size */
        put_tag(pb, "trkn");
        {
1114
            offset_t pos = url_ftell(pb);
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
            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 */
static int mov_write_ilst_tag(ByteIOContext *pb, MOVContext* mov,
                              AVFormatContext *s)
{
1134
    offset_t pos = url_ftell(pb);
1135 1136
    put_be32(pb, 0); /* size */
    put_tag(pb, "ilst");
michael's avatar
michael committed
1137 1138 1139 1140 1141
    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);
1142 1143
    if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT))
        mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 1);
michael's avatar
michael committed
1144 1145
    mov_write_string_tag(pb, "\251cmt", s->comment       , 1);
    mov_write_string_tag(pb, "\251gen", s->genre         , 1);
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
    mov_write_trkn_tag(pb, mov, s);
    return updateSize(pb, pos);
}

/* iTunes meta data tag */
static int mov_write_meta_tag(ByteIOContext *pb, MOVContext* mov,
                              AVFormatContext *s)
{
    int size = 0;

    // only save meta tag if required
1157
    if ( s->title[0] || s->author[0] || s->album[0] || s->year ||
1158
         s->comment[0] || s->genre[0] || s->track ) {
1159
        offset_t pos = url_ftell(pb);
1160 1161 1162 1163 1164 1165 1166 1167 1168
        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;
}
1169

1170 1171 1172
static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov,
                              AVFormatContext *s)
{
1173
    offset_t pos = url_ftell(pb);
1174 1175 1176 1177 1178
    int i;

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

1179 1180 1181
    /* iTunes meta data */
    mov_write_meta_tag(pb, mov, s);

1182
  if(mov->mode == MODE_MOV){ // the title field breaks gtkpod with mp4 and my suspicion is that stuff isnt valid in mp4
1183
    /* Requirements */
bcoudurier's avatar
bcoudurier committed
1184
    for (i=0; i<mov->nb_streams; i++) {
1185 1186 1187
        if(mov->tracks[i].entry <= 0) continue;
        if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC ||
            mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) {
michael's avatar
michael committed
1188
            mov_write_string_tag(pb, "\251req", "QuickTime 6.0 or greater", 0);
1189 1190 1191 1192
            break;
        }
    }

michael's avatar
michael committed
1193 1194 1195 1196
    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);
1197
    if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT))
michael's avatar
michael committed
1198 1199 1200
        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);
1201
  }
1202 1203 1204 1205

    return updateSize(pb, pos);
}

1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220

static size_t ascii_to_wc (ByteIOContext *pb, char *b, size_t n)
{
    size_t i;
    unsigned char c;
    for (i = 0; i < n - 1; i++) {
        c = b[i];
        if (! (0x20 <= c && c <= 0x7f ))
            c = 0x3f;  /* '?' */
        put_be16(pb, c);
    }
    put_be16(pb, 0x00);
    return 2*n;
}

1221
static uint16_t language_code (const char *str)
1222
{
1223
    return ((((str[0]-0x60) & 0x1F)<<10) + (((str[1]-0x60) & 0x1F)<<5) + ((str[2]-0x60) & 0x1F));
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
}

static int mov_write_uuidusmt_tag (ByteIOContext *pb, AVFormatContext *s)
{
    size_t len, size;
    offset_t pos, curpos;

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

        put_be32(pb, 0); /* size placeholder*/
        put_tag(pb, "MTDT");
1244
        put_be16(pb, 4);
1245 1246
        size += 10;

1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
        // ?
        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 */
        size += 12;

        // Encoder
        len = strlen(LIBAVCODEC_IDENT)+1;
        put_be16(pb, len*2+10);             /* size */
        put_be32(pb, 0x04);                 /* type */
        put_be16(pb, language_code("eng")); /* language */
        put_be16(pb, 0x01);                 /* ? */
        ascii_to_wc(pb, LIBAVCODEC_IDENT, len);
        size += len*2+10;

1264 1265 1266 1267
        // Title
        len = strlen(s->title)+1;
        put_be16(pb, len*2+10);             /* size */
        put_be32(pb, 0x01);                 /* type */
1268
        put_be16(pb, language_code("eng")); /* language */
1269 1270 1271 1272
        put_be16(pb, 0x01);                 /* ? */
        ascii_to_wc (pb, s->title, len);
        size += len*2+10;

1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
        // Date
//        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);
        len = strlen("2006/04/01 11:11:11")+1;
        put_be16(pb, len*2+10);    /* size */
        put_be32(pb, 0x03);        /* type */
        put_be16(pb, language_code("und")); /* language */
        put_be16(pb, 0x01);        /* ? */
        ascii_to_wc (pb, "2006/04/01 11:11:11", len);
        size += len*2+10;

1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
        // size
        curpos = url_ftell(pb);
        url_fseek(pb, pos, SEEK_SET);
        put_be32(pb, size);
        url_fseek(pb, pos+24, SEEK_SET);
        put_be32(pb, size-24);
        url_fseek(pb, curpos, SEEK_SET);
    }

    return size;
}

1295 1296
static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
                              AVFormatContext *s)
1297
{
1298 1299
    int i;
    offset_t pos = url_ftell(pb);
1300 1301 1302 1303
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "moov");
    mov->timescale = globalTimescale;

bcoudurier's avatar
bcoudurier committed
1304
    for (i=0; i<mov->nb_streams; i++) {
1305 1306 1307
        if(mov->tracks[i].entry <= 0) continue;

        if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) {
michael's avatar
michael committed
1308 1309
            mov->tracks[i].timescale = mov->tracks[i].enc->time_base.den;
            mov->tracks[i].sampleDuration = mov->tracks[i].enc->time_base.num;
1310 1311 1312
        } else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) {
            mov->tracks[i].timescale = mov->tracks[i].enc->sample_rate;
            mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_size;
1313
        }
1314

1315
        mov->tracks[i].trackDuration =
1316
            (int64_t)mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration;
1317 1318
        mov->tracks[i].time = mov->time;
        mov->tracks[i].trackID = i+1;
1319 1320
    }

1321 1322
    mov_write_mvhd_tag(pb, mov);
    //mov_write_iods_tag(pb, mov);
bcoudurier's avatar
bcoudurier committed
1323
    for (i=0; i<mov->nb_streams; i++) {
1324
        if(mov->tracks[i].entry > 0) {
1325
            mov_write_trak_tag(pb, &(mov->tracks[i]));
1326 1327 1328
        }
    }

1329 1330 1331
    if (mov->mode == MODE_PSP)
        mov_write_uuidusmt_tag(pb, s);
    else
1332 1333
    mov_write_udta_tag(pb, mov, s);

1334
    return updateSize(pb, pos);
1335 1336
}

1337
static int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov)
1338
{
1339 1340 1341
    put_be32(pb, 8);    // placeholder for extended size field (64 bit)
    put_tag(pb, "wide");

1342
    mov->mdat_pos = url_ftell(pb);
1343 1344 1345 1346 1347 1348
    put_be32(pb, 0); /* size placeholder*/
    put_tag(pb, "mdat");
    return 0;
}

/* TODO: This needs to be more general */
1349
static void mov_write_ftyp_tag (ByteIOContext *pb, AVFormatContext *s)
1350
{
1351 1352
    MOVContext *mov = s->priv_data;

1353 1354
    put_be32(pb, 0x14 ); /* size */
    put_tag(pb, "ftyp");
1355

1356
    if ( mov->mode == MODE_3GP )
1357
        put_tag(pb, "3gp4");
1358 1359
    else if ( mov->mode == MODE_3G2 )
        put_tag(pb, "3g2a");
1360 1361
    else if ( mov->mode == MODE_PSP )
        put_tag(pb, "MSNV");
1362
    else if ( mov->mode == MODE_MP4 )
1363
        put_tag(pb, "isom");
1364 1365
    else
        put_tag(pb, "qt  ");
1366

1367
    put_be32(pb, 0x200 );
1368

1369
    if ( mov->mode == MODE_3GP )
1370
        put_tag(pb, "3gp4");
1371 1372
    else if ( mov->mode == MODE_3G2 )
        put_tag(pb, "3g2a");
1373 1374
    else if ( mov->mode == MODE_PSP )
        put_tag(pb, "MSNV");
1375
    else if ( mov->mode == MODE_MP4 )
1376
        put_tag(pb, "mp41");
1377 1378
    else
        put_tag(pb, "qt  ");
1379 1380
}

1381
static void mov_write_uuidprof_tag(ByteIOContext *pb, AVFormatContext *s)
1382
{
1383 1384 1385 1386
    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);
1387 1388
    int audio_kbitrate= AudioCodec->bit_rate / 1000;
    int video_kbitrate= FFMIN(VideoCodec->bit_rate / 1000, 800 - audio_kbitrate);
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409

    put_be32(pb, 0x94 ); /* size */
    put_tag(pb, "uuid");
    put_tag(pb, "PROF");

    put_be32(pb, 0x21d24fce ); /* 96 bit UUID */
    put_be32(pb, 0xbb88695c );
    put_be32(pb, 0xfac9c740 );

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

    put_be32(pb, 0x14 ); /* size */
    put_tag(pb, "FPRF");
    put_be32(pb, 0x0 );  /* ? */
    put_be32(pb, 0x0 );  /* ? */
    put_be32(pb, 0x0 );  /* ? */

    put_be32(pb, 0x2c );  /* size */
    put_tag(pb, "APRF");   /* audio */
    put_be32(pb, 0x0 );
1410
    put_be32(pb, 0x2 );   /* TrackID */
1411 1412 1413
    put_tag(pb, "mp4a");
    put_be32(pb, 0x20f );
    put_be32(pb, 0x0 );
1414 1415
    put_be32(pb, audio_kbitrate);
    put_be32(pb, audio_kbitrate);
1416 1417
    put_be32(pb, AudioRate );
    put_be32(pb, AudioCodec->channels );
1418 1419 1420 1421

    put_be32(pb, 0x34 );  /* size */
    put_tag(pb, "VPRF");   /* video */
    put_be32(pb, 0x0 );
1422
    put_be32(pb, 0x1 );    /* TrackID */
1423 1424 1425 1426 1427 1428 1429 1430 1431
    if (VideoCodec->codec_id == CODEC_ID_H264) {
        put_tag(pb, "avc1");
        put_be16(pb, 0x014D );
        put_be16(pb, 0x0015 );
    } else {
        put_tag(pb, "mp4v");
        put_be16(pb, 0x0000 );
        put_be16(pb, 0x0103 );
    }
1432
    put_be32(pb, 0x0 );
1433 1434
    put_be32(pb, video_kbitrate);
    put_be32(pb, video_kbitrate);
1435 1436 1437 1438 1439
    put_be32(pb, FrameRate);
    put_be32(pb, FrameRate);
    put_be16(pb, VideoCodec->width);
    put_be16(pb, VideoCodec->height);
    put_be32(pb, 0x010001); /* ? */
1440 1441
}

1442 1443 1444
static int mov_write_header(AVFormatContext *s)
{
    ByteIOContext *pb = &s->pb;
1445 1446
    MOVContext *mov = s->priv_data;
    int i;
1447

1448 1449 1450 1451 1452 1453 1454 1455 1456
    /* Default mode == MP4 */
    mov->mode = MODE_MP4;

    if (s->oformat != NULL) {
        if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
        else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3G2;
        else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
        else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP;

1457
        mov_write_ftyp_tag(pb,s);
1458 1459 1460 1461 1462 1463 1464 1465 1466
        if ( mov->mode == MODE_PSP ) {
            if ( s->nb_streams != 2 ) {
                av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n");
                return -1;
            }
            mov_write_uuidprof_tag(pb,s);
        }
    }

1467
    for(i=0; i<s->nb_streams; i++){
bcoudurier's avatar
bcoudurier committed
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
        AVStream *st= s->streams[i];
        MOVTrack *track= &mov->tracks[i];

        track->enc = st->codec;
        if(st->codec->codec_type == CODEC_TYPE_VIDEO){
            track->tag = mov_find_video_codec_tag(s, track);
            av_set_pts_info(st, 64, 1, st->codec->time_base.den);
        }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){
            track->tag = mov_find_audio_codec_tag(s, track);
            av_set_pts_info(st, 64, 1, st->codec->sample_rate);
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498

            switch (st->codec->codec_id) {
            case CODEC_ID_PCM_MULAW:
            case CODEC_ID_PCM_ALAW:
                track->sampleSize = 1 * st->codec->channels;
                break;
            case CODEC_ID_PCM_S16BE:
            case CODEC_ID_PCM_S16LE:
                track->sampleSize = 2 * st->codec->channels;
                break;
            case CODEC_ID_PCM_S24BE:
            case CODEC_ID_PCM_S24LE:
                track->sampleSize = 3 * st->codec->channels;
                break;
            case CODEC_ID_PCM_S32BE:
            case CODEC_ID_PCM_S32LE:
                track->sampleSize = 4 * st->codec->channels;
                break;
            default:
                track->sampleSize = 0;
            }
1499
        }
bcoudurier's avatar
bcoudurier committed
1500 1501
        track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV);
        track->mode = mov->mode;
1502 1503
    }

1504 1505
    mov_write_mdat_tag(pb, mov);
    mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
bcoudurier's avatar
bcoudurier committed
1506
    mov->nb_streams = s->nb_streams;
1507

1508 1509 1510 1511 1512
    put_flush_packet(pb);

    return 0;
}

1513
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
1514 1515 1516
{
    MOVContext *mov = s->priv_data;
    ByteIOContext *pb = &s->pb;
bcoudurier's avatar
bcoudurier committed
1517 1518
    MOVTrack *trk = &mov->tracks[pkt->stream_index];
    AVCodecContext *enc = trk->enc;
1519
    unsigned int samplesInChunk = 0;
1520
    int size= pkt->size;
1521

1522 1523
    if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */
    if (!size) return 0; /* Discard 0 sized packets */
1524

1525 1526 1527 1528 1529 1530 1531 1532 1533
    if (enc->codec_type == CODEC_ID_AMR_NB) {
        /* 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++;
1534
        }
michael's avatar
michael committed
1535 1536 1537 1538
        if(samplesInChunk > 1){
            av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, inplement a AVParser for it\n");
            return -1;
        }
1539 1540 1541
    } else if (trk->sampleSize)
        samplesInChunk = size/trk->sampleSize;
    else
1542
        samplesInChunk = 1;
1543

1544 1545
    /* copy extradata if it exists */
    if (trk->vosLen == 0 && enc->extradata_size > 0) {
1546 1547 1548 1549 1550
        trk->vosLen = enc->extradata_size;
        trk->vosData = av_malloc(trk->vosLen);
        memcpy(trk->vosData, enc->extradata, trk->vosLen);
    }

1551 1552 1553 1554 1555 1556
    if (enc->codec_id == CODEC_ID_H264 && trk->vosLen > 0 && *(uint8_t *)trk->vosData != 1) {
        /* from x264 or from bytestream h264 */
        /* nal reformating needed */
        avc_parse_nal_units(&pkt->data, &pkt->size);
        assert(pkt->size);
        size = pkt->size;
1557 1558
    }

bcoudurier's avatar
bcoudurier committed
1559 1560
    if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) {
        trk->cluster = av_realloc(trk->cluster, (trk->entry + MOV_INDEX_CLUSTER_SIZE) * sizeof(*trk->cluster));
1561 1562 1563 1564
        if (!trk->cluster)
            return -1;
    }

bcoudurier's avatar
bcoudurier committed
1565 1566 1567 1568
    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;
1569
    if(enc->codec_type == CODEC_TYPE_VIDEO) {
1570 1571
        if (pkt->dts != pkt->pts)
            trk->hasBframes = 1;
bcoudurier's avatar
bcoudurier committed
1572 1573 1574
        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)
1575
            trk->hasKeyframes++;
1576
    }
1577 1578
    trk->entry++;
    trk->sampleCount += samplesInChunk;
bcoudurier's avatar
bcoudurier committed
1579
    mov->mdat_size += size;
1580

1581
    put_buffer(pb, pkt->data, size);
1582 1583 1584 1585 1586 1587 1588 1589 1590 1591

    put_flush_packet(pb);
    return 0;
}

static int mov_write_trailer(AVFormatContext *s)
{
    MOVContext *mov = s->priv_data;
    ByteIOContext *pb = &s->pb;
    int res = 0;
bcoudurier's avatar
bcoudurier committed
1592
    int i;
1593

1594
    offset_t moov_pos = url_ftell(pb);
1595 1596

    /* Write size of mdat tag */
bcoudurier's avatar
bcoudurier committed
1597
    if (mov->mdat_size+8 <= UINT32_MAX) {
1598
        url_fseek(pb, mov->mdat_pos, SEEK_SET);
bcoudurier's avatar
bcoudurier committed
1599
        put_be32(pb, mov->mdat_size+8);
1600 1601 1602 1603 1604
    } 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");
bcoudurier's avatar
bcoudurier committed
1605
        put_be64(pb, mov->mdat_size+16);
1606
    }
1607
    url_fseek(pb, moov_pos, SEEK_SET);
1608

1609
    mov_write_moov_tag(pb, mov, s);
1610

bcoudurier's avatar
bcoudurier committed
1611
    for (i=0; i<mov->nb_streams; i++) {
bcoudurier's avatar
bcoudurier committed
1612 1613
        av_freep(&mov->tracks[i].cluster);

1614 1615
        if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData );

1616
    }
1617

1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
    put_flush_packet(pb);

    return res;
}

static AVOutputFormat mov_oformat = {
    "mov",
    "mov format",
    NULL,
    "mov",
    sizeof(MOVContext),
1629
    CODEC_ID_AAC,
1630
    CODEC_ID_MPEG4,
1631 1632 1633
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1634
    .flags = AVFMT_GLOBALHEADER,
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
};

static AVOutputFormat _3gp_oformat = {
    "3gp",
    "3gp format",
    NULL,
    "3gp",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1648
    .flags = AVFMT_GLOBALHEADER,
1649 1650 1651 1652 1653
};

static AVOutputFormat mp4_oformat = {
    "mp4",
    "mp4 format",
1654 1655
    "application/mp4",
    "mp4,m4a",
1656 1657 1658 1659 1660 1661
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1662
    .flags = AVFMT_GLOBALHEADER,
1663 1664
};

1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675
static AVOutputFormat psp_oformat = {
    "psp",
    "psp mp4 format",
    NULL,
    "mp4,psp",
    sizeof(MOVContext),
    CODEC_ID_AAC,
    CODEC_ID_MPEG4,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1676
    .flags = AVFMT_GLOBALHEADER,
1677 1678
};

1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
static AVOutputFormat _3g2_oformat = {
    "3g2",
    "3gp2 format",
    NULL,
    "3g2",
    sizeof(MOVContext),
    CODEC_ID_AMR_NB,
    CODEC_ID_H263,
    mov_write_header,
    mov_write_packet,
    mov_write_trailer,
1690
    .flags = AVFMT_GLOBALHEADER,
1691 1692
};

1693 1694 1695 1696 1697
int movenc_init(void)
{
    av_register_output_format(&mov_oformat);
    av_register_output_format(&_3gp_oformat);
    av_register_output_format(&mp4_oformat);
1698
    av_register_output_format(&psp_oformat);
1699
    av_register_output_format(&_3g2_oformat);
1700 1701
    return 0;
}