1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
7 * Authors: Mike Matsnev
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 *****************************************************************************/
28 mk_Context
*mk_createContext(mk_Writer
*w
, mk_Context
*parent
, unsigned id
) {
33 w
->freelist
= w
->freelist
->next
;
35 c
= malloc(sizeof(*c
));
36 memset(c
, 0, sizeof(*c
));
46 if (c
->owner
->actlist
)
47 c
->owner
->actlist
->prev
= &c
->next
;
48 c
->next
= c
->owner
->actlist
;
49 c
->prev
= &c
->owner
->actlist
;
54 static int mk_appendContextData(mk_Context
*c
, const void *data
, unsigned size
) {
55 unsigned ns
= c
->d_cur
+ size
;
59 unsigned dn
= c
->d_max
? c
->d_max
<< 1 : 16;
63 dp
= realloc(c
->data
, dn
);
71 memcpy((char*)c
->data
+ c
->d_cur
, data
, size
);
78 static int mk_writeID(mk_Context
*c
, unsigned id
) {
79 unsigned char c_id
[4] = { id
>> 24, id
>> 16, id
>> 8, id
};
82 return mk_appendContextData(c
, c_id
, 4);
84 return mk_appendContextData(c
, c_id
+1, 3);
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
};
95 return mk_appendContextData(c
, c_size
+7, 1);
99 return mk_appendContextData(c
, c_size
+6, 2);
101 if (size
< 0x1fffff) {
103 return mk_appendContextData(c
, c_size
+5, 3);
105 if (size
< 0x0fffffff) {
107 return mk_appendContextData(c
, c_size
+4, 4);
109 if (size
< 0x07ffffffff) {
111 return mk_appendContextData(c
, c_size
+3, 5);
113 if (size
< 0x03ffffffffff) {
115 return mk_appendContextData(c
, c_size
+2, 6);
117 if (size
< 0x01ffffffffffff) {
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.
143 size
+= 0x03ffffffff;
146 size
+= 0x01ffffffffff;
149 size
+= 0x00ffffffffffff;
151 default: // Matroska currently doesn't support any int > 56-bit.
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};
164 CHECK(mk_writeID(c
->parent
, c
->id
));
165 CHECK(mk_appendContextData(c
->parent
, &size_undf
, 8));
172 int mk_flushContextData(mk_Context
*c
) {
177 CHECK(mk_appendContextData(c
->parent
, c
->data
, c
->d_cur
));
179 if (fwrite(c
->data
, c
->d_cur
, 1, c
->owner
->fp
) != 1)
187 int mk_closeContext(mk_Context
*c
, int64_t *off
) {
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
));
199 c
->next
->prev
= c
->prev
;
200 *(c
->prev
) = c
->next
;
201 c
->next
= c
->owner
->freelist
;
202 c
->owner
->freelist
= c
;
207 static void mk_destroyContexts(mk_Writer
*w
) {
208 mk_Context
*cur
, *next
;
210 for (cur
= w
->freelist
; cur
; cur
= next
) {
216 for (cur
= w
->actlist
; cur
; cur
= next
) {
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
));
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
));
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
};
245 CHECK(mk_writeID(c
, id
));
246 while (i
< 7 && c_ui
[i
] == 0)
248 CHECK(mk_writeSize(c
, 8 - i
));
249 CHECK(mk_appendContextData(c
, c_ui
+i
, 8 - i
));
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
};
257 CHECK(mk_writeID(c
, id
));
259 while (i
< 7 && c_si
[i
] == 0xff && c_si
[i
+1] & 0x80)
262 while (i
< 7 && c_si
[i
] == 0 && !(c_si
[i
+1] & 0x80))
264 CHECK(mk_writeSize(c
, 8 - i
));
265 CHECK(mk_appendContextData(c
, c_si
+i
, 8 - i
));
269 static int mk_writeFloatRaw(mk_Context
*c
, float f
) {
274 unsigned char c_f
[4];
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
));
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
));
302 static unsigned mk_ebmlSizeSize(uint64_t s
) {
311 if (s
< 0x07ffffffff)
313 if (s
< 0x03ffffffffff)
315 if (s
< 0x01ffffffffffff)
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
};
325 while (i
< 7 && c_si
[i
] == 0xff && c_si
[i
+1] & 0x80)
328 while (i
< 7 && c_si
[i
] == 0 && !(c_si
[i
+1] & 0x80))
334 mk_Writer
*mk_createWriter(const char *filename
, int64_t timescale
, uint8_t vlc_compat
) {
335 mk_Writer
*w
= malloc(sizeof(*w
));
339 memset(w
, 0, sizeof(*w
));
341 w
->root
= mk_createContext(w
, NULL
, 0);
342 if (w
->root
== NULL
) {
347 if ((w
->cues
= mk_createContext(w
, w
->root
, 0x1c53bb6b)) == NULL
) // Cues
349 mk_destroyContexts(w
);
354 w
->fp
= fopen(filename
, "wb");
356 mk_destroyContexts(w
);
361 w
->timescale
= timescale
;
362 w
->vlc_compat
= vlc_compat
;
367 int mk_writeHeader(mk_Writer
*w
, const char *writingApp
) {
373 if ((c
= mk_createContext(w
, w
->root
, 0x1a45dfa3)) == NULL
) // EBML
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
386 CHECK(mk_flushContextID(c
));
387 w
->segment_ptr
= c
->d_cur
;
388 CHECK(mk_closeContext(c
, &w
->segment_ptr
));
392 CHECK(mk_writeVoid(w
->root
, 256)); // 256 bytes should be enough room for our Seek entries.
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
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
;
413 CHECK(mk_closeContext(w
->tracks
, 0));
416 CHECK(mk_flushContextData(w
->root
));
419 w
->def_duration
= w
->tracks_arr
[0]->default_duration
;
423 static int mk_closeCluster(mk_Writer
*w
) {
424 if (w
->cluster
.context
== NULL
)
427 CHECK(mk_closeContext(w
->cluster
.context
, 0));
428 w
->cluster
.context
= NULL
;
429 CHECK(mk_flushContextData(w
->root
));
433 int mk_flushFrame(mk_Writer
*w
, mk_Track
*track
) {
435 int64_t delta
, ref
= 0;
436 unsigned fsize
, bgsize
;
437 uint8_t c_delta_flags
[2];
441 if (!track
->in_frame
)
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
)
454 w
->cluster
.pointer
= ftell(w
->fp
) - w
->segment_ptr
;
456 CHECK(mk_writeUInt(w
->cluster
.context
, 0xe7, w
->cluster
.tc_scaled
)); // Cluster Timecode
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
) {
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));
502 CHECK(mk_appendContextData(w
->cluster
.context
, &xiph_size
, 1));
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]));
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
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
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));
546 track
->prev_frame_tc_scaled
= w
->cluster
.tc_scaled
+ delta
;
548 if (w
->cluster
.context
->d_cur
> CLSIZE
)
549 CHECK(mk_closeCluster(w
));
554 int mk_startFrame(mk_Writer
*w
, mk_Track
*track
) {
555 if (mk_flushFrame(w
, track
) < 0)
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
;
568 int mk_setFrameFlags(mk_Writer
*w
, mk_Track
*track
, int64_t timestamp
, unsigned keyframe
) {
569 if (!track
->in_frame
)
572 track
->frame
.timecode
= timestamp
;
573 track
->frame
.keyframe
= keyframe
!= 0;
575 if (track
->max_frame_tc
< timestamp
)
576 track
->max_frame_tc
= timestamp
;
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
)
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
);
593 int mk_addFrameData(mk_Writer
*w
, mk_Track
*track
, const void *data
, unsigned size
) {
594 if (!track
->in_frame
)
597 if (track
->frame
.data
== NULL
)
598 if ((track
->frame
.data
= mk_createContext(w
, NULL
, 0)) == NULL
)
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
) {
607 int64_t seekhead_ptr
;
609 if ((c
= mk_createContext(w
, w
->root
, 0x114d9b74)) == NULL
) // SeekHead
612 seekhead_ptr
= ftell(w
->fp
);
613 if (w
->seek_data
.seekhead
) {
614 if ((s
= mk_createContext(w
, c
, 0x4dbb)) == NULL
) // Seek
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
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
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
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
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
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
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));
665 *pointer
= seekhead_ptr
;
670 int mk_close(mk_Writer
*w
) {
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)
685 if (mk_closeCluster(w
) < 0)
688 if (w
->chapters
!= NULL
)
690 w
->seek_data
.chapters
= ftell(w
->fp
) - w
->segment_ptr
;
692 if (mk_flushContextData(w
->root
) < 0)
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)
700 if (mk_closeContext(w
->cues
, 0) < 0)
702 if (mk_flushContextData(w
->root
) < 0)
705 if (w
->wrote_header
) {
707 fseek(w
->fp
, w
->segment_ptr
, SEEK_SET
);
709 if (mk_writeSeek(w
, &w
->seek_data
.seekhead
) < 0)
711 w
->seek_data
.seekhead
-= w
->segment_ptr
;
715 if (mk_flushContextData(w
->root
) < 0)
717 if (mk_writeVoid(w
->root
, (256 - (ftell(w
->fp
) - w
->segment_ptr
))) < 0)
721 if (mk_flushContextData(w
->root
) < 0)
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)
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)
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)
749 if (mk_closeContext(w
->root
, 0) < 1)
751 mk_destroyContexts(w
);