Compile fixes on OSX.
[libmkv.git] / src / matroska.c
blobe1e4fa9c55a4ac588522be2b809e877b96007360
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
5 * $Id: $
7 * Authors: Mike Matsnev
8 * Nathan Caldwell
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
24 #include "libmkv.h"
25 #include "matroska.h"
26 #include "config.h"
28 mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
29 mk_Context *c;
31 if (w->freelist) {
32 c = w->freelist;
33 w->freelist = w->freelist->next;
34 } else {
35 c = malloc(sizeof(*c));
36 memset(c, 0, sizeof(*c));
39 if (c == NULL)
40 return NULL;
42 c->parent = parent;
43 c->owner = w;
44 c->id = id;
46 if (c->owner->actlist)
47 c->owner->actlist->prev = &c->next;
48 c->next = c->owner->actlist;
49 c->prev = &c->owner->actlist;
51 return c;
54 static int mk_appendContextData(mk_Context *c, const void *data, unsigned size) {
55 unsigned ns = c->d_cur + size;
57 if (ns > c->d_max) {
58 void *dp;
59 unsigned dn = c->d_max ? c->d_max << 1 : 16;
60 while (ns > dn)
61 dn <<= 1;
63 dp = realloc(c->data, dn);
64 if (dp == NULL)
65 return -1;
67 c->data = dp;
68 c->d_max = dn;
71 memcpy((char*)c->data + c->d_cur, data, size);
73 c->d_cur = ns;
75 return 0;
78 static int mk_writeID(mk_Context *c, unsigned id) {
79 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
81 if (c_id[0])
82 return mk_appendContextData(c, c_id, 4);
83 if (c_id[1])
84 return mk_appendContextData(c, c_id+1, 3);
85 if (c_id[2])
86 return mk_appendContextData(c, c_id+2, 2);
87 return mk_appendContextData(c, c_id+3, 1);
90 static int mk_writeSize(mk_Context *c, uint64_t size) {
91 unsigned char c_size[8] = { 0x01, size >> 48, size >> 40, size >> 32, size >> 24, size >> 16, size >> 8, size };
93 if (size < 0x7f) {
94 c_size[7] |= 0x80;
95 return mk_appendContextData(c, c_size+7, 1);
97 if (size < 0x3fff) {
98 c_size[6] |= 0x40;
99 return mk_appendContextData(c, c_size+6, 2);
101 if (size < 0x1fffff) {
102 c_size[5] |= 0x20;
103 return mk_appendContextData(c, c_size+5, 3);
105 if (size < 0x0fffffff) {
106 c_size[4] |= 0x10;
107 return mk_appendContextData(c, c_size+4, 4);
109 if (size < 0x07ffffffff) {
110 c_size[3] |= 0x08;
111 return mk_appendContextData(c, c_size+3, 5);
113 if (size < 0x03ffffffffff) {
114 c_size[2] |= 0x04;
115 return mk_appendContextData(c, c_size+2, 6);
117 if (size < 0x01ffffffffffff) {
118 c_size[1] |= 0x02;
119 return mk_appendContextData(c, c_size+1, 7);
121 return mk_appendContextData(c, c_size, 9);
124 static int mk_writeSSize(mk_Context *c, int64_t size) {
125 uint64_t u_size = (uint64_t)llabs(size);
126 unsigned size_size = mk_ebmlSizeSize( u_size << 1 ); // We need to shift by one to get the correct size here.
128 switch (size_size)
130 case 1:
131 size += 0x3f;
132 break;
133 case 2:
134 size += 0x1fff;
135 break;
136 case 3:
137 size += 0x0fffff;
138 break;
139 case 4:
140 size += 0x07ffffff;
141 break;
142 case 5:
143 size += 0x03ffffffff;
144 break;
145 case 6:
146 size += 0x01ffffffffff;
147 break;
148 case 7:
149 size += 0x00ffffffffffff;
150 break;
151 default: // Matroska currently doesn't support any int > 56-bit.
152 return -1;
155 return mk_writeSize(c, size);
158 static int mk_flushContextID(mk_Context *c) {
159 unsigned char size_undf[8] = {0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
161 if (c->id == 0)
162 return 0;
164 CHECK(mk_writeID(c->parent, c->id));
165 CHECK(mk_appendContextData(c->parent, &size_undf, 8));
167 c->id = 0;
169 return 0;
172 int mk_flushContextData(mk_Context *c) {
173 if (c->d_cur == 0)
174 return 0;
176 if (c->parent)
177 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
178 else
179 if (fwrite(c->data, c->d_cur, 1, c->owner->fp) != 1)
180 return -1;
182 c->d_cur = 0;
184 return 0;
187 int mk_closeContext(mk_Context *c, int64_t *off) {
188 if (c->id) {
189 CHECK(mk_writeID(c->parent, c->id));
190 CHECK(mk_writeSize(c->parent, c->d_cur));
193 if (c->parent && off != NULL)
194 *off += c->parent->d_cur;
196 CHECK(mk_flushContextData(c));
198 if (c->next)
199 c->next->prev = c->prev;
200 *(c->prev) = c->next;
201 c->next = c->owner->freelist;
202 c->owner->freelist = c;
204 return 0;
207 static void mk_destroyContexts(mk_Writer *w) {
208 mk_Context *cur, *next;
210 for (cur = w->freelist; cur; cur = next) {
211 next = cur->next;
212 free(cur->data);
213 free(cur);
216 for (cur = w->actlist; cur; cur = next) {
217 next = cur->next;
218 free(cur->data);
219 free(cur);
222 w->freelist = w->actlist = w->root = NULL;
225 int mk_writeStr(mk_Context *c, unsigned id, const char *str) {
226 size_t len = strlen(str);
228 CHECK(mk_writeID(c, id));
229 CHECK(mk_writeSize(c, len));
230 CHECK(mk_appendContextData(c, str, len));
231 return 0;
234 int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
235 CHECK(mk_writeID(c, id));
236 CHECK(mk_writeSize(c, size));
237 CHECK(mk_appendContextData(c, data, size));
238 return 0;
241 int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui) {
242 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
243 unsigned i = 0;
245 CHECK(mk_writeID(c, id));
246 while (i < 7 && c_ui[i] == 0)
247 ++i;
248 CHECK(mk_writeSize(c, 8 - i));
249 CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
250 return 0;
253 static int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
254 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
255 unsigned i = 0;
257 CHECK(mk_writeID(c, id));
258 if (si < 0)
259 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
260 ++i;
261 else
262 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
263 ++i;
264 CHECK(mk_writeSize(c, 8 - i));
265 CHECK(mk_appendContextData(c, c_si+i, 8 - i));
266 return 0;
269 static int mk_writeFloatRaw(mk_Context *c, float f) {
270 union {
271 float f;
272 unsigned u;
273 } u;
274 unsigned char c_f[4];
276 u.f = f;
277 c_f[0] = u.u >> 24;
278 c_f[1] = u.u >> 16;
279 c_f[2] = u.u >> 8;
280 c_f[3] = u.u;
282 return mk_appendContextData(c, c_f, 4);
285 int mk_writeFloat(mk_Context *c, unsigned id, float f) {
286 CHECK(mk_writeID(c, id));
287 CHECK(mk_writeSize(c, 4));
288 CHECK(mk_writeFloatRaw(c, f));
289 return 0;
292 static int mk_writeVoid(mk_Context *c, unsigned length) {
293 char *c_void = calloc(length, sizeof(char));
295 CHECK(mk_writeID(c, 0xec));
296 CHECK(mk_writeSize(c, length));
297 CHECK(mk_appendContextData(c, c_void, length));
298 free(c_void);
299 return 0;
302 static unsigned mk_ebmlSizeSize(uint64_t s) {
303 if (s < 0x7f)
304 return 1;
305 if (s < 0x3fff)
306 return 2;
307 if (s < 0x1fffff)
308 return 3;
309 if (s < 0x0fffffff)
310 return 4;
311 if (s < 0x07ffffffff)
312 return 5;
313 if (s < 0x03ffffffffff)
314 return 6;
315 if (s < 0x01ffffffffffff)
316 return 7;
317 return 8;
320 static unsigned mk_ebmlSIntSize(int64_t si) {
321 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
322 unsigned i = 0;
324 if (si < 0)
325 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
326 ++i;
327 else
328 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
329 ++i;
331 return 8 - i;
334 mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat) {
335 mk_Writer *w = malloc(sizeof(*w));
336 if (w == NULL)
337 return NULL;
339 memset(w, 0, sizeof(*w));
341 w->root = mk_createContext(w, NULL, 0);
342 if (w->root == NULL) {
343 free(w);
344 return NULL;
347 if ((w->cues = mk_createContext(w, w->root, 0x1c53bb6b)) == NULL) // Cues
349 mk_destroyContexts(w);
350 free(w);
351 return NULL;
354 w->fp = fopen(filename, "wb");
355 if (w->fp == NULL) {
356 mk_destroyContexts(w);
357 free(w);
358 return NULL;
361 w->timescale = timescale;
362 w->vlc_compat = vlc_compat;
364 return w;
367 int mk_writeHeader(mk_Writer *w, const char *writingApp) {
368 mk_Context *c;
370 if (w->wrote_header)
371 return -1;
373 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
374 return -1;
375 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
376 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
377 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
378 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
379 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
380 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
381 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
382 CHECK(mk_closeContext(c, 0));
384 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
385 return -1;
386 CHECK(mk_flushContextID(c));
387 w->segment_ptr = c->d_cur;
388 CHECK(mk_closeContext(c, &w->segment_ptr));
390 if (w->vlc_compat)
392 CHECK(mk_writeVoid(w->root, 256)); // 256 bytes should be enough room for our Seek entries.
393 } else
395 w->seek_data.seekhead = 0x80000000;
396 CHECK(mk_writeSeek(w, &w->seekhead_ptr));
397 w->seek_data.seekhead = 0;
400 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
401 return -1;
402 w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr;
403 CHECK(mk_writeStr(c, 0x4d80, PACKAGE_STRING)); // MuxingApp
404 CHECK(mk_writeStr(c, 0x5741, writingApp)); // WritingApp
405 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale)); // TimecodeScale
406 CHECK(mk_writeFloat(c, 0x4489, 0)); // Duration
407 w->duration_ptr = c->d_cur - 4;
408 CHECK(mk_closeContext(c, &w->duration_ptr));
410 w->seek_data.tracks = w->root->d_cur - w->segment_ptr;
412 if (w->tracks) {
413 CHECK(mk_closeContext(w->tracks, 0));
416 CHECK(mk_flushContextData(w->root));
418 w->wrote_header = 1;
419 w->def_duration = w->tracks_arr[0]->default_duration;
420 return 0;
423 static int mk_closeCluster(mk_Writer *w) {
424 if (w->cluster.context == NULL)
425 return 0;
426 w->cluster.count++;
427 CHECK(mk_closeContext(w->cluster.context, 0));
428 w->cluster.context = NULL;
429 CHECK(mk_flushContextData(w->root));
430 return 0;
433 int mk_flushFrame(mk_Writer *w, mk_Track *track) {
434 mk_Context *c;
435 int64_t delta, ref = 0;
436 unsigned fsize, bgsize;
437 uint8_t c_delta_flags[2];
438 uint8_t flags;
439 int i;
441 if (!track->in_frame)
442 return 0;
444 delta = track->frame.timecode/w->timescale - w->cluster.tc_scaled;
445 if (delta > 32767ll || delta < -32768ll)
446 CHECK(mk_closeCluster(w));
448 if (w->cluster.context == NULL) {
449 w->cluster.tc_scaled = track->frame.timecode / w->timescale;
450 w->cluster.context = mk_createContext(w, w->root, 0x1f43b675); // Cluster
451 if (w->cluster.context == NULL)
452 return -1;
454 w->cluster.pointer = ftell(w->fp) - w->segment_ptr;
456 CHECK(mk_writeUInt(w->cluster.context, 0xe7, w->cluster.tc_scaled)); // Cluster Timecode
458 delta = 0;
459 w->cluster.block_count = 0;
461 if (w->cluster.count % 5 == 0) {
462 for(i = 0; i < w->num_tracks; i++)
463 w->tracks_arr[i]->cue_flag = 1;
467 fsize = track->frame.data ? track->frame.data->d_cur : 0;
468 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
469 if (!track->frame.keyframe) {
470 ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta;
471 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
474 CHECK(mk_writeID(w->cluster.context, 0xa0)); // BlockGroup
475 CHECK(mk_writeSize(w->cluster.context, bgsize));
476 CHECK(mk_writeID(w->cluster.context, 0xa1)); // Block
477 CHECK(mk_writeSize(w->cluster.context, fsize + 4)); //FIXME: Size is incorrect if we're using lacing!
478 CHECK(mk_writeSize(w->cluster.context, track->track_id)); // track number
480 w->cluster.block_count++;
482 c_delta_flags[0] = delta >> 8;
483 c_delta_flags[1] = delta;
484 CHECK(mk_appendContextData(w->cluster.context, c_delta_flags, 2));
486 flags = ( track->frame.keyframe << 8 ) | track->frame.lacing;
487 CHECK(mk_appendContextData(w->cluster.context, &flags, 1));
488 if (track->frame.lacing) {
489 CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1));
490 switch (track->frame.lacing) {
491 case MK_LACING_XIPH:
493 unsigned i, j;
494 uint8_t xiph_size = 255;
495 for (i = 0; i < track->frame.lacing_num_frames; i++)
497 for (j = track->frame.lacing_sizes[i]; j >= 255 ; j -= 255)
499 CHECK(mk_appendContextData(w->cluster.context, &xiph_size, 1));
501 xiph_size = j;
502 CHECK(mk_appendContextData(w->cluster.context, &xiph_size, 1));
505 break;
506 case MK_LACING_EBML:
507 mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0]);
508 for (i = 1; i < track->frame.lacing_num_frames; i++)
510 CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1]));
512 break;
516 if (track->frame.data) {
517 CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, track->frame.data->d_cur));
518 track->frame.data->d_cur = 0;
520 if (!track->frame.keyframe)
521 CHECK(mk_writeSInt(w->cluster.context, 0xfb, ref)); // ReferenceBlock
523 if (track->cue_flag && track->frame.keyframe) {
524 if (w->cue_point.timecode != track->frame.timecode) {
525 if (w->cue_point.context != NULL) {
526 CHECK(mk_closeContext(w->cue_point.context, 0));
527 w->cue_point.context = NULL;
530 if (w->cue_point.context == NULL) {
531 if ((w->cue_point.context = mk_createContext(w, w->cues, 0xbb)) == NULL) // CuePoint
532 return -1;
533 CHECK(mk_writeUInt(w->cue_point.context, 0xb3, track->frame.timecode)); // CueTime
534 w->cue_point.timecode = track->frame.timecode;
537 if ((c = mk_createContext(w, w->cue_point.context, 0xb7)) == NULL) // CueTrackPositions
538 return -1;
539 CHECK(mk_writeUInt(c, 0xf7, track->track_id)); // CueTrack
540 CHECK(mk_writeUInt(c, 0xf1, w->cluster.pointer)); // CueClusterPosition
541 CHECK(mk_writeUInt(c, 0x5378, w->cluster.block_count)); // CueBlockNumber
542 CHECK(mk_closeContext(c, 0));
545 track->in_frame = 0;
546 track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta;
548 if (w->cluster.context->d_cur > CLSIZE)
549 CHECK(mk_closeCluster(w));
551 return 0;
554 int mk_startFrame(mk_Writer *w, mk_Track *track) {
555 if (mk_flushFrame(w, track) < 0)
556 return -1;
558 track->in_frame = 1;
559 // track->keyframe = 0;
560 track->frame.keyframe = 0;
561 track->frame.lacing = MK_LACING_NONE;
562 track->frame.lacing_num_frames = 0;
563 track->frame.lacing_sizes = NULL;
565 return 0;
568 int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe) {
569 if (!track->in_frame)
570 return -1;
572 track->frame.timecode = timestamp;
573 track->frame.keyframe = keyframe != 0;
575 if (track->max_frame_tc < timestamp)
576 track->max_frame_tc = timestamp;
578 return 0;
581 int mk_setFrameLacing(mk_Writer *w, mk_Track *track, uint8_t lacing, uint8_t num_frames, uint32_t sizes[]) {
582 if (!track->in_frame)
583 return -1;
584 track->frame.lacing_sizes = calloc(num_frames, sizeof(uint32_t));
586 track->frame.lacing = lacing;
587 track->frame.lacing_num_frames = num_frames;
588 memcpy(track->frame.lacing_sizes, sizes, num_frames);
590 return 0;
593 int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size) {
594 if (!track->in_frame)
595 return -1;
597 if (track->frame.data == NULL)
598 if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL)
599 return -1;
601 return mk_appendContextData(track->frame.data, data, size);
604 /* The offset of the SeekHead is returned in *pointer. */
605 int mk_writeSeek(mk_Writer *w, int64_t *pointer) {
606 mk_Context *c, *s;
607 int64_t seekhead_ptr;
609 if ((c = mk_createContext(w, w->root, 0x114d9b74)) == NULL) // SeekHead
610 return -1;
611 if (pointer != NULL)
612 seekhead_ptr = ftell(w->fp);
613 if (w->seek_data.seekhead) {
614 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
615 return -1;
616 CHECK(mk_writeUInt(s, 0x53ab, 0x114d9b74)); // SeekID
617 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.seekhead)); // SeekPosition
618 CHECK(mk_closeContext(s, 0));
620 if (w->seek_data.segmentinfo) {
621 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
622 return -1;
623 CHECK(mk_writeUInt(s, 0x53ab, 0x1549a966)); // SeekID
624 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.segmentinfo)); // SeekPosition
625 CHECK(mk_closeContext(s, 0));
627 if (w->seek_data.tracks) {
628 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
629 return -1;
630 CHECK(mk_writeUInt(s, 0x53ab, 0x1654ae6b)); // SeekID
631 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.tracks)); // SeekPosition
632 CHECK(mk_closeContext(s, 0));
634 if (w->seek_data.cues) {
635 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
636 return -1;
637 CHECK(mk_writeUInt(s, 0x53ab, 0x1c53bb6b)); // SeekID
638 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.cues)); // SeekPosition
639 CHECK(mk_closeContext(s, 0));
641 if (w->seek_data.attachments) {
642 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
643 return -1;
644 CHECK(mk_writeUInt(s, 0x53ab, 0x1941a469)); // SeekID
645 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.attachments)); // SeekPosition
646 CHECK(mk_closeContext(s, 0));
648 if (w->seek_data.chapters) {
649 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
650 return -1;
651 CHECK(mk_writeUInt(s, 0x53ab, 0x1043a770)); // SeekID
652 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.chapters)); // SeekPosition
653 CHECK(mk_closeContext(s, 0));
655 if (w->seek_data.tags) {
656 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
657 return -1;
658 CHECK(mk_writeUInt(s, 0x53ab, 0x1254c367)); // SeekID
659 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.tags)); // SeekPosition
660 CHECK(mk_closeContext(s, 0));
662 CHECK(mk_closeContext(c, 0));
664 if (pointer != NULL)
665 *pointer = seekhead_ptr;
667 return 0;
670 int mk_close(mk_Writer *w) {
671 int i, ret = 0;
672 mk_Track *tk;
673 int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc;
675 for (i = w->num_tracks - 1; i >= 0; i--)
677 tk = w->tracks_arr[i];
678 w->tracks_arr[i] = NULL;
679 if (mk_flushFrame(w, tk) < 0)
680 ret = -1;
681 free(tk);
682 tk = NULL;
685 if (mk_closeCluster(w) < 0)
686 ret = -1;
688 if (w->chapters != NULL)
690 w->seek_data.chapters = ftell(w->fp) - w->segment_ptr;
691 mk_writeChapters(w);
692 if (mk_flushContextData(w->root) < 0)
693 ret = -1;
696 w->seek_data.cues = ftell(w->fp) - w->segment_ptr;
697 if (w->cue_point.context != NULL)
698 if (mk_closeContext(w->cue_point.context, 0) < 0)
699 ret = -1;
700 if (mk_closeContext(w->cues, 0) < 0)
701 ret = -1;
702 if (mk_flushContextData(w->root) < 0)
703 ret = -1;
705 if (w->wrote_header) {
706 if (w->vlc_compat)
707 fseek(w->fp, w->segment_ptr, SEEK_SET);
709 if (mk_writeSeek(w, &w->seek_data.seekhead) < 0)
710 ret = -1;
711 w->seek_data.seekhead -= w->segment_ptr;
713 if (w->vlc_compat)
715 if (mk_flushContextData(w->root) < 0)
716 ret = -1;
717 if (mk_writeVoid(w->root, (256 - (ftell(w->fp) - w->segment_ptr))) < 0)
718 ret = -1;
721 if (mk_flushContextData(w->root) < 0)
722 ret = -1;
724 if (!w->vlc_compat)
726 int i = w->seek_data.segmentinfo;
727 w->seek_data.segmentinfo = 0;
728 w->seek_data.tracks = 0;
729 w->seek_data.cues = 0;
730 w->seek_data.chapters = 0;
731 w->seek_data.attachments = 0;
732 w->seek_data.tags = 0;
733 fseek(w->fp, w->segment_ptr, SEEK_SET);
734 if (mk_writeSeek(w, NULL) < 0 ||
735 mk_flushContextData(w->root) < 0)
736 ret = -1;
737 if (((i + w->segment_ptr) - ftell(w->fp) - 2) > 1)
738 if (mk_writeVoid(w->root, (i + w->segment_ptr) - ftell(w->fp) - 2) < 0 ||
739 mk_flushContextData(w->root) < 0)
740 ret = -1;
743 fseek(w->fp, w->duration_ptr, SEEK_SET);
744 if (mk_writeFloatRaw(w->root, (float)((double)(max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
745 mk_flushContextData(w->root) < 0)
746 ret = -1;
749 if (mk_closeContext(w->root, 0) < 1)
750 ret = -1;
751 mk_destroyContexts(w);
752 fclose(w->fp);
753 free(w->tracks_arr);
754 free(w);
756 return ret;