2 * MOV, 3GP, MP4 encoder.
3 * Copyright (c) 2003 Thomas Raivio.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * - Currently supports h.263, MPEG4 video codecs, and AMR audio codec.
31 #define MOV_INDEX_CLUSTER_SIZE 16384
32 #define globalTimescale 1000
34 typedef struct MOVIentry {
35 unsigned int flags, pos, len;
39 typedef struct MOVIndex {
62 MOVTrack tracks[MAX_STREAMS];
65 //FIXME supprt 64bit varaint with wide placeholders
66 static int updateSize (ByteIOContext *pb, int pos)
68 long curpos = url_ftell(pb);
69 url_fseek(pb, pos, SEEK_SET);
70 put_be32(pb, curpos - pos); /* rewrite size */
71 url_fseek(pb, curpos, SEEK_SET);
76 static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track)
79 put_be32(pb, 16+track->entry*4); /* size */
81 put_be32(pb, 0); /* version & flags */
82 put_be32(pb, track->entry); /* entry count */
83 for (i=0; i<track->entry; i++) {
84 int cl = i / MOV_INDEX_CLUSTER_SIZE;
85 int id = i % MOV_INDEX_CLUSTER_SIZE;
86 put_be32(pb, track->cluster[cl][id].pos);
88 return 16+track->entry*4;
91 static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track)
95 if(track->cluster[0][0].entries != 0)
98 size = 20+track->samples*4;
99 put_be32(pb, size); /* size */
101 put_be32(pb, 0); /* version & flags */
103 /* TODO: Ugly (and false) assumption: if we have a chunk of samples, assume
104 * all sizes are same */
105 if(track->cluster[0][0].entries != 0) {
106 int sSize = track->cluster[0][0].len/track->cluster[0][0].entries;
107 put_be32(pb, sSize); /* sample size */
108 put_be32(pb, track->samples); /* sample count */
112 put_be32(pb, 0); /* sample size */
113 put_be32(pb, track->samples); /* sample count */
114 for (i=0; i<track->samples; i++) {
115 int cl = i / MOV_INDEX_CLUSTER_SIZE;
116 int id = i % MOV_INDEX_CLUSTER_SIZE;
117 put_be32(pb, track->cluster[cl][id].len);
123 static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track)
126 if(track->cluster[0][0].entries != 0)
127 size = 16+track->entry*4*3;
130 put_be32(pb, size); // size
132 put_be32(pb, 0); // version & flags
133 if(track->cluster[0][0].entries != 0) {
135 put_be32(pb, track->entry); // entry count
136 for (i=0; i<track->entry; i++) {
137 int cl = i / MOV_INDEX_CLUSTER_SIZE;
138 int id = i % MOV_INDEX_CLUSTER_SIZE;
139 put_be32(pb, i+1); // first chunk
140 put_be32(pb, track->cluster[cl][id].entries);
141 put_be32(pb, 0x1); // sample description index
145 put_be32(pb, 1); // entry count
146 put_be32(pb, 0x1); // first chunk
147 put_be32(pb, 0x1); // samples per chunk
148 put_be32(pb, 0x1); // sample description index
155 static int mov_write_stss_tag(ByteIOContext *pb) //TRA OK
157 put_be32(pb, 0x14); /* size */
159 put_be32(pb, 0); /* version & flags */
160 put_be32(pb, 1); /* entry count */
161 put_be32(pb, 0x1); /* sample number */
165 static int mov_write_damr_tag(ByteIOContext *pb)
167 put_be32(pb, 0x11); /* size */
171 put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */
172 put_be16(pb, 1); /* Mode change period (no restriction) */
176 static int mov_write_samr_tag(ByteIOContext *pb, MOVTrack* track)
178 int pos = url_ftell(pb);
179 put_be32(pb, 0); /* size */
180 /* "samr" for AMR NB, "sawb" for AMR WB */
182 put_be32(pb, 0); /* Reserved */
183 put_be16(pb, 0); /* Reserved */
184 put_be16(pb, 1); /* Data-reference index, XXX == 1 */
185 put_be32(pb, 0); /* Reserved */
186 put_be32(pb, 0); /* Reserved */
188 put_be16(pb, 2); /* Reserved */
189 put_be16(pb, 0x10); /* Reserved */
190 put_be32(pb, 0); /* Reserved */
191 put_be16(pb, track->timescale); /* Time scale */
192 put_be16(pb, 0); /* Reserved */
194 mov_write_damr_tag(pb);
195 return updateSize (pb, pos);
198 static int mov_write_d263_tag(ByteIOContext *pb)
200 put_be32(pb, 0xf); /* size */
208 static int mov_write_s263_tag(ByteIOContext *pb, MOVTrack* track)
210 int pos = url_ftell(pb);
211 put_be32(pb, 0); /* size */
213 put_be32(pb, 0); /* Reserved */
214 put_be16(pb, 0); /* Reserved */
215 put_be16(pb, 1); /* Data-reference index */
216 put_be32(pb, 0); /* Reserved */
217 put_be32(pb, 0); /* Reserved */
218 put_be32(pb, 0); /* Reserved */
219 put_be32(pb, 0); /* Reserved */
220 put_be16(pb, track->enc->width); /* Video width */
221 put_be16(pb, track->enc->height); /* Video height */
222 put_be32(pb, 0x00480000); /* Reserved */
223 put_be32(pb, 0x00480000); /* Reserved */
224 put_be32(pb, 0); /* Reserved */
225 put_be16(pb, 1); /* Reserved */
226 put_be32(pb, 0); /* Reserved */
227 put_be32(pb, 0); /* Reserved */
228 put_be32(pb, 0); /* Reserved */
229 put_be32(pb, 0); /* Reserved */
230 put_be32(pb, 0); /* Reserved */
231 put_be32(pb, 0); /* Reserved */
232 put_be32(pb, 0); /* Reserved */
233 put_be32(pb, 0); /* Reserved */
234 put_be16(pb, 0x18); /* Reserved */
235 put_be16(pb, 0xffff); /* Reserved */
236 mov_write_d263_tag(pb);
237 return updateSize (pb, pos);
240 static unsigned int esdsLength(unsigned int len)
242 unsigned int result = 0;
243 unsigned char b = len & 0x7f;
245 b = (len >> 8) & 0x7f;
246 result += (b + 0x80) << 8;
247 b = (len >> 16) & 0x7f;
248 result += (b + 0x80) << 16;
249 b = (len >> 24) & 0x7f;
250 result += (b + 0x80) << 24;
254 static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
256 put_be32(pb, track->vosLen+18+14+17);
258 put_be32(pb, 0); // Version
260 put_byte(pb, 0x03); // tag = ES_DescriptorTag
261 put_be32(pb, esdsLength(track->vosLen+18+14)); // Length
262 put_be16(pb, 0x0001); // ID (= 1)
263 put_byte(pb, 0x00); // flags (= no flags)
265 // Decoderconfigdescriptor = 4
266 put_byte(pb, 0x04); // tag = DecoderConfigDescriptor
267 put_be32(pb, esdsLength(track->vosLen+18)); // Length
268 put_byte(pb, 0x20); // Object type indication (Visual 14496-2)
269 put_byte(pb, 0x11); // flags (= Visualstream)
270 put_byte(pb, 0x0); // Buffersize DB (24 bits)
271 put_be16(pb, 0x0dd2); // Buffersize DB
273 // TODO: find real values for these
274 put_be32(pb, 0x0002e918); // maxbitrate
275 put_be32(pb, 0x00017e6b); // avg bitrate
277 // Decoderspecific info Tag = 5
278 put_byte(pb, 0x05); // tag = Decoderspecific info
279 put_be32(pb, esdsLength(track->vosLen)); // length
280 put_buffer(pb, track->vosData, track->vosLen);
283 put_be32(pb, esdsLength(1)); // length
285 return track->vosLen+18+14+17;
288 static int mov_write_mp4v_tag(ByteIOContext *pb, MOVTrack* track) // Basic
290 int pos = url_ftell(pb);
294 put_be32(pb, 0); // Reserved
295 put_be16(pb, 0); // Reserved
296 put_be16(pb, 1); // Data-reference index
297 put_be32(pb, 0); // Reserved
298 put_be32(pb, 0); // Reserved
299 put_be32(pb, 0); // Reserved
300 put_be32(pb, 0); // Reserved
301 put_be16(pb, track->enc->width); // Width
302 put_be16(pb, track->enc->height); // Height
303 put_be32(pb, 0x00480000); // Reserved
304 put_be32(pb, 0x00480000); // Reserved
305 put_be32(pb, 0); // Reserved
306 put_be16(pb, 1); // Reserved
307 put_be32(pb, 0); // Reserved
308 put_be32(pb, 0); // Reserved
309 put_be32(pb, 0); // Reserved
310 put_be32(pb, 0); // Reserved
311 put_be32(pb, 0); // Reserved
312 put_be32(pb, 0); // Reserved
313 put_be32(pb, 0); // Reserved
314 put_be32(pb, 0); // Reserved
315 put_be16(pb, 24); // Reserved
316 put_be16(pb, 0xFFFF); // Reserved
317 mov_write_esds_tag(pb, track);
318 return updateSize(pb, pos);
321 static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
323 int pos = url_ftell(pb);
324 put_be32(pb, 0); /* size */
326 put_be32(pb, 0); /* version & flags */
327 put_be32(pb, 1); /* entry count */
328 if (track->enc->codec_type == CODEC_TYPE_VIDEO) {
329 if (track->enc->codec_id == CODEC_ID_H263)
330 mov_write_s263_tag(pb, track);
331 else if (track->enc->codec_id == CODEC_ID_MPEG4)
332 mov_write_mp4v_tag(pb, track);
334 else if (track->enc->codec_type == CODEC_TYPE_AUDIO) {
335 if (track->enc->codec_id == CODEC_ID_AMR_NB)
336 mov_write_samr_tag(pb, track);
338 return updateSize(pb, pos);
341 static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
343 put_be32(pb, 0x18); /* size */
345 put_be32(pb, 0); /* version & flags */
346 put_be32(pb, 1); /* entry count */
348 put_be32(pb, track->samples); /* sample count */
349 put_be32(pb, track->sampleDelta); /* sample delta */
353 static int mov_write_dref_tag(ByteIOContext *pb)
355 put_be32(pb, 28); /* size */
357 put_be32(pb, 0); /* version & flags */
358 put_be32(pb, 1); /* entry count */
360 put_be32(pb, 0xc); /* size */
362 put_be32(pb, 1); /* version & flags */
367 static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track)
369 int pos = url_ftell(pb);
370 put_be32(pb, 0); /* size */
372 mov_write_stsd_tag(pb, track);
373 mov_write_stts_tag(pb, track);
374 if (track->enc->codec_type == CODEC_TYPE_VIDEO)
375 mov_write_stss_tag(pb);
376 mov_write_stsc_tag(pb, track);
377 mov_write_stsz_tag(pb, track);
378 mov_write_stco_tag(pb, track);
379 return updateSize(pb, pos);
382 static int mov_write_dinf_tag(ByteIOContext *pb)
384 int pos = url_ftell(pb);
385 put_be32(pb, 0); /* size */
387 mov_write_dref_tag(pb);
388 return updateSize(pb, pos);
391 static int mov_write_smhd_tag(ByteIOContext *pb)
393 put_be32(pb, 16); /* size */
395 put_be32(pb, 0); /* version & flags */
396 put_be16(pb, 0); /* reserved (balance, normally = 0) */
397 put_be16(pb, 0); /* reserved */
401 static int mov_write_vmhd_tag(ByteIOContext *pb)
403 put_be32(pb, 0x14); /* size (always 0x14) */
405 put_be32(pb, 0x01); /* version & flags */
406 put_be64(pb, 0); /* reserved (graphics mode = copy) */
410 static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track)
412 int pos = url_ftell(pb);
413 put_be32(pb, 0); /* size */
415 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
416 mov_write_vmhd_tag(pb);
418 mov_write_smhd_tag(pb);
419 mov_write_dinf_tag(pb);
420 mov_write_stbl_tag(pb, track);
421 return updateSize(pb, pos);
424 static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track)
428 put_be32(pb, size); /* size */
430 put_be32(pb, 0); /* Version & flags */
431 put_be32(pb, 0); /* reserved */
432 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
433 put_tag(pb, "vide"); /* handler type */
435 put_tag(pb, "soun"); /* handler type */
436 put_byte(pb, 0); /* reserved */
437 put_byte(pb, 0); /* reserved */
438 put_byte(pb, 0); /* reserved */
439 put_byte(pb, 0); /* reserved */
440 put_byte(pb, 0); /* reserved */
441 put_byte(pb, 0); /* reserved */
442 put_byte(pb, 0); /* reserved */
443 put_byte(pb, 0); /* reserved */
444 put_byte(pb, 0); /* reserved */
445 put_byte(pb, 0); /* reserved */
446 put_byte(pb, 0); /* reserved */
447 put_byte(pb, 0); /* reserved */
448 if(track->enc->codec_type == CODEC_TYPE_VIDEO)
449 put_buffer(pb, "VideoHandler", 13);
451 put_buffer(pb, "SoundHandler", 13);
455 static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
457 put_be32(pb, 32); /* size */
459 put_be32(pb, 0); /* Version & flags */
460 put_be32(pb, track->time); /* creation time */
461 put_be32(pb, track->time); /* modification time */
462 put_be32(pb, track->timescale); /* time scale */
464 put_be32(pb, track->timescale*track->entry*track->frameDuration/globalTimescale); /* duration */
465 put_be16(pb, 0); /* language, 0 = english */
466 put_be16(pb, 0); /* reserved (quality) */
470 static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track)
472 int pos = url_ftell(pb);
473 put_be32(pb, 0); /* size */
475 mov_write_mdhd_tag(pb, track);
476 mov_write_hdlr_tag(pb, track);
477 mov_write_minf_tag(pb, track);
478 return updateSize(pb, pos);
481 static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track)
483 put_be32(pb, 0x5c); /* size (always 0x5c) */
485 put_be32(pb, 1); /* version & flags (track enabled) */
486 put_be32(pb, track->time); /* creation time */
487 put_be32(pb, track->time); /* modification time */
488 put_be32(pb, track->trackID); /* track-id */
489 put_be32(pb, 0); /* reserved */
490 put_be32(pb, track->entry*track->frameDuration); /* duration */
492 put_be32(pb, 0); /* reserved */
493 put_be32(pb, 0); /* reserved */
494 put_be32(pb, 0x0); /* reserved (Layer & Alternate group) */
495 /* Volume, only for audio */
496 if(track->enc->codec_type == CODEC_TYPE_AUDIO)
497 put_be16(pb, 0x0100);
500 put_be16(pb, 0); /* reserved */
502 /* Matrix structure */
503 put_be32(pb, 0x00010000); /* reserved */
504 put_be32(pb, 0x0); /* reserved */
505 put_be32(pb, 0x0); /* reserved */
506 put_be32(pb, 0x0); /* reserved */
507 put_be32(pb, 0x00010000); /* reserved */
508 put_be32(pb, 0x0); /* reserved */
509 put_be32(pb, 0x0); /* reserved */
510 put_be32(pb, 0x0); /* reserved */
511 put_be32(pb, 0x40000000); /* reserved */
513 /* Track width and height, for visual only */
514 if(track->enc->codec_type == CODEC_TYPE_VIDEO) {
515 put_be32(pb, 0x01400000);
516 put_be32(pb, 0x00f00000);
525 static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack* track)
527 int pos = url_ftell(pb);
528 put_be32(pb, 0); /* size */
530 mov_write_tkhd_tag(pb, track);
531 mov_write_mdia_tag(pb, track);
532 return updateSize(pb, pos);
535 /* TODO: Not sorted out, but not necessary either */
536 static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov)
538 put_be32(pb, 0x15); /* size */
540 put_be32(pb, 0); /* version & flags */
541 put_be16(pb, 0x1007);
543 put_be16(pb, 0x4fff);
544 put_be16(pb, 0xfffe);
545 put_be16(pb, 0x01ff);
549 static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov)
551 int maxTrackID = 1, maxTrackLen = 0, i;
553 put_be32(pb, 0x6c); /* size (always 0x6c) */
555 put_be32(pb, 0); /* version & flags */
556 put_be32(pb, mov->time); /* creation time */
557 put_be32(pb, mov->time); /* modification time */
558 put_be32(pb, mov->timescale); /* timescale */
559 for (i=0; i<MAX_STREAMS; i++) {
560 if(mov->tracks[i].entry > 0) {
561 if(maxTrackLen < mov->tracks[i].entry*mov->tracks[i].frameDuration)
562 maxTrackLen = mov->tracks[i].entry*mov->tracks[i].frameDuration;
563 if(maxTrackID < mov->tracks[i].trackID)
564 maxTrackID = mov->tracks[i].trackID;
567 put_be32(pb, maxTrackLen); /* duration of longest track */
569 put_be32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */
570 put_be16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */
571 put_be16(pb, 0); /* reserved */
572 put_be32(pb, 0); /* reserved */
573 put_be32(pb, 0); /* reserved */
575 /* Matrix structure */
576 put_be32(pb, 0x00010000); /* reserved */
577 put_be32(pb, 0x0); /* reserved */
578 put_be32(pb, 0x0); /* reserved */
579 put_be32(pb, 0x0); /* reserved */
580 put_be32(pb, 0x00010000); /* reserved */
581 put_be32(pb, 0x0); /* reserved */
582 put_be32(pb, 0x0); /* reserved */
583 put_be32(pb, 0x0); /* reserved */
584 put_be32(pb, 0x40000000); /* reserved */
586 put_be32(pb, 0); /* reserved (preview time) */
587 put_be32(pb, 0); /* reserved (preview duration) */
588 put_be32(pb, 0); /* reserved (poster time) */
589 put_be32(pb, 0); /* reserved (selection time) */
590 put_be32(pb, 0); /* reserved (selection duration) */
591 put_be32(pb, 0); /* reserved (current time) */
592 put_be32(pb, maxTrackID+1); /* Next track id */
596 static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov)
600 put_be32(pb, 0); /* size placeholder*/
602 mov->timescale = globalTimescale;
604 for (i=0; i<MAX_STREAMS; i++) {
605 if(mov->tracks[i].entry > 0) {
606 if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) {
607 mov->tracks[i].timescale = globalTimescale;
608 mov->tracks[i].sampleDelta = mov->tracks[i].frameDuration =
609 globalTimescale*mov->tracks[i].enc->frame_rate_base/mov->tracks[i].enc->frame_rate;
611 else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) {
612 /* If AMR, track timescale = 8000, AMR_WB = 16000 */
613 if(mov->tracks[i].enc->codec_id == CODEC_ID_AMR_NB) {
614 mov->tracks[i].frameDuration = 20;
615 mov->tracks[i].sampleDelta = 160;
616 mov->tracks[i].timescale = 8000;
619 mov->tracks[i].timescale = globalTimescale;
620 mov->tracks[i].frameDuration =
621 globalTimescale*mov->tracks[i].enc->frame_rate_base/mov->tracks[i].enc->frame_rate;
624 mov->tracks[i].time = mov->time;
625 mov->tracks[i].trackID = i+1;
629 mov_write_mvhd_tag(pb, mov);
630 //mov_write_iods_tag(pb, mov);
631 for (i=0; i<MAX_STREAMS; i++) {
632 if(mov->tracks[i].entry > 0) {
633 mov_write_trak_tag(pb, &(mov->tracks[i]));
637 return updateSize(pb, pos);
640 int mov_write_mdat_tag(ByteIOContext *pb, MOVTrack* track)
642 track->mdat_pos = url_ftell(pb);
643 put_be32(pb, 0); /* size placeholder*/
648 /* TODO: This needs to be more general */
649 int mov_write_ftyp_tag(ByteIOContext *pb)
651 put_be32(pb, 0x14 ); /* size */
654 put_be32(pb, 0x200 );
659 static int mov_write_header(AVFormatContext *s)
661 ByteIOContext *pb = &s->pb;
664 mov_write_ftyp_tag(pb);
666 put_flush_packet(pb);
671 static int Timestamp() {
674 return ltime+(24107*86400);
677 static int mov_write_packet(AVFormatContext *s, int stream_index,
678 const uint8_t *buf, int size, int64_t pts)
680 MOVContext *mov = s->priv_data;
681 ByteIOContext *pb = &s->pb;
685 enc = &s->streams[stream_index]->codec;
686 if (!url_is_streamed(&s->pb)) {
687 MOVTrack* trk = &mov->tracks[stream_index];
690 /* We must find out how many AMR blocks there are in one packet */
691 if(enc->codec_id == CODEC_ID_AMR_NB) {
692 static uint16_t packed_size[16] = {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0};
695 while(len < size && sampleCount < 100) {
696 len += packed_size[(buf[len] >> 3) & 0x0F];
701 if(enc->codec_id == CODEC_ID_MPEG4 &&
704 assert(enc->extradata_size);
706 trk->vosLen = enc->extradata_size;
707 trk->vosData = av_malloc(trk->vosLen);
708 memcpy(trk->vosData, enc->extradata, trk->vosLen);
711 cl = trk->entry / MOV_INDEX_CLUSTER_SIZE;
712 id = trk->entry % MOV_INDEX_CLUSTER_SIZE;
714 if (trk->ents_allocated <= trk->entry) {
715 trk->cluster = av_realloc(trk->cluster, (cl+1)*sizeof(void*));
718 trk->cluster[cl] = av_malloc(MOV_INDEX_CLUSTER_SIZE*sizeof(MOVIentry));
719 if (!trk->cluster[cl])
721 trk->ents_allocated += MOV_INDEX_CLUSTER_SIZE;
723 if(stream_index == 0 && trk->entry == 0) {
724 mov_write_mdat_tag(pb, trk);
725 mov->time = Timestamp();
728 trk->cluster[cl][id].pos = url_ftell(pb) - mov->movi_list;
729 trk->cluster[cl][id].len = size;
730 trk->cluster[cl][id].entries = sampleCount;
736 trk->samples += sampleCount;
737 trk->mdat_size += size;
739 put_buffer(pb, buf, size);
741 put_flush_packet(pb);
745 static int mov_write_trailer(AVFormatContext *s)
747 MOVContext *mov = s->priv_data;
748 ByteIOContext *pb = &s->pb;
753 file_size = url_ftell(pb);
756 /* Write size of mdat tag */
757 for (i=0; i<MAX_STREAMS; i++) {
758 if(mov->tracks[i].ents_allocated > 0) {
759 j += mov->tracks[i].mdat_size;
762 url_fseek(pb, mov->tracks[0].mdat_pos, SEEK_SET);
764 url_fseek(pb, file_size, SEEK_SET);
766 mov_write_moov_tag(pb, mov);
768 for (i=0; i<MAX_STREAMS; i++) {
769 for (j=0; j<mov->tracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) {
770 av_free(mov->tracks[i].cluster[j]);
772 av_free(mov->tracks[i].cluster);
773 mov->tracks[i].cluster = NULL;
774 mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0;
776 put_flush_packet(pb);
781 static AVOutputFormat mov_oformat = {
794 static AVOutputFormat _3gp_oformat = {
807 static AVOutputFormat mp4_oformat = {
820 int movenc_init(void)
822 av_register_output_format(&mov_oformat);
823 av_register_output_format(&_3gp_oformat);
824 av_register_output_format(&mp4_oformat);