warn user if ms style codec tag is used
[libav.git] / libavformat / movenc.c
CommitLineData
1cb5f7fd
MN
1/*
2 * MOV, 3GP, MP4 encoder.
3 * Copyright (c) 2003 Thomas Raivio.
69dde1ad 4 * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>.
1cb5f7fd
MN
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include "avformat.h"
f578f938 21#include "avi.h"
1cb5f7fd 22#include "avio.h"
1cb5f7fd 23
6e6d6dc0
MN
24#undef NDEBUG
25#include <assert.h>
26
1cb5f7fd
MN
27#define MOV_INDEX_CLUSTER_SIZE 16384
28#define globalTimescale 1000
29
69dde1ad
GB
30#define MODE_MP4 0
31#define MODE_MOV 1
32#define MODE_3GP 2
33
1cb5f7fd 34typedef struct MOVIentry {
e45ccf79
GB
35 unsigned int flags, pos, size;
36 unsigned int samplesInChunk;
f578f938 37 char key_frame;
1cb5f7fd
MN
38 unsigned int entries;
39} MOVIentry;
40
41typedef struct MOVIndex {
69dde1ad 42 int mode;
1cb5f7fd 43 int entry;
1cb5f7fd 44 int mdat_size;
1cb5f7fd
MN
45 int ents_allocated;
46 long timescale;
47 long time;
f578f938 48 long trackDuration;
e45ccf79
GB
49 long sampleCount;
50 long sampleDuration;
f578f938 51 int hasKeyframes;
1cb5f7fd
MN
52 int trackID;
53 AVCodecContext *enc;
54
55 int vosLen;
6e6d6dc0 56 uint8_t *vosData;
1cb5f7fd
MN
57 MOVIentry** cluster;
58} MOVTrack;
59
60typedef struct {
69dde1ad 61 int mode;
1cb5f7fd
MN
62 long time;
63 int nb_streams;
f578f938
TR
64 int mdat_written;
65 offset_t mdat_pos;
1cb5f7fd
MN
66 long timescale;
67 MOVTrack tracks[MAX_STREAMS];
68} MOVContext;
69
e45ccf79
GB
70static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track);
71
6e6d6dc0
MN
72//FIXME supprt 64bit varaint with wide placeholders
73static int updateSize (ByteIOContext *pb, int pos)
1cb5f7fd
MN
74{
75 long curpos = url_ftell(pb);
76 url_fseek(pb, pos, SEEK_SET);
6e6d6dc0 77 put_be32(pb, curpos - pos); /* rewrite size */
1cb5f7fd 78 url_fseek(pb, curpos, SEEK_SET);
6e6d6dc0
MN
79
80 return curpos - pos;
1cb5f7fd
MN
81}
82
e45ccf79 83/* Chunk offset atom */
6e6d6dc0 84static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd
MN
85{
86 int i;
f578f938
TR
87 int pos = url_ftell(pb);
88 put_be32(pb, 0); /* size */
1cb5f7fd
MN
89 put_tag(pb, "stco");
90 put_be32(pb, 0); /* version & flags */
91 put_be32(pb, track->entry); /* entry count */
92 for (i=0; i<track->entry; i++) {
93 int cl = i / MOV_INDEX_CLUSTER_SIZE;
94 int id = i % MOV_INDEX_CLUSTER_SIZE;
95 put_be32(pb, track->cluster[cl][id].pos);
96 }
f578f938 97 return updateSize (pb, pos);
1cb5f7fd
MN
98}
99
e45ccf79 100/* Sample size atom */
6e6d6dc0 101static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 102{
f578f938 103 int equalChunks = 1;
e45ccf79 104 int i, j, entries = 0, tst = -1, oldtst = -1;
1cb5f7fd 105
f578f938
TR
106 int pos = url_ftell(pb);
107 put_be32(pb, 0); /* size */
1cb5f7fd
MN
108 put_tag(pb, "stsz");
109 put_be32(pb, 0); /* version & flags */
110
f578f938
TR
111 for (i=0; i<track->entry; i++) {
112 int cl = i / MOV_INDEX_CLUSTER_SIZE;
113 int id = i % MOV_INDEX_CLUSTER_SIZE;
e45ccf79
GB
114 tst = track->cluster[cl][id].size/track->cluster[cl][id].entries;
115 if(oldtst != -1 && tst != oldtst) {
116 equalChunks = 0;
f578f938
TR
117 }
118 oldtst = tst;
e45ccf79 119 entries += track->cluster[cl][id].entries;
f578f938 120 }
e45ccf79
GB
121 if (equalChunks) {
122 int sSize = track->cluster[0][0].size/track->cluster[0][0].entries;
f578f938 123 put_be32(pb, sSize); // sample size
e45ccf79 124 put_be32(pb, entries); // sample count
1cb5f7fd 125 }
f578f938
TR
126 else {
127 put_be32(pb, 0); // sample size
e45ccf79 128 put_be32(pb, entries); // sample count
f578f938 129 for (i=0; i<track->entry; i++) {
1cb5f7fd
MN
130 int cl = i / MOV_INDEX_CLUSTER_SIZE;
131 int id = i % MOV_INDEX_CLUSTER_SIZE;
e45ccf79
GB
132 for ( j=0; j<track->cluster[cl][id].entries; j++) {
133 put_be32(pb, track->cluster[cl][id].size /
134 track->cluster[cl][id].entries);
135 }
1cb5f7fd
MN
136 }
137 }
f578f938 138 return updateSize (pb, pos);
1cb5f7fd
MN
139}
140
e45ccf79 141/* Sample to chunk atom */
6e6d6dc0 142static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 143{
f578f938
TR
144 int index = 0, oldval = -1, i, entryPos, curpos;
145
146 int pos = url_ftell(pb);
147 put_be32(pb, 0); /* size */
1cb5f7fd
MN
148 put_tag(pb, "stsc");
149 put_be32(pb, 0); // version & flags
f578f938
TR
150 entryPos = url_ftell(pb);
151 put_be32(pb, track->entry); // entry count
152 for (i=0; i<track->entry; i++) {
153 int cl = i / MOV_INDEX_CLUSTER_SIZE;
154 int id = i % MOV_INDEX_CLUSTER_SIZE;
e45ccf79 155 if(oldval != track->cluster[cl][id].samplesInChunk)
f578f938 156 {
1cb5f7fd 157 put_be32(pb, i+1); // first chunk
e45ccf79 158 put_be32(pb, track->cluster[cl][id].samplesInChunk); // samples per chunk
1cb5f7fd 159 put_be32(pb, 0x1); // sample description index
e45ccf79 160 oldval = track->cluster[cl][id].samplesInChunk;
f578f938 161 index++;
1cb5f7fd
MN
162 }
163 }
f578f938
TR
164 curpos = url_ftell(pb);
165 url_fseek(pb, entryPos, SEEK_SET);
166 put_be32(pb, index); // rewrite size
167 url_fseek(pb, curpos, SEEK_SET);
1cb5f7fd 168
f578f938 169 return updateSize (pb, pos);
1cb5f7fd
MN
170}
171
e45ccf79 172/* Sync sample atom */
f578f938 173static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 174{
f578f938
TR
175 long curpos;
176 int i, index = 0, entryPos;
177 int pos = url_ftell(pb);
178 put_be32(pb, 0); // size
1cb5f7fd 179 put_tag(pb, "stss");
f578f938
TR
180 put_be32(pb, 0); // version & flags
181 entryPos = url_ftell(pb);
182 put_be32(pb, track->entry); // entry count
183 for (i=0; i<track->entry; i++) {
184 int cl = i / MOV_INDEX_CLUSTER_SIZE;
185 int id = i % MOV_INDEX_CLUSTER_SIZE;
186 if(track->cluster[cl][id].key_frame == 1) {
187 put_be32(pb, i+1);
188 index++;
189 }
190 }
191 curpos = url_ftell(pb);
192 url_fseek(pb, entryPos, SEEK_SET);
193 put_be32(pb, index); // rewrite size
194 url_fseek(pb, curpos, SEEK_SET);
195 return updateSize (pb, pos);
1cb5f7fd
MN
196}
197
6e6d6dc0 198static int mov_write_damr_tag(ByteIOContext *pb)
1cb5f7fd
MN
199{
200 put_be32(pb, 0x11); /* size */
201 put_tag(pb, "damr");
202 put_tag(pb, "FFMP");
203 put_byte(pb, 0);
f578f938
TR
204
205 put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */
206 put_be16(pb, 0xa); /* Mode change period (no restriction) */
207 //put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */
208 //put_be16(pb, 1); /* Mode change period (no restriction) */
1cb5f7fd
MN
209 return 0x11;
210}
211
69dde1ad
GB
212static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track)
213{
214 int pos = url_ftell(pb);
215
216 put_be32(pb, 0); /* size */
217 put_tag(pb, "wave");
218
219 put_be32(pb, 12); /* size */
220 put_tag(pb, "frma");
221 put_tag(pb, "mp4a");
222
223 put_be32(pb, 12); /* size */
224 put_tag(pb, "mp4a");
225 put_be32(pb, 0);
226
227 mov_write_esds_tag(pb, track);
228
229 put_be32(pb, 12); /* size */
230 put_tag(pb, "srcq");
231 put_be32(pb, 0x40);
232
233 put_be32(pb, 8); /* size */
234 put_be32(pb, 0); /* null tag */
235
236 return updateSize (pb, pos);
237}
238
8cc7a34d
AB
239const CodecTag codec_movaudio_tags[] = {
240 { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') },
241 { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') },
242 { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') },
243 { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') },
244 { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') },
245 { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') },
246 { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') },
247 { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') },
248 { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') },
249 { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') },
250 { 0, 0 },
251};
252
f578f938 253static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 254{
ffdd57d4
MN
255 int pos = url_ftell(pb);
256 int tag;
8cc7a34d 257
1cb5f7fd 258 put_be32(pb, 0); /* size */
f578f938 259
8cc7a34d
AB
260 tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id);
261 // if no mac fcc found, try with Microsoft tags
262 if (!tag)
263 {
264 int tmp = codec_get_tag(codec_wav_tags, track->enc->codec_id);
ffdd57d4 265 tag = MKTAG('m', 's', ((tmp >> 8) & 0xff), (tmp & 0xff));
8cc7a34d 266 }
ffdd57d4 267 put_le32(pb, tag); // store it byteswapped
f578f938 268
1cb5f7fd
MN
269 put_be32(pb, 0); /* Reserved */
270 put_be16(pb, 0); /* Reserved */
271 put_be16(pb, 1); /* Data-reference index, XXX == 1 */
69dde1ad 272
e45ccf79 273 /* SoundDescription */
69dde1ad
GB
274 if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC)
275 put_be16(pb, 1); /* Version 1 */
276 else
277 put_be16(pb, 0); /* Version 0 */
e45ccf79 278 put_be16(pb, 0); /* Revision level */
1cb5f7fd
MN
279 put_be32(pb, 0); /* Reserved */
280
f578f938
TR
281 put_be16(pb, track->enc->channels); /* Number of channels */
282 /* TODO: Currently hard-coded to 16-bit, there doesn't seem
e45ccf79 283 to be a good way to get number of bits of audio */
1cb5f7fd 284 put_be16(pb, 0x10); /* Reserved */
9a4d9388
RS
285 put_be16(pb, 0); /* compression ID (= 0) */
286 put_be16(pb, 0); /* packet size (= 0) */
1cb5f7fd
MN
287 put_be16(pb, track->timescale); /* Time scale */
288 put_be16(pb, 0); /* Reserved */
289
69dde1ad
GB
290 if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC)
291 {
292 /* SoundDescription V1 extended info */
293 put_be32(pb, track->enc->frame_size); /* Samples per packet */
294 put_be32(pb, 1536); /* Bytes per packet */
295 put_be32(pb, 2); /* Bytes per frame */
296 put_be32(pb, 2); /* Bytes per sample */
297 }
298
299 if(track->enc->codec_id == CODEC_ID_AAC) {
300 if( track->mode == MODE_MOV ) mov_write_wave_tag(pb, track);
301 else mov_write_esds_tag(pb, track);
302 }
f578f938
TR
303 if(track->enc->codec_id == CODEC_ID_AMR_NB)
304 mov_write_damr_tag(pb);
6e6d6dc0 305 return updateSize (pb, pos);
1cb5f7fd
MN
306}
307
6e6d6dc0 308static int mov_write_d263_tag(ByteIOContext *pb)
1cb5f7fd
MN
309{
310 put_be32(pb, 0xf); /* size */
311 put_tag(pb, "d263");
312 put_tag(pb, "FFMP");
313 put_be16(pb, 0x0a);
314 put_byte(pb, 0);
315 return 0xf;
316}
317
f578f938
TR
318/* TODO: No idea about these values */
319static int mov_write_svq3_tag(ByteIOContext *pb)
1cb5f7fd 320{
f578f938
TR
321 put_be32(pb, 0x15);
322 put_tag(pb, "SMI ");
323 put_tag(pb, "SEQH");
324 put_be32(pb, 0x5);
325 put_be32(pb, 0xe2c0211d);
326 put_be32(pb, 0xc0000000);
327 put_byte(pb, 0);
328 return 0x15;
1cb5f7fd
MN
329}
330
e45ccf79
GB
331static unsigned int descrLength(unsigned int len)
332{
333 if (len < 0x00000080)
334 return 2 + len;
335 else if (len < 0x00004000)
336 return 3 + len;
337 else if(len < 0x00200000)
338 return 4 + len;
339 else
340 return 5 + len;
341}
342
343static void putDescr(ByteIOContext *pb, int tag, int size)
1cb5f7fd 344{
e45ccf79
GB
345 uint32_t len;
346 uint8_t vals[4];
347
348 len = size;
349 vals[3] = (uint8_t)(len & 0x7f);
350 len >>= 7;
351 vals[2] = (uint8_t)((len & 0x7f) | 0x80);
352 len >>= 7;
353 vals[1] = (uint8_t)((len & 0x7f) | 0x80);
354 len >>= 7;
355 vals[0] = (uint8_t)((len & 0x7f) | 0x80);
356
357 put_byte(pb, tag); // DescriptorTag
358
359 if (size < 0x00000080)
360 {
361 put_byte(pb, vals[3]);
362 }
363 else if (size < 0x00004000)
364 {
365 put_byte(pb, vals[2]);
366 put_byte(pb, vals[3]);
367 }
368 else if (size < 0x00200000)
369 {
370 put_byte(pb, vals[1]);
371 put_byte(pb, vals[2]);
372 put_byte(pb, vals[3]);
373 }
374 else if (size < 0x10000000)
375 {
376 put_byte(pb, vals[0]);
377 put_byte(pb, vals[1]);
378 put_byte(pb, vals[2]);
379 put_byte(pb, vals[3]);
380 }
1cb5f7fd
MN
381}
382
6e6d6dc0 383static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
1cb5f7fd 384{
e45ccf79
GB
385 int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
386 int pos = url_ftell(pb);
387
388 put_be32(pb, 0); // size
1cb5f7fd 389 put_tag(pb, "esds");
e45ccf79 390 put_be32(pb, 0); // Version
1cb5f7fd 391
e45ccf79
GB
392 // ES descriptor
393 putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
394 descrLength(1));
395 put_be16(pb, 0x0001); // ID (= 1)
1cb5f7fd
MN
396 put_byte(pb, 0x00); // flags (= no flags)
397
e45ccf79
GB
398 // DecoderConfig descriptor
399 putDescr(pb, 0x04, 13 + decoderSpecificInfoLen);
400
401 if(track->enc->codec_id == CODEC_ID_AAC)
402 put_byte(pb, 0x40); // Object type indication
403 else if(track->enc->codec_id == CODEC_ID_MPEG4)
404 put_byte(pb, 0x20); // Object type indication (Visual 14496-2)
405
406 if(track->enc->codec_type == CODEC_TYPE_AUDIO)
407 put_byte(pb, 0x15); // flags (= Audiostream)
408 else
409 put_byte(pb, 0x11); // flags (= Visualstream)
410
1cb5f7fd
MN
411 put_byte(pb, 0x0); // Buffersize DB (24 bits)
412 put_be16(pb, 0x0dd2); // Buffersize DB
413
414 // TODO: find real values for these
415 put_be32(pb, 0x0002e918); // maxbitrate
416 put_be32(pb, 0x00017e6b); // avg bitrate
417
e45ccf79
GB
418 if (track->vosLen)
419 {
420 // DecoderSpecific info descriptor
421 putDescr(pb, 0x05, track->vosLen);
422 put_buffer(pb, track->vosData, track->vosLen);
423 }
424
425 // SL descriptor
4bfc029f 426 putDescr(pb, 0x06, 1);
1cb5f7fd 427 put_byte(pb, 0x02);
e45ccf79 428 return updateSize (pb, pos);
1cb5f7fd
MN
429}
430
8cc7a34d
AB
431const CodecTag codec_movvideo_tags[] = {
432 { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') },
433 { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') },
434 { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
435 { CODEC_ID_H263, MKTAG('s', '2', '6', '3') },
436 { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') },
437 { 0, 0 },
438};
439
f578f938 440static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 441{
ffdd57d4
MN
442 int pos = url_ftell(pb);
443 int tag;
8cc7a34d 444
f578f938 445 put_be32(pb, 0); /* size */
8cc7a34d
AB
446
447 tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id);
448 // if no mac fcc found, try with Microsoft tags
449 if (!tag)
450 tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id);
ffdd57d4 451 put_le32(pb, tag); // store it byteswapped
6e6d6dc0 452
f578f938
TR
453 put_be32(pb, 0); /* Reserved */
454 put_be16(pb, 0); /* Reserved */
455 put_be16(pb, 1); /* Data-reference index */
456
457 put_be32(pb, 0); /* Reserved (= 02000c) */
458 put_be32(pb, 0); /* Reserved ("SVis")*/
459 put_be32(pb, 0); /* Reserved */
460 put_be32(pb, 0); /* Reserved (400)*/
461 put_be16(pb, track->enc->width); /* Video width */
462 put_be16(pb, track->enc->height); /* Video height */
463 put_be32(pb, 0x00480000); /* Reserved */
464 put_be32(pb, 0x00480000); /* Reserved */
9a4d9388
RS
465 put_be32(pb, 0); /* Data size (= 0) */
466 put_be16(pb, 1); /* Frame count (= 1) */
467
f578f938
TR
468 put_be32(pb, 0); /* Reserved */
469 put_be32(pb, 0); /* Reserved */
470 put_be32(pb, 0); /* Reserved */
471 put_be32(pb, 0); /* Reserved */
472 put_be32(pb, 0); /* Reserved */
473 put_be32(pb, 0); /* Reserved */
f578f938
TR
474 put_be32(pb, 0); /* Reserved */
475 put_be32(pb, 0); /* Reserved */
9a4d9388 476
f578f938
TR
477 put_be16(pb, 0x18); /* Reserved */
478 put_be16(pb, 0xffff); /* Reserved */
479 if(track->enc->codec_id == CODEC_ID_MPEG4)
480 mov_write_esds_tag(pb, track);
481 else if(track->enc->codec_id == CODEC_ID_H263)
482 mov_write_d263_tag(pb);
483 else if(track->enc->codec_id == CODEC_ID_SVQ3)
484 mov_write_svq3_tag(pb);
485
486 return updateSize (pb, pos);
1cb5f7fd
MN
487}
488
6e6d6dc0 489static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 490{
6e6d6dc0 491 int pos = url_ftell(pb);
1cb5f7fd
MN
492 put_be32(pb, 0); /* size */
493 put_tag(pb, "stsd");
494 put_be32(pb, 0); /* version & flags */
495 put_be32(pb, 1); /* entry count */
f578f938
TR
496 if (track->enc->codec_type == CODEC_TYPE_VIDEO)
497 mov_write_video_tag(pb, track);
498 else if (track->enc->codec_type == CODEC_TYPE_AUDIO)
499 mov_write_audio_tag(pb, track);
6e6d6dc0 500 return updateSize(pb, pos);
1cb5f7fd
MN
501}
502
f578f938 503/* TODO?: Currently all samples/frames seem to have same duration */
e45ccf79 504/* Time to sample atom */
6e6d6dc0 505static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd
MN
506{
507 put_be32(pb, 0x18); /* size */
508 put_tag(pb, "stts");
509 put_be32(pb, 0); /* version & flags */
510 put_be32(pb, 1); /* entry count */
511
e45ccf79
GB
512 put_be32(pb, track->sampleCount); /* sample count */
513 put_be32(pb, track->sampleDuration); /* sample duration */
1cb5f7fd
MN
514 return 0x18;
515}
516
6e6d6dc0 517static int mov_write_dref_tag(ByteIOContext *pb)
1cb5f7fd
MN
518{
519 put_be32(pb, 28); /* size */
520 put_tag(pb, "dref");
521 put_be32(pb, 0); /* version & flags */
522 put_be32(pb, 1); /* entry count */
523
524 put_be32(pb, 0xc); /* size */
525 put_tag(pb, "url ");
526 put_be32(pb, 1); /* version & flags */
527
528 return 28;
529}
530
6e6d6dc0 531static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 532{
6e6d6dc0 533 int pos = url_ftell(pb);
1cb5f7fd
MN
534 put_be32(pb, 0); /* size */
535 put_tag(pb, "stbl");
6e6d6dc0
MN
536 mov_write_stsd_tag(pb, track);
537 mov_write_stts_tag(pb, track);
f578f938
TR
538 if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
539 track->hasKeyframes)
540 mov_write_stss_tag(pb, track);
6e6d6dc0
MN
541 mov_write_stsc_tag(pb, track);
542 mov_write_stsz_tag(pb, track);
543 mov_write_stco_tag(pb, track);
544 return updateSize(pb, pos);
1cb5f7fd
MN
545}
546
6e6d6dc0 547static int mov_write_dinf_tag(ByteIOContext *pb)
1cb5f7fd 548{
6e6d6dc0 549 int pos = url_ftell(pb);
1cb5f7fd
MN
550 put_be32(pb, 0); /* size */
551 put_tag(pb, "dinf");
6e6d6dc0
MN
552 mov_write_dref_tag(pb);
553 return updateSize(pb, pos);
1cb5f7fd
MN
554}
555
6e6d6dc0 556static int mov_write_smhd_tag(ByteIOContext *pb)
1cb5f7fd
MN
557{
558 put_be32(pb, 16); /* size */
559 put_tag(pb, "smhd");
560 put_be32(pb, 0); /* version & flags */
561 put_be16(pb, 0); /* reserved (balance, normally = 0) */
562 put_be16(pb, 0); /* reserved */
563 return 16;
564}
565
6e6d6dc0 566static int mov_write_vmhd_tag(ByteIOContext *pb)
1cb5f7fd
MN
567{
568 put_be32(pb, 0x14); /* size (always 0x14) */
569 put_tag(pb, "vmhd");
570 put_be32(pb, 0x01); /* version & flags */
571 put_be64(pb, 0); /* reserved (graphics mode = copy) */
572 return 0x14;
573}
574
6e6d6dc0 575static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 576{
9a4d9388 577 char *descr, *hdlr, *hdlr_type;
f578f938 578 int pos = url_ftell(pb);
9a4d9388
RS
579
580 if (!track) { /* no media --> data handler */
581 hdlr = "dhlr";
582 hdlr_type = "url ";
583 descr = "DataHandler";
584 } else {
585 hdlr = (track->mode == MODE_MOV) ? "mhlr" : "\0\0\0\0";
586 if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
587 hdlr_type = "vide";
588 descr = "VideoHandler";
589 } else {
590 hdlr_type = "soun";
591 descr = "SoundHandler";
592 }
593 }
594
f578f938 595 put_be32(pb, 0); /* size */
1cb5f7fd
MN
596 put_tag(pb, "hdlr");
597 put_be32(pb, 0); /* Version & flags */
906b578f 598 put_buffer(pb, hdlr, 4); /* handler */
9a4d9388 599 put_tag(pb, hdlr_type); /* handler type */
f578f938
TR
600 put_be32(pb ,0); /* reserved */
601 put_be32(pb ,0); /* reserved */
602 put_be32(pb ,0); /* reserved */
9a4d9388
RS
603 put_byte(pb, strlen(descr)); /* string counter */
604 put_buffer(pb, descr, strlen(descr)); /* handler description */
605 return updateSize(pb, pos);
606}
607
608static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track)
609{
610 int pos = url_ftell(pb);
611 put_be32(pb, 0); /* size */
612 put_tag(pb, "minf");
1cb5f7fd 613 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
9a4d9388 614 mov_write_vmhd_tag(pb);
1cb5f7fd 615 else
9a4d9388
RS
616 mov_write_smhd_tag(pb);
617 if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */
618 mov_write_hdlr_tag(pb, NULL);
619 mov_write_dinf_tag(pb);
620 mov_write_stbl_tag(pb, track);
f578f938 621 return updateSize(pb, pos);
1cb5f7fd
MN
622}
623
6e6d6dc0 624static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd
MN
625{
626 put_be32(pb, 32); /* size */
627 put_tag(pb, "mdhd");
628 put_be32(pb, 0); /* Version & flags */
629 put_be32(pb, track->time); /* creation time */
630 put_be32(pb, track->time); /* modification time */
e45ccf79
GB
631 put_be32(pb, track->timescale); /* time scale (sample rate for audio) */
632 put_be32(pb, track->trackDuration); /* duration */
1cb5f7fd
MN
633 put_be16(pb, 0); /* language, 0 = english */
634 put_be16(pb, 0); /* reserved (quality) */
635 return 32;
636}
637
6e6d6dc0 638static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 639{
6e6d6dc0 640 int pos = url_ftell(pb);
1cb5f7fd
MN
641 put_be32(pb, 0); /* size */
642 put_tag(pb, "mdia");
6e6d6dc0
MN
643 mov_write_mdhd_tag(pb, track);
644 mov_write_hdlr_tag(pb, track);
645 mov_write_minf_tag(pb, track);
646 return updateSize(pb, pos);
1cb5f7fd
MN
647}
648
6e6d6dc0 649static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 650{
f578f938 651 int64_t maxTrackLenTemp;
1cb5f7fd
MN
652 put_be32(pb, 0x5c); /* size (always 0x5c) */
653 put_tag(pb, "tkhd");
f578f938 654 put_be32(pb, 0xf); /* version & flags (track enabled) */
1cb5f7fd
MN
655 put_be32(pb, track->time); /* creation time */
656 put_be32(pb, track->time); /* modification time */
657 put_be32(pb, track->trackID); /* track-id */
658 put_be32(pb, 0); /* reserved */
f578f938
TR
659 maxTrackLenTemp = ((int64_t)globalTimescale*(int64_t)track->trackDuration)/(int64_t)track->timescale;
660 put_be32(pb, (long)maxTrackLenTemp); /* duration */
1cb5f7fd
MN
661
662 put_be32(pb, 0); /* reserved */
663 put_be32(pb, 0); /* reserved */
664 put_be32(pb, 0x0); /* reserved (Layer & Alternate group) */
665 /* Volume, only for audio */
666 if(track->enc->codec_type == CODEC_TYPE_AUDIO)
667 put_be16(pb, 0x0100);
668 else
669 put_be16(pb, 0);
670 put_be16(pb, 0); /* reserved */
671
672 /* Matrix structure */
673 put_be32(pb, 0x00010000); /* reserved */
674 put_be32(pb, 0x0); /* reserved */
675 put_be32(pb, 0x0); /* reserved */
676 put_be32(pb, 0x0); /* reserved */
677 put_be32(pb, 0x00010000); /* reserved */
678 put_be32(pb, 0x0); /* reserved */
679 put_be32(pb, 0x0); /* reserved */
680 put_be32(pb, 0x0); /* reserved */
681 put_be32(pb, 0x40000000); /* reserved */
682
683 /* Track width and height, for visual only */
684 if(track->enc->codec_type == CODEC_TYPE_VIDEO) {
69dde1ad
GB
685 double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio);
686 if( !sample_aspect_ratio ) sample_aspect_ratio = 1;
687 put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000);
f578f938 688 put_be32(pb, track->enc->height*0x10000);
1cb5f7fd
MN
689 }
690 else {
691 put_be32(pb, 0);
692 put_be32(pb, 0);
693 }
694 return 0x5c;
695}
696
6e6d6dc0 697static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 698{
6e6d6dc0 699 int pos = url_ftell(pb);
1cb5f7fd
MN
700 put_be32(pb, 0); /* size */
701 put_tag(pb, "trak");
6e6d6dc0
MN
702 mov_write_tkhd_tag(pb, track);
703 mov_write_mdia_tag(pb, track);
704 return updateSize(pb, pos);
1cb5f7fd
MN
705}
706
707/* TODO: Not sorted out, but not necessary either */
6e6d6dc0 708static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov)
1cb5f7fd
MN
709{
710 put_be32(pb, 0x15); /* size */
711 put_tag(pb, "iods");
712 put_be32(pb, 0); /* version & flags */
713 put_be16(pb, 0x1007);
714 put_byte(pb, 0);
715 put_be16(pb, 0x4fff);
716 put_be16(pb, 0xfffe);
717 put_be16(pb, 0x01ff);
718 return 0x15;
719}
720
6e6d6dc0 721static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov)
1cb5f7fd
MN
722{
723 int maxTrackID = 1, maxTrackLen = 0, i;
f578f938 724 int64_t maxTrackLenTemp;
1cb5f7fd
MN
725
726 put_be32(pb, 0x6c); /* size (always 0x6c) */
727 put_tag(pb, "mvhd");
728 put_be32(pb, 0); /* version & flags */
729 put_be32(pb, mov->time); /* creation time */
730 put_be32(pb, mov->time); /* modification time */
731 put_be32(pb, mov->timescale); /* timescale */
732 for (i=0; i<MAX_STREAMS; i++) {
733 if(mov->tracks[i].entry > 0) {
f578f938
TR
734 maxTrackLenTemp = ((int64_t)globalTimescale*(int64_t)mov->tracks[i].trackDuration)/(int64_t)mov->tracks[i].timescale;
735 if(maxTrackLen < maxTrackLenTemp)
736 maxTrackLen = maxTrackLenTemp;
1cb5f7fd
MN
737 if(maxTrackID < mov->tracks[i].trackID)
738 maxTrackID = mov->tracks[i].trackID;
739 }
740 }
741 put_be32(pb, maxTrackLen); /* duration of longest track */
742
743 put_be32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */
744 put_be16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */
745 put_be16(pb, 0); /* reserved */
746 put_be32(pb, 0); /* reserved */
747 put_be32(pb, 0); /* reserved */
748
749 /* Matrix structure */
750 put_be32(pb, 0x00010000); /* reserved */
751 put_be32(pb, 0x0); /* reserved */
752 put_be32(pb, 0x0); /* reserved */
753 put_be32(pb, 0x0); /* reserved */
754 put_be32(pb, 0x00010000); /* reserved */
755 put_be32(pb, 0x0); /* reserved */
756 put_be32(pb, 0x0); /* reserved */
757 put_be32(pb, 0x0); /* reserved */
758 put_be32(pb, 0x40000000); /* reserved */
759
760 put_be32(pb, 0); /* reserved (preview time) */
761 put_be32(pb, 0); /* reserved (preview duration) */
762 put_be32(pb, 0); /* reserved (poster time) */
763 put_be32(pb, 0); /* reserved (selection time) */
764 put_be32(pb, 0); /* reserved (selection duration) */
765 put_be32(pb, 0); /* reserved (current time) */
766 put_be32(pb, maxTrackID+1); /* Next track id */
767 return 0x6c;
768}
769
69dde1ad
GB
770static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov,
771 AVFormatContext *s)
772{
773 int pos = url_ftell(pb);
774 int i;
775
776 put_be32(pb, 0); /* size */
777 put_tag(pb, "udta");
778
779 /* Requirements */
780 for (i=0; i<MAX_STREAMS; i++) {
781 if(mov->tracks[i].entry <= 0) continue;
782 if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC ||
783 mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) {
784 int pos = url_ftell(pb);
785 put_be32(pb, 0); /* size */
786 put_tag(pb, "\251req");
787 put_be16(pb, sizeof("QuickTime 6.0 or greater") - 1);
788 put_be16(pb, 0);
789 put_buffer(pb, "QuickTime 6.0 or greater",
790 sizeof("QuickTime 6.0 or greater") - 1);
791 updateSize(pb, pos);
792 break;
793 }
794 }
795
796 /* Encoder */
75553f82 797 if(!(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT))
69dde1ad
GB
798 {
799 int pos = url_ftell(pb);
800 put_be32(pb, 0); /* size */
801 put_tag(pb, "\251enc");
802 put_be16(pb, sizeof(LIBAVFORMAT_IDENT) - 1); /* string length */
803 put_be16(pb, 0);
804 put_buffer(pb, LIBAVFORMAT_IDENT, sizeof(LIBAVFORMAT_IDENT) - 1);
805 updateSize(pb, pos);
806 }
807
808 if( s->title[0] )
809 {
810 int pos = url_ftell(pb);
811 put_be32(pb, 0); /* size */
812 put_tag(pb, "\251nam");
813 put_be16(pb, strlen(s->title)); /* string length */
814 put_be16(pb, 0);
815 put_buffer(pb, s->title, strlen(s->title));
816 updateSize(pb, pos);
817 }
818
819 if( s->author[0] )
820 {
821 int pos = url_ftell(pb);
822 put_be32(pb, 0); /* size */
823 put_tag(pb, /*"\251aut"*/ "\251day" );
824 put_be16(pb, strlen(s->author)); /* string length */
825 put_be16(pb, 0);
826 put_buffer(pb, s->author, strlen(s->author));
827 updateSize(pb, pos);
828 }
829
830 if( s->comment[0] )
831 {
832 int pos = url_ftell(pb);
833 put_be32(pb, 0); /* size */
834 put_tag(pb, "\251des");
835 put_be16(pb, strlen(s->comment)); /* string length */
836 put_be16(pb, 0);
837 put_buffer(pb, s->comment, strlen(s->comment));
838 updateSize(pb, pos);
839 }
840
841 return updateSize(pb, pos);
842}
843
844static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
845 AVFormatContext *s)
1cb5f7fd 846{
6e6d6dc0 847 int pos, i;
1cb5f7fd
MN
848 pos = url_ftell(pb);
849 put_be32(pb, 0); /* size placeholder*/
850 put_tag(pb, "moov");
851 mov->timescale = globalTimescale;
852
853 for (i=0; i<MAX_STREAMS; i++) {
e45ccf79
GB
854 if(mov->tracks[i].entry <= 0) continue;
855
856 if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) {
857 mov->tracks[i].timescale = mov->tracks[i].enc->frame_rate;
858 mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_rate_base;
859 }
860 else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) {
861 /* If AMR, track timescale = 8000, AMR_WB = 16000 */
862 if(mov->tracks[i].enc->codec_id == CODEC_ID_AMR_NB) {
863 mov->tracks[i].sampleDuration = 160; // Bytes per chunk
864 mov->tracks[i].timescale = 8000;
1cb5f7fd 865 }
e45ccf79
GB
866 else {
867 mov->tracks[i].timescale = mov->tracks[i].enc->sample_rate;
868 mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_size;
1cb5f7fd 869 }
1cb5f7fd 870 }
e45ccf79
GB
871
872 mov->tracks[i].trackDuration =
873 mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration;
874 mov->tracks[i].time = mov->time;
875 mov->tracks[i].trackID = i+1;
1cb5f7fd
MN
876 }
877
6e6d6dc0
MN
878 mov_write_mvhd_tag(pb, mov);
879 //mov_write_iods_tag(pb, mov);
1cb5f7fd
MN
880 for (i=0; i<MAX_STREAMS; i++) {
881 if(mov->tracks[i].entry > 0) {
6e6d6dc0 882 mov_write_trak_tag(pb, &(mov->tracks[i]));
1cb5f7fd
MN
883 }
884 }
885
69dde1ad
GB
886 mov_write_udta_tag(pb, mov, s);
887
6e6d6dc0 888 return updateSize(pb, pos);
1cb5f7fd
MN
889}
890
f578f938 891int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov)
1cb5f7fd 892{
f578f938 893 mov->mdat_pos = url_ftell(pb);
1cb5f7fd
MN
894 put_be32(pb, 0); /* size placeholder*/
895 put_tag(pb, "mdat");
896 return 0;
897}
898
899/* TODO: This needs to be more general */
e45ccf79 900int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s)
1cb5f7fd 901{
69dde1ad
GB
902 MOVContext *mov = s->priv_data;
903
1cb5f7fd
MN
904 put_be32(pb, 0x14 ); /* size */
905 put_tag(pb, "ftyp");
e45ccf79 906
69dde1ad 907 if ( mov->mode == MODE_3GP )
e45ccf79
GB
908 put_tag(pb, "3gp4");
909 else
910 put_tag(pb, "isom");
911
1cb5f7fd 912 put_be32(pb, 0x200 );
e45ccf79 913
69dde1ad 914 if ( mov->mode == MODE_3GP )
e45ccf79
GB
915 put_tag(pb, "3gp4");
916 else
917 put_tag(pb, "mp41");
918
1cb5f7fd
MN
919 return 0x14;
920}
921
922static int mov_write_header(AVFormatContext *s)
923{
924 ByteIOContext *pb = &s->pb;
69dde1ad
GB
925 MOVContext *mov = s->priv_data;
926 int i;
1cb5f7fd 927
ffdd57d4
MN
928 for(i=0; i<s->nb_streams; i++){
929 AVCodecContext *c= &s->streams[i]->codec;
930
931 if (c->codec_type == CODEC_TYPE_VIDEO){
932 if (!codec_get_tag(codec_movvideo_tags, c->codec_id)){
933 if(!codec_get_tag(codec_bmp_tags, c->codec_id))
934 return -1;
935 else
936 av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n");
937 }
938 }else if(c->codec_type == CODEC_TYPE_AUDIO){
939 if (!codec_get_tag(codec_movaudio_tags, c->codec_id)){
940 if(!codec_get_tag(codec_wav_tags, c->codec_id))
941 return -1;
942 else
943 av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n");
944 }
945 }
946 }
947
69dde1ad
GB
948 /* Default mode == MP4 */
949 mov->mode = MODE_MP4;
950
951 if (s->oformat != NULL) {
952 if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
953 else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
954
955 if ( mov->mode == MODE_3GP || mov->mode == MODE_MP4 )
956 mov_write_ftyp_tag(pb,s);
957 }
958
959 for (i=0; i<MAX_STREAMS; i++) {
960 mov->tracks[i].mode = mov->mode;
f578f938
TR
961 }
962
1cb5f7fd
MN
963 put_flush_packet(pb);
964
965 return 0;
966}
967
e928649b 968static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
1cb5f7fd
MN
969{
970 MOVContext *mov = s->priv_data;
971 ByteIOContext *pb = &s->pb;
e928649b
MN
972 AVCodecContext *enc = &s->streams[pkt->stream_index]->codec;
973 MOVTrack* trk = &mov->tracks[pkt->stream_index];
1a31840c 974 int cl, id;
e45ccf79 975 unsigned int samplesInChunk = 0;
e928649b 976 int size= pkt->size;
1cb5f7fd 977
e45ccf79
GB
978 if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */
979 if (!size) return 0; /* Discard 0 sized packets */
1cb5f7fd 980
e45ccf79
GB
981 if (enc->codec_type == CODEC_TYPE_VIDEO ) {
982 samplesInChunk = 1;
983 }
984 else if (enc->codec_type == CODEC_TYPE_AUDIO ) {
985 if( enc->codec_id == CODEC_ID_AMR_NB) {
f578f938 986 /* We must find out how many AMR blocks there are in one packet */
e45ccf79
GB
987 static uint16_t packed_size[16] =
988 {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0};
989 int len = 0;
990
991 while (len < size && samplesInChunk < 100) {
e928649b 992 len += packed_size[(pkt->data[len] >> 3) & 0x0F];
e45ccf79 993 samplesInChunk++;
f578f938 994 }
1cb5f7fd 995 }
e45ccf79
GB
996 else if(enc->codec_id == CODEC_ID_PCM_ALAW) {
997 samplesInChunk = size/enc->channels;
1cb5f7fd 998 }
9a4d9388
RS
999 else if(enc->codec_id == CODEC_ID_PCM_S16BE || enc->codec_id == CODEC_ID_PCM_S16LE) {
1000 samplesInChunk = size/(2*enc->channels);
1001 }
e45ccf79
GB
1002 else {
1003 samplesInChunk = 1;
1cb5f7fd 1004 }
e45ccf79
GB
1005 }
1006
1007 if ((enc->codec_id == CODEC_ID_MPEG4 || enc->codec_id == CODEC_ID_AAC)
1008 && trk->vosLen == 0) {
21f52609 1009// assert(enc->extradata_size);
e45ccf79
GB
1010
1011 trk->vosLen = enc->extradata_size;
1012 trk->vosData = av_malloc(trk->vosLen);
1013 memcpy(trk->vosData, enc->extradata, trk->vosLen);
1014 }
1015
1016 cl = trk->entry / MOV_INDEX_CLUSTER_SIZE;
1017 id = trk->entry % MOV_INDEX_CLUSTER_SIZE;
1018
1019 if (trk->ents_allocated <= trk->entry) {
1020 trk->cluster = av_realloc(trk->cluster, (cl+1)*sizeof(void*));
1021 if (!trk->cluster)
1022 return -1;
1023 trk->cluster[cl] = av_malloc(MOV_INDEX_CLUSTER_SIZE*sizeof(MOVIentry));
1024 if (!trk->cluster[cl])
1025 return -1;
1026 trk->ents_allocated += MOV_INDEX_CLUSTER_SIZE;
1027 }
1028 if (mov->mdat_written == 0) {
1029 mov_write_mdat_tag(pb, mov);
1030 mov->mdat_written = 1;
21f52609 1031 mov->time = s->timestamp;
e45ccf79
GB
1032 }
1033
69dde1ad 1034 trk->cluster[cl][id].pos = url_ftell(pb);
e45ccf79
GB
1035 trk->cluster[cl][id].samplesInChunk = samplesInChunk;
1036 trk->cluster[cl][id].size = size;
1037 trk->cluster[cl][id].entries = samplesInChunk;
1038 if(enc->codec_type == CODEC_TYPE_VIDEO) {
e928649b
MN
1039 trk->cluster[cl][id].key_frame = !!(pkt->flags & PKT_FLAG_KEY);
1040 if(trk->cluster[cl][id].key_frame)
f578f938 1041 trk->hasKeyframes = 1;
1cb5f7fd 1042 }
e45ccf79
GB
1043 trk->enc = enc;
1044 trk->entry++;
1045 trk->sampleCount += samplesInChunk;
1046 trk->mdat_size += size;
1047
e928649b 1048 put_buffer(pb, pkt->data, size);
1cb5f7fd
MN
1049
1050 put_flush_packet(pb);
1051 return 0;
1052}
1053
1054static int mov_write_trailer(AVFormatContext *s)
1055{
1056 MOVContext *mov = s->priv_data;
1057 ByteIOContext *pb = &s->pb;
1058 int res = 0;
1059 int i, j;
1cb5f7fd 1060
69dde1ad 1061 offset_t moov_pos = url_ftell(pb);
1cb5f7fd
MN
1062
1063 /* Write size of mdat tag */
69dde1ad 1064 for (i=0, j=0; i<MAX_STREAMS; i++) {
1cb5f7fd
MN
1065 if(mov->tracks[i].ents_allocated > 0) {
1066 j += mov->tracks[i].mdat_size;
1067 }
1068 }
f578f938 1069 url_fseek(pb, mov->mdat_pos, SEEK_SET);
1cb5f7fd 1070 put_be32(pb, j+8);
69dde1ad 1071 url_fseek(pb, moov_pos, SEEK_SET);
1cb5f7fd 1072
69dde1ad 1073 mov_write_moov_tag(pb, mov, s);
1cb5f7fd
MN
1074
1075 for (i=0; i<MAX_STREAMS; i++) {
1076 for (j=0; j<mov->tracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) {
1077 av_free(mov->tracks[i].cluster[j]);
1078 }
1079 av_free(mov->tracks[i].cluster);
ec7d0d2e
GB
1080 if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData );
1081
1cb5f7fd
MN
1082 mov->tracks[i].cluster = NULL;
1083 mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0;
1084 }
69dde1ad 1085
1cb5f7fd
MN
1086 put_flush_packet(pb);
1087
1088 return res;
1089}
1090
1091static AVOutputFormat mov_oformat = {
1092 "mov",
1093 "mov format",
1094 NULL,
1095 "mov",
1096 sizeof(MOVContext),
69dde1ad 1097 CODEC_ID_AAC,
2187d948 1098 CODEC_ID_MPEG4,
1cb5f7fd
MN
1099 mov_write_header,
1100 mov_write_packet,
1101 mov_write_trailer,
1102};
1103
1104static AVOutputFormat _3gp_oformat = {
1105 "3gp",
1106 "3gp format",
1107 NULL,
1108 "3gp",
1109 sizeof(MOVContext),
1110 CODEC_ID_AMR_NB,
1111 CODEC_ID_H263,
1112 mov_write_header,
1113 mov_write_packet,
1114 mov_write_trailer,
1115};
1116
1117static AVOutputFormat mp4_oformat = {
1118 "mp4",
1119 "mp4 format",
4cb3f3b6
DC
1120 "application/mp4",
1121 "mp4,m4a",
1cb5f7fd
MN
1122 sizeof(MOVContext),
1123 CODEC_ID_AAC,
1124 CODEC_ID_MPEG4,
1125 mov_write_header,
1126 mov_write_packet,
1127 mov_write_trailer,
1128};
1129
1130int movenc_init(void)
1131{
1132 av_register_output_format(&mov_oformat);
1133 av_register_output_format(&_3gp_oformat);
1134 av_register_output_format(&mp4_oformat);
1135 return 0;
1136}