diff -w
[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
f578f938 239static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 240{
6e6d6dc0 241 int pos = url_ftell(pb);
1cb5f7fd 242 put_be32(pb, 0); /* size */
f578f938
TR
243
244 if(track->enc->codec_id == CODEC_ID_PCM_MULAW)
245 put_tag(pb, "ulaw");
246 else if(track->enc->codec_id == CODEC_ID_PCM_ALAW)
247 put_tag(pb, "alaw");
248 else if(track->enc->codec_id == CODEC_ID_ADPCM_IMA_QT)
249 put_tag(pb, "ima4");
250 else if(track->enc->codec_id == CODEC_ID_MACE3)
251 put_tag(pb, "MAC3");
252 else if(track->enc->codec_id == CODEC_ID_MACE6)
253 put_tag(pb, "MAC6");
254 else if(track->enc->codec_id == CODEC_ID_AAC)
255 put_tag(pb, "mp4a");
256 else if(track->enc->codec_id == CODEC_ID_AMR_NB)
257 put_tag(pb, "samr");
ca20f118
RS
258 else if(track->enc->codec_id == CODEC_ID_PCM_S16BE)
259 put_tag(pb, "twos");
260 else if(track->enc->codec_id == CODEC_ID_PCM_S16LE)
261 put_tag(pb, "sowt");
f578f938
TR
262 else
263 put_tag(pb, " ");
264
1cb5f7fd
MN
265 put_be32(pb, 0); /* Reserved */
266 put_be16(pb, 0); /* Reserved */
267 put_be16(pb, 1); /* Data-reference index, XXX == 1 */
69dde1ad 268
e45ccf79 269 /* SoundDescription */
69dde1ad
GB
270 if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC)
271 put_be16(pb, 1); /* Version 1 */
272 else
273 put_be16(pb, 0); /* Version 0 */
e45ccf79 274 put_be16(pb, 0); /* Revision level */
1cb5f7fd
MN
275 put_be32(pb, 0); /* Reserved */
276
f578f938
TR
277 put_be16(pb, track->enc->channels); /* Number of channels */
278 /* TODO: Currently hard-coded to 16-bit, there doesn't seem
e45ccf79 279 to be a good way to get number of bits of audio */
1cb5f7fd 280 put_be16(pb, 0x10); /* Reserved */
69dde1ad
GB
281 put_be16(pb, 0xfffe); /* compression ID (= 0) */
282 put_be16(pb, 0xac); /* packet size (= 0) */
1cb5f7fd
MN
283 put_be16(pb, track->timescale); /* Time scale */
284 put_be16(pb, 0); /* Reserved */
285
69dde1ad
GB
286 if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC)
287 {
288 /* SoundDescription V1 extended info */
289 put_be32(pb, track->enc->frame_size); /* Samples per packet */
290 put_be32(pb, 1536); /* Bytes per packet */
291 put_be32(pb, 2); /* Bytes per frame */
292 put_be32(pb, 2); /* Bytes per sample */
293 }
294
295 if(track->enc->codec_id == CODEC_ID_AAC) {
296 if( track->mode == MODE_MOV ) mov_write_wave_tag(pb, track);
297 else mov_write_esds_tag(pb, track);
298 }
f578f938
TR
299 if(track->enc->codec_id == CODEC_ID_AMR_NB)
300 mov_write_damr_tag(pb);
6e6d6dc0 301 return updateSize (pb, pos);
1cb5f7fd
MN
302}
303
6e6d6dc0 304static int mov_write_d263_tag(ByteIOContext *pb)
1cb5f7fd
MN
305{
306 put_be32(pb, 0xf); /* size */
307 put_tag(pb, "d263");
308 put_tag(pb, "FFMP");
309 put_be16(pb, 0x0a);
310 put_byte(pb, 0);
311 return 0xf;
312}
313
f578f938
TR
314/* TODO: No idea about these values */
315static int mov_write_svq3_tag(ByteIOContext *pb)
1cb5f7fd 316{
f578f938
TR
317 put_be32(pb, 0x15);
318 put_tag(pb, "SMI ");
319 put_tag(pb, "SEQH");
320 put_be32(pb, 0x5);
321 put_be32(pb, 0xe2c0211d);
322 put_be32(pb, 0xc0000000);
323 put_byte(pb, 0);
324 return 0x15;
1cb5f7fd
MN
325}
326
e45ccf79
GB
327static unsigned int descrLength(unsigned int len)
328{
329 if (len < 0x00000080)
330 return 2 + len;
331 else if (len < 0x00004000)
332 return 3 + len;
333 else if(len < 0x00200000)
334 return 4 + len;
335 else
336 return 5 + len;
337}
338
339static void putDescr(ByteIOContext *pb, int tag, int size)
1cb5f7fd 340{
e45ccf79
GB
341 uint32_t len;
342 uint8_t vals[4];
343
344 len = size;
345 vals[3] = (uint8_t)(len & 0x7f);
346 len >>= 7;
347 vals[2] = (uint8_t)((len & 0x7f) | 0x80);
348 len >>= 7;
349 vals[1] = (uint8_t)((len & 0x7f) | 0x80);
350 len >>= 7;
351 vals[0] = (uint8_t)((len & 0x7f) | 0x80);
352
353 put_byte(pb, tag); // DescriptorTag
354
355 if (size < 0x00000080)
356 {
357 put_byte(pb, vals[3]);
358 }
359 else if (size < 0x00004000)
360 {
361 put_byte(pb, vals[2]);
362 put_byte(pb, vals[3]);
363 }
364 else if (size < 0x00200000)
365 {
366 put_byte(pb, vals[1]);
367 put_byte(pb, vals[2]);
368 put_byte(pb, vals[3]);
369 }
370 else if (size < 0x10000000)
371 {
372 put_byte(pb, vals[0]);
373 put_byte(pb, vals[1]);
374 put_byte(pb, vals[2]);
375 put_byte(pb, vals[3]);
376 }
1cb5f7fd
MN
377}
378
6e6d6dc0 379static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
1cb5f7fd 380{
e45ccf79
GB
381 int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
382 int pos = url_ftell(pb);
383
384 put_be32(pb, 0); // size
1cb5f7fd 385 put_tag(pb, "esds");
e45ccf79 386 put_be32(pb, 0); // Version
1cb5f7fd 387
e45ccf79
GB
388 // ES descriptor
389 putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
390 descrLength(1));
391 put_be16(pb, 0x0001); // ID (= 1)
1cb5f7fd
MN
392 put_byte(pb, 0x00); // flags (= no flags)
393
e45ccf79
GB
394 // DecoderConfig descriptor
395 putDescr(pb, 0x04, 13 + decoderSpecificInfoLen);
396
397 if(track->enc->codec_id == CODEC_ID_AAC)
398 put_byte(pb, 0x40); // Object type indication
399 else if(track->enc->codec_id == CODEC_ID_MPEG4)
400 put_byte(pb, 0x20); // Object type indication (Visual 14496-2)
401
402 if(track->enc->codec_type == CODEC_TYPE_AUDIO)
403 put_byte(pb, 0x15); // flags (= Audiostream)
404 else
405 put_byte(pb, 0x11); // flags (= Visualstream)
406
1cb5f7fd
MN
407 put_byte(pb, 0x0); // Buffersize DB (24 bits)
408 put_be16(pb, 0x0dd2); // Buffersize DB
409
410 // TODO: find real values for these
411 put_be32(pb, 0x0002e918); // maxbitrate
412 put_be32(pb, 0x00017e6b); // avg bitrate
413
e45ccf79
GB
414 if (track->vosLen)
415 {
416 // DecoderSpecific info descriptor
417 putDescr(pb, 0x05, track->vosLen);
418 put_buffer(pb, track->vosData, track->vosLen);
419 }
420
421 // SL descriptor
4bfc029f 422 putDescr(pb, 0x06, 1);
1cb5f7fd 423 put_byte(pb, 0x02);
e45ccf79 424 return updateSize (pb, pos);
1cb5f7fd
MN
425}
426
f578f938 427static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 428{
6e6d6dc0 429 int pos = url_ftell(pb);
f578f938
TR
430 put_be32(pb, 0); /* size */
431 if(track->enc->codec_id == CODEC_ID_SVQ1)
432 put_tag(pb, "SVQ1");
433 else if(track->enc->codec_id == CODEC_ID_SVQ3)
434 put_tag(pb, "SVQ3");
435 else if(track->enc->codec_id == CODEC_ID_MPEG4)
436 put_tag(pb, "mp4v");
437 else if(track->enc->codec_id == CODEC_ID_H263)
438 put_tag(pb, "s263");
ca20f118
RS
439 else if(track->enc->codec_id == CODEC_ID_DVVIDEO)
440 put_tag(pb, "dvc ");
f578f938
TR
441 else
442 put_tag(pb, " "); /* Unknown tag */
6e6d6dc0 443
f578f938
TR
444 put_be32(pb, 0); /* Reserved */
445 put_be16(pb, 0); /* Reserved */
446 put_be16(pb, 1); /* Data-reference index */
447
448 put_be32(pb, 0); /* Reserved (= 02000c) */
449 put_be32(pb, 0); /* Reserved ("SVis")*/
450 put_be32(pb, 0); /* Reserved */
451 put_be32(pb, 0); /* Reserved (400)*/
452 put_be16(pb, track->enc->width); /* Video width */
453 put_be16(pb, track->enc->height); /* Video height */
454 put_be32(pb, 0x00480000); /* Reserved */
455 put_be32(pb, 0x00480000); /* Reserved */
456 put_be32(pb, 0); /* Reserved */
457 put_be32(pb, 0); /* Reserved */
458 put_be32(pb, 0); /* Reserved */
459 put_be32(pb, 0); /* Reserved */
460 put_be32(pb, 0); /* Reserved */
461 put_be32(pb, 0); /* Reserved */
462
463 put_be16(pb, 0); /* Reserved */
464 put_be32(pb, 0); /* Reserved */
465 put_be32(pb, 0); /* Reserved */
466 put_be32(pb, 0); /* Reserved */
467 put_be16(pb, 0x18); /* Reserved */
468 put_be16(pb, 0xffff); /* Reserved */
469 if(track->enc->codec_id == CODEC_ID_MPEG4)
470 mov_write_esds_tag(pb, track);
471 else if(track->enc->codec_id == CODEC_ID_H263)
472 mov_write_d263_tag(pb);
473 else if(track->enc->codec_id == CODEC_ID_SVQ3)
474 mov_write_svq3_tag(pb);
475
476 return updateSize (pb, pos);
1cb5f7fd
MN
477}
478
6e6d6dc0 479static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 480{
6e6d6dc0 481 int pos = url_ftell(pb);
1cb5f7fd
MN
482 put_be32(pb, 0); /* size */
483 put_tag(pb, "stsd");
484 put_be32(pb, 0); /* version & flags */
485 put_be32(pb, 1); /* entry count */
f578f938
TR
486 if (track->enc->codec_type == CODEC_TYPE_VIDEO)
487 mov_write_video_tag(pb, track);
488 else if (track->enc->codec_type == CODEC_TYPE_AUDIO)
489 mov_write_audio_tag(pb, track);
6e6d6dc0 490 return updateSize(pb, pos);
1cb5f7fd
MN
491}
492
f578f938 493/* TODO?: Currently all samples/frames seem to have same duration */
e45ccf79 494/* Time to sample atom */
6e6d6dc0 495static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd
MN
496{
497 put_be32(pb, 0x18); /* size */
498 put_tag(pb, "stts");
499 put_be32(pb, 0); /* version & flags */
500 put_be32(pb, 1); /* entry count */
501
e45ccf79
GB
502 put_be32(pb, track->sampleCount); /* sample count */
503 put_be32(pb, track->sampleDuration); /* sample duration */
1cb5f7fd
MN
504 return 0x18;
505}
506
6e6d6dc0 507static int mov_write_dref_tag(ByteIOContext *pb)
1cb5f7fd
MN
508{
509 put_be32(pb, 28); /* size */
510 put_tag(pb, "dref");
511 put_be32(pb, 0); /* version & flags */
512 put_be32(pb, 1); /* entry count */
513
514 put_be32(pb, 0xc); /* size */
515 put_tag(pb, "url ");
516 put_be32(pb, 1); /* version & flags */
517
518 return 28;
519}
520
6e6d6dc0 521static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 522{
6e6d6dc0 523 int pos = url_ftell(pb);
1cb5f7fd
MN
524 put_be32(pb, 0); /* size */
525 put_tag(pb, "stbl");
6e6d6dc0
MN
526 mov_write_stsd_tag(pb, track);
527 mov_write_stts_tag(pb, track);
f578f938
TR
528 if (track->enc->codec_type == CODEC_TYPE_VIDEO &&
529 track->hasKeyframes)
530 mov_write_stss_tag(pb, track);
6e6d6dc0
MN
531 mov_write_stsc_tag(pb, track);
532 mov_write_stsz_tag(pb, track);
533 mov_write_stco_tag(pb, track);
534 return updateSize(pb, pos);
1cb5f7fd
MN
535}
536
6e6d6dc0 537static int mov_write_dinf_tag(ByteIOContext *pb)
1cb5f7fd 538{
6e6d6dc0 539 int pos = url_ftell(pb);
1cb5f7fd
MN
540 put_be32(pb, 0); /* size */
541 put_tag(pb, "dinf");
6e6d6dc0
MN
542 mov_write_dref_tag(pb);
543 return updateSize(pb, pos);
1cb5f7fd
MN
544}
545
6e6d6dc0 546static int mov_write_smhd_tag(ByteIOContext *pb)
1cb5f7fd
MN
547{
548 put_be32(pb, 16); /* size */
549 put_tag(pb, "smhd");
550 put_be32(pb, 0); /* version & flags */
551 put_be16(pb, 0); /* reserved (balance, normally = 0) */
552 put_be16(pb, 0); /* reserved */
553 return 16;
554}
555
6e6d6dc0 556static int mov_write_vmhd_tag(ByteIOContext *pb)
1cb5f7fd
MN
557{
558 put_be32(pb, 0x14); /* size (always 0x14) */
559 put_tag(pb, "vmhd");
560 put_be32(pb, 0x01); /* version & flags */
561 put_be64(pb, 0); /* reserved (graphics mode = copy) */
562 return 0x14;
563}
564
6e6d6dc0 565static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 566{
6e6d6dc0 567 int pos = url_ftell(pb);
1cb5f7fd
MN
568 put_be32(pb, 0); /* size */
569 put_tag(pb, "minf");
570 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
6e6d6dc0 571 mov_write_vmhd_tag(pb);
1cb5f7fd 572 else
6e6d6dc0
MN
573 mov_write_smhd_tag(pb);
574 mov_write_dinf_tag(pb);
575 mov_write_stbl_tag(pb, track);
576 return updateSize(pb, pos);
1cb5f7fd
MN
577}
578
6e6d6dc0 579static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 580{
f578f938
TR
581 char *str;
582 int pos = url_ftell(pb);
583 put_be32(pb, 0); /* size */
1cb5f7fd
MN
584 put_tag(pb, "hdlr");
585 put_be32(pb, 0); /* Version & flags */
ca20f118
RS
586 if (track->mode == MODE_MOV)
587 put_tag(pb, "mhlr"); /* handler */
588 else
589 put_be32(pb, 0); /* reserved */
1cb5f7fd
MN
590 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
591 put_tag(pb, "vide"); /* handler type */
592 else
593 put_tag(pb, "soun"); /* handler type */
f578f938
TR
594 put_be32(pb ,0); /* reserved */
595 put_be32(pb ,0); /* reserved */
596 put_be32(pb ,0); /* reserved */
1cb5f7fd 597 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
f578f938 598 str = "VideoHandler";
1cb5f7fd 599 else
f578f938
TR
600 str = "SoundHandler";
601 put_byte(pb, strlen(str)); /* string counter */
602 put_buffer(pb, str, strlen(str));
603 return updateSize(pb, pos);
1cb5f7fd
MN
604}
605
6e6d6dc0 606static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd
MN
607{
608 put_be32(pb, 32); /* size */
609 put_tag(pb, "mdhd");
610 put_be32(pb, 0); /* Version & flags */
611 put_be32(pb, track->time); /* creation time */
612 put_be32(pb, track->time); /* modification time */
e45ccf79
GB
613 put_be32(pb, track->timescale); /* time scale (sample rate for audio) */
614 put_be32(pb, track->trackDuration); /* duration */
1cb5f7fd
MN
615 put_be16(pb, 0); /* language, 0 = english */
616 put_be16(pb, 0); /* reserved (quality) */
617 return 32;
618}
619
6e6d6dc0 620static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 621{
6e6d6dc0 622 int pos = url_ftell(pb);
1cb5f7fd
MN
623 put_be32(pb, 0); /* size */
624 put_tag(pb, "mdia");
6e6d6dc0
MN
625 mov_write_mdhd_tag(pb, track);
626 mov_write_hdlr_tag(pb, track);
627 mov_write_minf_tag(pb, track);
628 return updateSize(pb, pos);
1cb5f7fd
MN
629}
630
6e6d6dc0 631static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 632{
f578f938 633 int64_t maxTrackLenTemp;
1cb5f7fd
MN
634 put_be32(pb, 0x5c); /* size (always 0x5c) */
635 put_tag(pb, "tkhd");
f578f938 636 put_be32(pb, 0xf); /* version & flags (track enabled) */
1cb5f7fd
MN
637 put_be32(pb, track->time); /* creation time */
638 put_be32(pb, track->time); /* modification time */
639 put_be32(pb, track->trackID); /* track-id */
640 put_be32(pb, 0); /* reserved */
f578f938
TR
641 maxTrackLenTemp = ((int64_t)globalTimescale*(int64_t)track->trackDuration)/(int64_t)track->timescale;
642 put_be32(pb, (long)maxTrackLenTemp); /* duration */
1cb5f7fd
MN
643
644 put_be32(pb, 0); /* reserved */
645 put_be32(pb, 0); /* reserved */
646 put_be32(pb, 0x0); /* reserved (Layer & Alternate group) */
647 /* Volume, only for audio */
648 if(track->enc->codec_type == CODEC_TYPE_AUDIO)
649 put_be16(pb, 0x0100);
650 else
651 put_be16(pb, 0);
652 put_be16(pb, 0); /* reserved */
653
654 /* Matrix structure */
655 put_be32(pb, 0x00010000); /* reserved */
656 put_be32(pb, 0x0); /* reserved */
657 put_be32(pb, 0x0); /* reserved */
658 put_be32(pb, 0x0); /* reserved */
659 put_be32(pb, 0x00010000); /* reserved */
660 put_be32(pb, 0x0); /* reserved */
661 put_be32(pb, 0x0); /* reserved */
662 put_be32(pb, 0x0); /* reserved */
663 put_be32(pb, 0x40000000); /* reserved */
664
665 /* Track width and height, for visual only */
666 if(track->enc->codec_type == CODEC_TYPE_VIDEO) {
69dde1ad
GB
667 double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio);
668 if( !sample_aspect_ratio ) sample_aspect_ratio = 1;
669 put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000);
f578f938 670 put_be32(pb, track->enc->height*0x10000);
1cb5f7fd
MN
671 }
672 else {
673 put_be32(pb, 0);
674 put_be32(pb, 0);
675 }
676 return 0x5c;
677}
678
6e6d6dc0 679static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack* track)
1cb5f7fd 680{
6e6d6dc0 681 int pos = url_ftell(pb);
1cb5f7fd
MN
682 put_be32(pb, 0); /* size */
683 put_tag(pb, "trak");
6e6d6dc0
MN
684 mov_write_tkhd_tag(pb, track);
685 mov_write_mdia_tag(pb, track);
686 return updateSize(pb, pos);
1cb5f7fd
MN
687}
688
689/* TODO: Not sorted out, but not necessary either */
6e6d6dc0 690static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov)
1cb5f7fd
MN
691{
692 put_be32(pb, 0x15); /* size */
693 put_tag(pb, "iods");
694 put_be32(pb, 0); /* version & flags */
695 put_be16(pb, 0x1007);
696 put_byte(pb, 0);
697 put_be16(pb, 0x4fff);
698 put_be16(pb, 0xfffe);
699 put_be16(pb, 0x01ff);
700 return 0x15;
701}
702
6e6d6dc0 703static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov)
1cb5f7fd
MN
704{
705 int maxTrackID = 1, maxTrackLen = 0, i;
f578f938 706 int64_t maxTrackLenTemp;
1cb5f7fd
MN
707
708 put_be32(pb, 0x6c); /* size (always 0x6c) */
709 put_tag(pb, "mvhd");
710 put_be32(pb, 0); /* version & flags */
711 put_be32(pb, mov->time); /* creation time */
712 put_be32(pb, mov->time); /* modification time */
713 put_be32(pb, mov->timescale); /* timescale */
714 for (i=0; i<MAX_STREAMS; i++) {
715 if(mov->tracks[i].entry > 0) {
f578f938
TR
716 maxTrackLenTemp = ((int64_t)globalTimescale*(int64_t)mov->tracks[i].trackDuration)/(int64_t)mov->tracks[i].timescale;
717 if(maxTrackLen < maxTrackLenTemp)
718 maxTrackLen = maxTrackLenTemp;
1cb5f7fd
MN
719 if(maxTrackID < mov->tracks[i].trackID)
720 maxTrackID = mov->tracks[i].trackID;
721 }
722 }
723 put_be32(pb, maxTrackLen); /* duration of longest track */
724
725 put_be32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */
726 put_be16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */
727 put_be16(pb, 0); /* reserved */
728 put_be32(pb, 0); /* reserved */
729 put_be32(pb, 0); /* reserved */
730
731 /* Matrix structure */
732 put_be32(pb, 0x00010000); /* reserved */
733 put_be32(pb, 0x0); /* reserved */
734 put_be32(pb, 0x0); /* reserved */
735 put_be32(pb, 0x0); /* reserved */
736 put_be32(pb, 0x00010000); /* reserved */
737 put_be32(pb, 0x0); /* reserved */
738 put_be32(pb, 0x0); /* reserved */
739 put_be32(pb, 0x0); /* reserved */
740 put_be32(pb, 0x40000000); /* reserved */
741
742 put_be32(pb, 0); /* reserved (preview time) */
743 put_be32(pb, 0); /* reserved (preview duration) */
744 put_be32(pb, 0); /* reserved (poster time) */
745 put_be32(pb, 0); /* reserved (selection time) */
746 put_be32(pb, 0); /* reserved (selection duration) */
747 put_be32(pb, 0); /* reserved (current time) */
748 put_be32(pb, maxTrackID+1); /* Next track id */
749 return 0x6c;
750}
751
69dde1ad
GB
752static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov,
753 AVFormatContext *s)
754{
755 int pos = url_ftell(pb);
756 int i;
757
758 put_be32(pb, 0); /* size */
759 put_tag(pb, "udta");
760
761 /* Requirements */
762 for (i=0; i<MAX_STREAMS; i++) {
763 if(mov->tracks[i].entry <= 0) continue;
764 if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC ||
765 mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) {
766 int pos = url_ftell(pb);
767 put_be32(pb, 0); /* size */
768 put_tag(pb, "\251req");
769 put_be16(pb, sizeof("QuickTime 6.0 or greater") - 1);
770 put_be16(pb, 0);
771 put_buffer(pb, "QuickTime 6.0 or greater",
772 sizeof("QuickTime 6.0 or greater") - 1);
773 updateSize(pb, pos);
774 break;
775 }
776 }
777
778 /* Encoder */
779 {
780 int pos = url_ftell(pb);
781 put_be32(pb, 0); /* size */
782 put_tag(pb, "\251enc");
783 put_be16(pb, sizeof(LIBAVFORMAT_IDENT) - 1); /* string length */
784 put_be16(pb, 0);
785 put_buffer(pb, LIBAVFORMAT_IDENT, sizeof(LIBAVFORMAT_IDENT) - 1);
786 updateSize(pb, pos);
787 }
788
789 if( s->title[0] )
790 {
791 int pos = url_ftell(pb);
792 put_be32(pb, 0); /* size */
793 put_tag(pb, "\251nam");
794 put_be16(pb, strlen(s->title)); /* string length */
795 put_be16(pb, 0);
796 put_buffer(pb, s->title, strlen(s->title));
797 updateSize(pb, pos);
798 }
799
800 if( s->author[0] )
801 {
802 int pos = url_ftell(pb);
803 put_be32(pb, 0); /* size */
804 put_tag(pb, /*"\251aut"*/ "\251day" );
805 put_be16(pb, strlen(s->author)); /* string length */
806 put_be16(pb, 0);
807 put_buffer(pb, s->author, strlen(s->author));
808 updateSize(pb, pos);
809 }
810
811 if( s->comment[0] )
812 {
813 int pos = url_ftell(pb);
814 put_be32(pb, 0); /* size */
815 put_tag(pb, "\251des");
816 put_be16(pb, strlen(s->comment)); /* string length */
817 put_be16(pb, 0);
818 put_buffer(pb, s->comment, strlen(s->comment));
819 updateSize(pb, pos);
820 }
821
822 return updateSize(pb, pos);
823}
824
825static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
826 AVFormatContext *s)
1cb5f7fd 827{
6e6d6dc0 828 int pos, i;
1cb5f7fd
MN
829 pos = url_ftell(pb);
830 put_be32(pb, 0); /* size placeholder*/
831 put_tag(pb, "moov");
832 mov->timescale = globalTimescale;
833
834 for (i=0; i<MAX_STREAMS; i++) {
e45ccf79
GB
835 if(mov->tracks[i].entry <= 0) continue;
836
837 if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) {
838 mov->tracks[i].timescale = mov->tracks[i].enc->frame_rate;
839 mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_rate_base;
840 }
841 else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) {
842 /* If AMR, track timescale = 8000, AMR_WB = 16000 */
843 if(mov->tracks[i].enc->codec_id == CODEC_ID_AMR_NB) {
844 mov->tracks[i].sampleDuration = 160; // Bytes per chunk
845 mov->tracks[i].timescale = 8000;
1cb5f7fd 846 }
e45ccf79
GB
847 else {
848 mov->tracks[i].timescale = mov->tracks[i].enc->sample_rate;
849 mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_size;
1cb5f7fd 850 }
1cb5f7fd 851 }
e45ccf79
GB
852
853 mov->tracks[i].trackDuration =
854 mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration;
855 mov->tracks[i].time = mov->time;
856 mov->tracks[i].trackID = i+1;
1cb5f7fd
MN
857 }
858
6e6d6dc0
MN
859 mov_write_mvhd_tag(pb, mov);
860 //mov_write_iods_tag(pb, mov);
1cb5f7fd
MN
861 for (i=0; i<MAX_STREAMS; i++) {
862 if(mov->tracks[i].entry > 0) {
6e6d6dc0 863 mov_write_trak_tag(pb, &(mov->tracks[i]));
1cb5f7fd
MN
864 }
865 }
866
69dde1ad
GB
867 mov_write_udta_tag(pb, mov, s);
868
6e6d6dc0 869 return updateSize(pb, pos);
1cb5f7fd
MN
870}
871
f578f938 872int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov)
1cb5f7fd 873{
f578f938 874 mov->mdat_pos = url_ftell(pb);
1cb5f7fd
MN
875 put_be32(pb, 0); /* size placeholder*/
876 put_tag(pb, "mdat");
877 return 0;
878}
879
880/* TODO: This needs to be more general */
e45ccf79 881int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s)
1cb5f7fd 882{
69dde1ad
GB
883 MOVContext *mov = s->priv_data;
884
1cb5f7fd
MN
885 put_be32(pb, 0x14 ); /* size */
886 put_tag(pb, "ftyp");
e45ccf79 887
69dde1ad 888 if ( mov->mode == MODE_3GP )
e45ccf79
GB
889 put_tag(pb, "3gp4");
890 else
891 put_tag(pb, "isom");
892
1cb5f7fd 893 put_be32(pb, 0x200 );
e45ccf79 894
69dde1ad 895 if ( mov->mode == MODE_3GP )
e45ccf79
GB
896 put_tag(pb, "3gp4");
897 else
898 put_tag(pb, "mp41");
899
1cb5f7fd
MN
900 return 0x14;
901}
902
903static int mov_write_header(AVFormatContext *s)
904{
905 ByteIOContext *pb = &s->pb;
69dde1ad
GB
906 MOVContext *mov = s->priv_data;
907 int i;
1cb5f7fd 908
69dde1ad
GB
909 /* Default mode == MP4 */
910 mov->mode = MODE_MP4;
911
912 if (s->oformat != NULL) {
913 if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP;
914 else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV;
915
916 if ( mov->mode == MODE_3GP || mov->mode == MODE_MP4 )
917 mov_write_ftyp_tag(pb,s);
918 }
919
920 for (i=0; i<MAX_STREAMS; i++) {
921 mov->tracks[i].mode = mov->mode;
f578f938
TR
922 }
923
1cb5f7fd
MN
924 put_flush_packet(pb);
925
926 return 0;
927}
928
7906085f 929static int Timestamp(void) {
dd0003fa 930 return 1067949799U+(24107*86400); //its the modification time of this line :)
1cb5f7fd
MN
931}
932
933static int mov_write_packet(AVFormatContext *s, int stream_index,
49057904 934 const uint8_t *buf, int size, int64_t pts)
1cb5f7fd
MN
935{
936 MOVContext *mov = s->priv_data;
937 ByteIOContext *pb = &s->pb;
e45ccf79
GB
938 AVCodecContext *enc = &s->streams[stream_index]->codec;
939 MOVTrack* trk = &mov->tracks[stream_index];
1a31840c 940 int cl, id;
e45ccf79 941 unsigned int samplesInChunk = 0;
1cb5f7fd 942
e45ccf79
GB
943 if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */
944 if (!size) return 0; /* Discard 0 sized packets */
1cb5f7fd 945
e45ccf79
GB
946 if (enc->codec_type == CODEC_TYPE_VIDEO ) {
947 samplesInChunk = 1;
948 }
949 else if (enc->codec_type == CODEC_TYPE_AUDIO ) {
950 if( enc->codec_id == CODEC_ID_AMR_NB) {
f578f938 951 /* We must find out how many AMR blocks there are in one packet */
e45ccf79
GB
952 static uint16_t packed_size[16] =
953 {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0};
954 int len = 0;
955
956 while (len < size && samplesInChunk < 100) {
957 len += packed_size[(buf[len] >> 3) & 0x0F];
958 samplesInChunk++;
f578f938 959 }
1cb5f7fd 960 }
e45ccf79
GB
961 else if(enc->codec_id == CODEC_ID_PCM_ALAW) {
962 samplesInChunk = size/enc->channels;
1cb5f7fd 963 }
e45ccf79
GB
964 else {
965 samplesInChunk = 1;
1cb5f7fd 966 }
e45ccf79
GB
967 }
968
969 if ((enc->codec_id == CODEC_ID_MPEG4 || enc->codec_id == CODEC_ID_AAC)
970 && trk->vosLen == 0) {
971 assert(enc->extradata_size);
972
973 trk->vosLen = enc->extradata_size;
974 trk->vosData = av_malloc(trk->vosLen);
975 memcpy(trk->vosData, enc->extradata, trk->vosLen);
976 }
977
978 cl = trk->entry / MOV_INDEX_CLUSTER_SIZE;
979 id = trk->entry % MOV_INDEX_CLUSTER_SIZE;
980
981 if (trk->ents_allocated <= trk->entry) {
982 trk->cluster = av_realloc(trk->cluster, (cl+1)*sizeof(void*));
983 if (!trk->cluster)
984 return -1;
985 trk->cluster[cl] = av_malloc(MOV_INDEX_CLUSTER_SIZE*sizeof(MOVIentry));
986 if (!trk->cluster[cl])
987 return -1;
988 trk->ents_allocated += MOV_INDEX_CLUSTER_SIZE;
989 }
990 if (mov->mdat_written == 0) {
991 mov_write_mdat_tag(pb, mov);
992 mov->mdat_written = 1;
993 mov->time = Timestamp();
994 }
995
69dde1ad 996 trk->cluster[cl][id].pos = url_ftell(pb);
e45ccf79
GB
997 trk->cluster[cl][id].samplesInChunk = samplesInChunk;
998 trk->cluster[cl][id].size = size;
999 trk->cluster[cl][id].entries = samplesInChunk;
1000 if(enc->codec_type == CODEC_TYPE_VIDEO) {
1001 trk->cluster[cl][id].key_frame = enc->coded_frame->key_frame;
1002 if(enc->coded_frame->pict_type == FF_I_TYPE)
f578f938 1003 trk->hasKeyframes = 1;
1cb5f7fd 1004 }
e45ccf79
GB
1005 trk->enc = enc;
1006 trk->entry++;
1007 trk->sampleCount += samplesInChunk;
1008 trk->mdat_size += size;
1009
1cb5f7fd
MN
1010 put_buffer(pb, buf, size);
1011
1012 put_flush_packet(pb);
1013 return 0;
1014}
1015
1016static int mov_write_trailer(AVFormatContext *s)
1017{
1018 MOVContext *mov = s->priv_data;
1019 ByteIOContext *pb = &s->pb;
1020 int res = 0;
1021 int i, j;
1cb5f7fd 1022
69dde1ad 1023 offset_t moov_pos = url_ftell(pb);
1cb5f7fd
MN
1024
1025 /* Write size of mdat tag */
69dde1ad 1026 for (i=0, j=0; i<MAX_STREAMS; i++) {
1cb5f7fd
MN
1027 if(mov->tracks[i].ents_allocated > 0) {
1028 j += mov->tracks[i].mdat_size;
1029 }
1030 }
f578f938 1031 url_fseek(pb, mov->mdat_pos, SEEK_SET);
1cb5f7fd 1032 put_be32(pb, j+8);
69dde1ad 1033 url_fseek(pb, moov_pos, SEEK_SET);
1cb5f7fd 1034
69dde1ad 1035 mov_write_moov_tag(pb, mov, s);
1cb5f7fd
MN
1036
1037 for (i=0; i<MAX_STREAMS; i++) {
1038 for (j=0; j<mov->tracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) {
1039 av_free(mov->tracks[i].cluster[j]);
1040 }
1041 av_free(mov->tracks[i].cluster);
ec7d0d2e
GB
1042 if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData );
1043
1cb5f7fd
MN
1044 mov->tracks[i].cluster = NULL;
1045 mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0;
1046 }
69dde1ad 1047
1cb5f7fd
MN
1048 put_flush_packet(pb);
1049
1050 return res;
1051}
1052
1053static AVOutputFormat mov_oformat = {
1054 "mov",
1055 "mov format",
1056 NULL,
1057 "mov",
1058 sizeof(MOVContext),
69dde1ad 1059 CODEC_ID_AAC,
2187d948 1060 CODEC_ID_MPEG4,
1cb5f7fd
MN
1061 mov_write_header,
1062 mov_write_packet,
1063 mov_write_trailer,
1064};
1065
1066static AVOutputFormat _3gp_oformat = {
1067 "3gp",
1068 "3gp format",
1069 NULL,
1070 "3gp",
1071 sizeof(MOVContext),
1072 CODEC_ID_AMR_NB,
1073 CODEC_ID_H263,
1074 mov_write_header,
1075 mov_write_packet,
1076 mov_write_trailer,
1077};
1078
1079static AVOutputFormat mp4_oformat = {
1080 "mp4",
1081 "mp4 format",
4cb3f3b6
DC
1082 "application/mp4",
1083 "mp4,m4a",
1cb5f7fd
MN
1084 sizeof(MOVContext),
1085 CODEC_ID_AAC,
1086 CODEC_ID_MPEG4,
1087 mov_write_header,
1088 mov_write_packet,
1089 mov_write_trailer,
1090};
1091
1092int movenc_init(void)
1093{
1094 av_register_output_format(&mov_oformat);
1095 av_register_output_format(&_3gp_oformat);
1096 av_register_output_format(&mp4_oformat);
1097 return 0;
1098}