Remove some magic numbers, replace them with the correct RESERVED_ constant.
[libmkv.git] / src / matroska.c
blobd9bea0d27a278410e0a233dd766570fdbb5bc7be
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
6 * Authors: Mike Matsnev
7 * Nathan Caldwell
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
23 #include "config.h"
24 #include "libmkv.h"
25 #include "matroska.h"
26 #include "md5.h"
28 #define RESERVED_SEEKHEAD 0x100
29 /* 256 bytes should be enough room for our Seek entries. */
30 #define RESERVED_CHAPTERS 0x800
31 /* 2048 bytes, hopefully enough for Chapters. */
33 int mk_seekFile(mk_Writer *w, uint64_t pos)
35 if (fseek(w->fp, pos, SEEK_SET))
36 return -1;
38 w->f_pos = pos;
40 if (pos > w->f_eof)
41 w->f_eof = pos;
43 return 0;
46 char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames,
47 uint64_t *output_size)
49 unsigned i, j;
50 uint64_t offset = 0;
51 uint64_t alloc_size = num_frames * 6; /* Complete guess. It gets realloc'd
52 * below if we need more space, though.
54 char *laced = calloc(alloc_size, sizeof(*laced));
55 if (laced == NULL)
56 return NULL;
58 laced[offset++] = num_frames;
59 for (i = 0; i < num_frames; i++) {
60 for (j = sizes[i]; j >= 255; j -= 255) {
61 laced[offset++] = 255;
62 if (offset + 1 >= alloc_size) {
63 int avg_sz = offset / (i - 1); /* Compute approximate average bytes/frame */
64 alloc_size += avg_sz * (num_frames - i);
65 /* Sum average so far and number of frames left
66 * with alloc'd size
68 if ((laced = realloc(laced, alloc_size)) == NULL)
69 return NULL;
72 laced[offset++] = j;
75 if (output_size != NULL)
76 *output_size = offset;
78 return laced;
81 mk_Writer *mk_createWriter(const char *filename, int64_t timescale,
82 uint8_t vlc_compat)
84 mk_Writer *w = calloc(1, sizeof(*w));
85 if (w == NULL)
86 return NULL;
88 w->root = mk_createContext(w, NULL, 0);
89 if (w->root == NULL) {
90 free(w);
91 return NULL;
94 /* Cues */
95 if ((w->cues = mk_createContext(w, w->root, MATROSKA_ID_CUES)) == NULL)
97 mk_destroyContexts(w);
98 free(w);
99 return NULL;
102 if (vlc_compat) {
103 /* SeekHead */
104 if ((w->cluster.seekhead = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL)
106 mk_destroyContexts(w);
107 free(w);
108 return NULL;
112 w->fp = fopen(filename, "wb");
113 if (w->fp == NULL) {
114 mk_destroyContexts(w);
115 free(w);
116 return NULL;
119 w->timescale = timescale;
120 w->vlc_compat = vlc_compat;
122 return w;
125 int mk_writeHeader(mk_Writer *w, const char *writingApp)
127 mk_Context *c;
128 int64_t offset = 0;
130 if (w->wrote_header)
131 return -1;
133 md5_starts(&w->segment_md5); /* Initalize MD5 */
135 CHECK(mk_writeEbmlHeader(w, "matroska", MATROSKA_VERSION, MATROSKA_VERSION));
137 /* Segment */
138 if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEGMENT)) == NULL)
139 return -1;
140 CHECK(mk_flushContextID(c));
141 w->segment_ptr = c->d_cur;
142 CHECK(mk_closeContext(c, &w->segment_ptr));
144 if (w->vlc_compat) {
145 CHECK(mk_writeVoid(w->root, RESERVED_SEEKHEAD)); /* Reserved space for SeekHead */
146 CHECK(mk_writeVoid(w->root, RESERVED_CHAPTERS)); /* Reserved space for Chapters */
147 } else {
148 w->seek_data.seekhead = 0x80000000;
149 CHECK(mk_writeSeekHead(w, &w->seekhead_ptr));
150 w->seek_data.seekhead = 0;
153 if ((c = mk_createContext(w, w->root, MATROSKA_ID_INFO)) == NULL) /* SegmentInfo */
154 return -1;
155 w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr;
156 CHECK(mk_writeVoid(c, 16)); /* Reserve space for a SegmentUID, to be written it later. */
157 CHECK(mk_writeStr(c, MATROSKA_ID_MUXINGAPP, PACKAGE_STRING)); /* MuxingApp */
158 CHECK(mk_writeStr(c, MATROSKA_ID_WRITINGAPP, writingApp)); /* WritingApp */
159 CHECK(mk_writeUInt(c, MATROSKA_ID_TIMECODESCALE, w->timescale)); /* TimecodeScale */
160 CHECK(mk_writeFloat(c, MATROSKA_ID_DURATION, 0)); /* Duration */
161 w->duration_ptr = c->d_cur - 4;
162 CHECK(mk_closeContext(c, &offset));
163 w->duration_ptr += offset;
164 w->segmentuid_ptr = offset;
166 w->seek_data.tracks = w->root->d_cur - w->segment_ptr;
168 if (w->tracks)
169 CHECK(mk_closeContext(w->tracks, 0));
171 CHECK(mk_flushContextData(w->root));
173 w->wrote_header = 1;
174 w->def_duration = w->tracks_arr[0]->default_duration;
175 return 0;
178 static int mk_closeCluster(mk_Writer *w)
180 if (w->cluster.context == NULL)
181 return 0;
182 w->cluster.count++;
183 CHECK(mk_closeContext(w->cluster.context, 0));
184 w->cluster.context = NULL;
185 CHECK(mk_flushContextData(w->root));
187 return 0;
190 int mk_flushFrame(mk_Writer *w, mk_Track *track)
192 mk_Context *c, *tp;
193 int64_t delta, ref = 0;
194 unsigned fsize, bgsize;
195 uint8_t flags, c_delta[2];
196 int i;
197 char *laced = NULL;
198 uint64_t length = 0;
200 if (!track->in_frame)
201 return 0;
203 delta = track->frame.timecode / w->timescale - w->cluster.tc_scaled;
204 /* If switch rapidly back-and-forth between tracks with drastically different timecodes
205 * this causes a new cluster to be written each time a switch is made. This causes
206 * unnecessary overhead.
209 if (delta > 20000ll || delta < -20000ll)
210 CHECK(mk_closeCluster(w));
212 if (w->cluster.context == NULL) {
213 w->cluster.tc_scaled = track->frame.timecode / w->timescale;
214 /* Cluster */
215 w->cluster.context = mk_createContext(w, w->root, MATROSKA_ID_CLUSTER);
216 if (w->cluster.context == NULL)
217 return -1;
219 w->cluster.pointer = w->f_pos - w->segment_ptr;
221 if (w->vlc_compat) {
222 /* Cluster SeekEntry */
223 CHECK(mk_writeSeek(w, w->cluster.seekhead, MATROSKA_ID_CLUSTER,
224 w->cluster.pointer));
227 /* Cluster Timecode */
228 CHECK(mk_writeUInt(w->cluster.context, MATROSKA_ID_CLUSTERTIMECODE, w->cluster.tc_scaled));
230 delta = 0;
231 w->cluster.block_count = 0;
234 /* Calculate the encoded lacing sizes. */
235 switch (track->frame.lacing) {
236 case MK_LACING_XIPH:
237 laced = mk_laceXiph(track->frame.lacing_sizes,
238 track->frame.lacing_num_frames, &length);
239 break;
240 case MK_LACING_EBML:
242 uint64_t u_size = 0;
243 /* Add one below for the frame count. */
244 length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1;
245 for (i = 1; i < track->frame.lacing_num_frames; i++) {
246 u_size = llabs(track->frame.lacing_sizes[i] -
247 track->frame.lacing_sizes[i - 1]);
248 /* Shift by one so we get the right size for a signed number. */
249 length += mk_ebmlSizeSize((u_size) << 1);
251 break;
253 case MK_LACING_FIXED:
255 laced = calloc(1, sizeof(*laced));
256 laced[0] = track->frame.lacing_num_frames;
257 ++length;
258 break;
260 default:
261 break;
264 fsize = track->frame.data ? track->frame.data->d_cur : 0;
265 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4 + length) + 1 + length;
266 if (!track->frame.keyframe) {
267 ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta;
268 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
271 CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCKGROUP)); /* BlockGroup */
272 CHECK(mk_writeSize(w->cluster.context, bgsize));
273 CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCK)); /* Block */
274 CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length)); /* BlockSize */
275 CHECK(mk_writeSize(w->cluster.context, track->track_id)); /* track number */
277 w->cluster.block_count++;
279 c_delta[0] = delta >> 8;
280 c_delta[1] = delta;
281 /* Timecode relative to Cluster. */
282 CHECK(mk_appendContextData(w->cluster.context, c_delta, 2));
284 /* flags = ( track->frame.keyframe << 8 ) | track->frame.lacing; */
285 flags = track->frame.lacing << 1; /* Flags: Bit 5-6 describe what type of lacing to use. */
286 CHECK(mk_appendContextData(w->cluster.context, &flags, 1));
287 if (track->frame.lacing) {
288 if (track->frame.lacing == MK_LACING_EBML) {
289 /* Number of frames in lace - 1 */
290 CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1));
291 /* Size of 1st frame. */
292 CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0]));
293 for (i = 1; i < track->frame.lacing_num_frames; i++) {
294 /* Size difference between previous size and this size. */
295 CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i - 1]));
297 } else if (length > 0 && laced != NULL) {
298 CHECK(mk_appendContextData(w->cluster.context, laced, length));
299 free(laced);
300 laced = NULL;
304 if (track->frame.data) {
305 CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data,
306 track->frame.data->d_cur));
307 track->frame.data->d_cur = 0;
309 if (!track->frame.keyframe)
310 /* ReferenceBlock */
311 CHECK(mk_writeSInt(w->cluster.context, MATROSKA_ID_REFERENCEBLOCK, ref));
313 /* This may get a little out of hand, but it seems sane enough for now. */
314 if (track->frame.keyframe && (track->track_type == MK_TRACK_VIDEO)) {
315 /* if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && ((track->prev_cue_pos + 3*CLSIZE) <= w->f_pos || track->frame.timecode == 0)) { */
316 /* CuePoint */
317 if ((c = mk_createContext(w, w->cues, MATROSKA_ID_CUEPOINT)) == NULL)
318 return -1;
319 /* CueTime */
320 CHECK(mk_writeUInt(c, MATROSKA_ID_CUETIME, (track->frame.timecode / w->timescale)));
322 /* CueTrackPositions */
323 if ((tp = mk_createContext(w, c, MATROSKA_ID_CUETRACKPOSITIONS)) == NULL)
324 return -1;
325 /* CueTrack */
326 CHECK(mk_writeUInt(tp, MATROSKA_ID_CUETRACK, track->track_id));
327 /* CueClusterPosition */
328 CHECK(mk_writeUInt(tp, MATROSKA_ID_CUECLUSTERPOSITION, w->cluster.pointer));
329 /* CueBlockNumber */
330 /* CHECK(mk_writeUInt(c, MATROSKA_ID_CUEBLOCKNUMBER, w->cluster.block_count)); */
331 CHECK(mk_closeContext(tp, 0));
332 CHECK(mk_closeContext(c, 0));
333 track->prev_cue_pos = w->f_pos;
336 track->in_frame = 0;
337 track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta;
339 /* Write a new Cluster ~ every 5MB */
340 if (w->cluster.context->d_cur > 5 * CLSIZE)
341 CHECK(mk_closeCluster(w));
343 return 0;
346 int mk_startFrame(mk_Writer *w, mk_Track *track)
348 if (mk_flushFrame(w, track) < 0)
349 return -1;
351 track->in_frame = 1;
352 track->frame.keyframe = 0;
353 track->frame.lacing = MK_LACING_NONE;
354 track->frame.lacing_num_frames = 0;
355 track->frame.lacing_sizes = NULL;
357 return 0;
360 int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp,
361 unsigned keyframe)
363 if (!track->in_frame)
364 return -1;
366 track->frame.timecode = timestamp;
367 track->frame.keyframe = keyframe != 0;
369 if (track->max_frame_tc < timestamp)
370 track->max_frame_tc = timestamp;
372 return 0;
375 int mk_setFrameLacing(mk_Writer *w, mk_Track *track,
376 mk_LacingType lacing, uint8_t num_frames,
377 uint64_t sizes[])
379 if (!track->in_frame)
380 return -1;
381 track->frame.lacing_sizes = calloc(num_frames, sizeof(*sizes));
383 track->frame.lacing = lacing;
384 track->frame.lacing_num_frames = num_frames;
385 memcpy(track->frame.lacing_sizes, sizes,
386 num_frames * sizeof(*sizes));
388 return 0;
391 int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data,
392 unsigned size)
394 if (!track->in_frame)
395 return -1;
397 if (track->frame.data == NULL) {
398 if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL)
399 return -1;
402 md5_update(&w->segment_md5, (unsigned char *) data, size);
404 return mk_appendContextData(track->frame.data, data, size);
407 int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id,
408 uint64_t seek_pos)
410 mk_Context *s;
412 if ((s = mk_createContext(w, c, MATROSKA_ID_SEEKENTRY)) == NULL) /* Seek */
413 return -1;
414 CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKID, seek_id)); /* SeekID */
415 CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKPOSITION, seek_pos)); /* SeekPosition */
416 CHECK(mk_closeContext(s, 0));
418 return 0;
421 /* The offset of the SeekHead is returned in pointer. */
422 int mk_writeSeekHead(mk_Writer *w, int64_t *pointer)
424 mk_Context *c;
425 int64_t seekhead_ptr;
427 /* SeekHead */
428 if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL)
429 return -1;
430 if (pointer != NULL)
431 seekhead_ptr = w->f_pos;
432 if (w->seek_data.seekhead)
433 CHECK(mk_writeSeek(w, c, MATROSKA_ID_SEEKHEAD, w->seek_data.seekhead));
434 if (w->seek_data.segmentinfo)
435 CHECK(mk_writeSeek(w, c, MATROSKA_ID_INFO, w->seek_data.segmentinfo));
436 if (w->seek_data.tracks)
437 CHECK(mk_writeSeek(w, c, MATROSKA_ID_TRACKS, w->seek_data.tracks));
438 if (w->seek_data.cues)
439 CHECK(mk_writeSeek(w, c, MATROSKA_ID_CUES, w->seek_data.cues));
440 if (w->seek_data.attachments)
441 CHECK(mk_writeSeek(w, c, MATROSKA_ID_ATTACHMENTS, w->seek_data.attachments));
442 if (w->seek_data.chapters)
443 CHECK(mk_writeSeek(w, c, MATROSKA_ID_CHAPTERS, w->seek_data.chapters));
444 if (w->seek_data.tags)
445 CHECK(mk_writeSeek(w, c, MATROSKA_ID_TAGS, w->seek_data.tags));
446 CHECK(mk_closeContext(c, 0));
448 if (pointer != NULL)
449 *pointer = seekhead_ptr;
451 return 0;
454 int mk_close(mk_Writer *w)
456 int i, ret = 0;
457 mk_Track *tk;
458 int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc;
459 uint64_t segment_size = 0;
460 unsigned char c_size[8];
461 unsigned char segment_uid[16];
463 md5_finish(&w->segment_md5, segment_uid);
465 for (i = w->num_tracks - 1; i >= 0; i--) {
466 tk = w->tracks_arr[i];
467 w->tracks_arr[i] = NULL;
468 --w->num_tracks;
469 if (mk_flushFrame(w, tk) < 0)
470 ret = -1;
471 free(tk);
472 tk = NULL;
475 if (mk_closeCluster(w) < 0)
476 ret = -1;
478 w->seek_data.cues = w->f_pos - w->segment_ptr;
479 if (mk_closeContext(w->cues, 0) < 0)
480 ret = -1;
481 if (mk_flushContextData(w->root) < 0)
482 ret = -1;
484 if (w->vlc_compat && w->cluster.seekhead) {
485 w->seek_data.seekhead = w->f_pos - w->segment_ptr;
486 if (mk_closeContext(w->cluster.seekhead, 0) < 0)
487 ret = -1;
488 if (mk_flushContextData(w->root) < 0)
489 ret = -1;
492 if (w->chapters != NULL) {
493 if (w->vlc_compat) {
494 if (mk_flushContextData(w->root) < 0)
495 ret = -1;
496 if (mk_seekFile(w, w->segment_ptr + RESERVED_SEEKHEAD + 3) < 0)
497 ret = -1;
499 w->seek_data.chapters = w->f_pos - w->segment_ptr;
500 mk_writeChapters(w);
501 if (mk_flushContextData(w->root) < 0)
502 ret = -1;
503 if (w->vlc_compat) {
504 if (mk_writeVoid(w->root, (RESERVED_CHAPTERS - (w->f_pos - w->segment_ptr - RESERVED_SEEKHEAD - 3))) < 0)
505 ret = -1;
506 if (mk_flushContextData(w->root) < 0)
507 ret = -1;
511 if (w->tags != NULL) {
512 w->seek_data.tags = w->f_pos - w->segment_ptr;
513 mk_writeTags(w);
514 if (mk_flushContextData(w->root) < 0)
515 ret = -1;
518 if (w->wrote_header) {
519 if (w->vlc_compat) {
520 if (mk_seekFile(w, w->segment_ptr) < 0)
521 ret = -1;
524 if (mk_writeSeekHead(w, &w->seek_data.seekhead) < 0)
525 ret = -1;
526 w->seek_data.seekhead -= w->segment_ptr;
528 if (w->vlc_compat) {
529 if (mk_flushContextData(w->root) < 0)
530 ret = -1;
531 if (mk_writeVoid(w->root, (RESERVED_SEEKHEAD - (w->f_pos - w->segment_ptr))) < 0)
532 ret = -1;
535 if (mk_flushContextData(w->root) < 0)
536 ret = -1;
538 if (!w->vlc_compat) {
539 int i = w->seek_data.segmentinfo;
540 w->seek_data.segmentinfo = 0;
541 w->seek_data.tracks = 0;
542 w->seek_data.cues = 0;
543 w->seek_data.chapters = 0;
544 w->seek_data.attachments = 0;
545 w->seek_data.tags = 0;
546 if (mk_seekFile(w, w->segment_ptr) < 0)
547 ret = -1;
548 if (mk_writeSeekHead(w, NULL) < 0 || mk_flushContextData(w->root) < 0)
549 ret = -1;
550 // The conditional below is easier to understand, but incorrect
551 // because f_pos is unsigned and causes the lhs to be evaluated
552 // as an unsigned quantity.
553 // if (((i + w->segment_ptr) - w->f_pos - 2) > 1)
554 if ((i + w->segment_ptr) > (w->f_pos + 3))
555 if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0
556 || mk_flushContextData(w->root) < 0)
557 ret = -1;
560 if (mk_seekFile(w, w->duration_ptr) < 0)
561 ret = -1;
562 if (mk_writeFloatRaw(w->root,
563 (float) ((double) (max_frame_tc + w->def_duration) /
564 w->timescale)) < 0
565 || mk_flushContextData(w->root) < 0)
566 ret = -1;
567 if (mk_seekFile(w, w->segment_ptr - 8) < 0)
568 ret = -1;
569 segment_size = w->f_eof - w->segment_ptr;
570 for (i = 7; i > 0; --i)
571 c_size[i] = segment_size >> (8 * (7 - i));
572 c_size[i] = 0x01;
573 if (mk_appendContextData(w->root, &c_size, 8) < 0 ||
574 mk_flushContextData(w->root) < 0)
575 ret = -1;
576 if (mk_seekFile(w, w->segmentuid_ptr) < 0)
577 ret = -1;
578 if (mk_writeBin(w->root, MATROSKA_ID_SEGMENTUID, segment_uid,
579 sizeof(segment_uid)) < 0 ||
580 mk_flushContextData(w->root) < 0)
581 ret = -1;
584 if (mk_closeContext(w->root, 0) < 0)
585 ret = -1;
586 mk_destroyContexts(w);
587 fclose(w->fp);
588 free(w->tracks_arr);
589 free(w);
591 return ret;