Fix chapters correctly this time.
[libmkv.git] / src / matroska.c
blob51c3506b537b136abb75567b8766f10e78949a5c
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 int mk_seekFile(mk_Writer *w, uint64_t pos) {
29 if (fseek(w->fp, pos, SEEK_SET))
30 return -1;
32 w->f_pos = pos;
34 if (pos > w->f_eof)
35 w->f_eof = pos;
37 return 0;
40 char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, uint64_t *output_size) {
41 unsigned i, j;
42 uint64_t offset = 0;
43 uint64_t alloc_size = num_frames * 6; // Complete guess. We'll realloc if we need more space, though.
44 char *laced = calloc(alloc_size, sizeof(char));
45 if (laced == NULL)
46 return NULL;
48 laced[offset++] = num_frames;
49 for (i = 0; i < num_frames; i++)
51 for (j = sizes[i]; j >= 255 ; j -= 255)
53 laced[offset++] = 255;
54 if (offset + 1 >= alloc_size) {
55 int avg_sz = offset / (i - 1); // Compute approximate average bytes/frame
56 alloc_size += avg_sz * (num_frames - i); // Add our average + number of frames left to size
57 if ((laced = realloc(laced, alloc_size)) == NULL)
58 return NULL;
61 laced[offset++] = j;
64 if (output_size != NULL)
65 *output_size = offset;
67 return laced;
70 mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat) {
71 mk_Writer *w = calloc(1, sizeof(*w));
72 if (w == NULL)
73 return NULL;
75 w->root = mk_createContext(w, NULL, 0);
76 if (w->root == NULL) {
77 free(w);
78 return NULL;
81 if ((w->cues = mk_createContext(w, w->root, 0x1c53bb6b)) == NULL) // Cues
83 mk_destroyContexts(w);
84 free(w);
85 return NULL;
88 if (vlc_compat) {
89 if ((w->cluster.seekhead = mk_createContext(w, w->root, 0x114d9b74)) == NULL) // SeekHead
91 mk_destroyContexts(w);
92 free(w);
93 return NULL;
97 w->fp = fopen(filename, "wb");
98 if (w->fp == NULL) {
99 mk_destroyContexts(w);
100 free(w);
101 return NULL;
104 w->timescale = timescale;
105 w->vlc_compat = vlc_compat;
107 return w;
110 int mk_writeHeader(mk_Writer *w, const char *writingApp) {
111 mk_Context *c;
113 if (w->wrote_header)
114 return -1;
116 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
117 return -1;
118 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
119 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
120 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
121 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
122 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
123 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
124 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
125 CHECK(mk_closeContext(c, 0));
127 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
128 return -1;
129 CHECK(mk_flushContextID(c));
130 w->segment_ptr = c->d_cur;
131 CHECK(mk_closeContext(c, &w->segment_ptr));
133 if (w->vlc_compat) {
134 CHECK(mk_writeVoid(w->root, 0x100)); // 256 bytes should be enough room for our Seek entries.
135 CHECK(mk_writeVoid(w->root, 0x800)); // 2048 bytes for Chapters.
137 else {
138 w->seek_data.seekhead = 0x80000000;
139 CHECK(mk_writeSeekHead(w, &w->seekhead_ptr));
140 w->seek_data.seekhead = 0;
143 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
144 return -1;
145 w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr;
146 CHECK(mk_writeStr(c, 0x4d80, PACKAGE_STRING)); // MuxingApp
147 CHECK(mk_writeStr(c, 0x5741, writingApp)); // WritingApp
148 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale)); // TimecodeScale
149 CHECK(mk_writeFloat(c, 0x4489, 0)); // Duration
150 w->duration_ptr = c->d_cur - 4;
151 CHECK(mk_closeContext(c, &w->duration_ptr));
153 w->seek_data.tracks = w->root->d_cur - w->segment_ptr;
155 if (w->tracks)
156 CHECK(mk_closeContext(w->tracks, 0));
158 CHECK(mk_flushContextData(w->root));
160 w->wrote_header = 1;
161 w->def_duration = w->tracks_arr[0]->default_duration;
162 return 0;
165 static int mk_closeCluster(mk_Writer *w) {
166 if (w->cluster.context == NULL)
167 return 0;
168 w->cluster.count++;
169 CHECK(mk_closeContext(w->cluster.context, 0));
170 w->cluster.context = NULL;
171 CHECK(mk_flushContextData(w->root));
172 return 0;
175 int mk_flushFrame(mk_Writer *w, mk_Track *track) {
176 mk_Context *c, *tp;
177 int64_t delta, ref = 0;
178 unsigned fsize, bgsize;
179 uint8_t flags, c_delta_flags[2];
180 int i;
181 char *laced = NULL;
182 uint64_t length = 0;
184 if (!track->in_frame)
185 return 0;
187 delta = track->frame.timecode/w->timescale - w->cluster.tc_scaled;
188 if (delta > 2000ll || delta < -2000ll)
189 CHECK(mk_closeCluster(w));
191 if (w->cluster.context == NULL) {
192 w->cluster.tc_scaled = track->frame.timecode / w->timescale;
193 w->cluster.context = mk_createContext(w, w->root, 0x1f43b675); // Cluster
194 if (w->cluster.context == NULL)
195 return -1;
197 w->cluster.pointer = w->f_pos - w->segment_ptr;
199 if (w->vlc_compat)
200 CHECK(mk_writeSeek(w, w->cluster.seekhead, 0x1f43b675, w->cluster.pointer));
202 CHECK(mk_writeUInt(w->cluster.context, 0xe7, w->cluster.tc_scaled)); // Cluster Timecode
204 delta = 0;
205 w->cluster.block_count = 0;
208 fsize = track->frame.data ? track->frame.data->d_cur : 0;
209 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
210 if (!track->frame.keyframe) {
211 ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta;
212 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
215 CHECK(mk_writeID(w->cluster.context, 0xa0)); // BlockGroup
216 CHECK(mk_writeSize(w->cluster.context, bgsize));
217 CHECK(mk_writeID(w->cluster.context, 0xa1)); // Block
219 switch (track->frame.lacing) {
220 case MK_LACING_XIPH:
221 laced = mk_laceXiph(track->frame.lacing_sizes, track->frame.lacing_num_frames, &length);
222 break;
223 case MK_LACING_EBML:
224 length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1;
225 for (i = 1; i < track->frame.lacing_num_frames; i++)
226 length += mk_ebmlSizeSize(track->frame.lacing_sizes[i] << 1);
227 break;
228 case MK_LACING_FIXED:
230 laced = calloc(1, sizeof(char));
231 laced[0] = track->frame.lacing_num_frames;
232 ++length;
234 break;
237 CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length));
238 CHECK(mk_writeSize(w->cluster.context, track->track_id)); // track number
240 w->cluster.block_count++;
242 c_delta_flags[0] = delta >> 8;
243 c_delta_flags[1] = delta;
244 CHECK(mk_appendContextData(w->cluster.context, c_delta_flags, 2));
246 flags = ( track->frame.keyframe << 8 ) | track->frame.lacing;
247 CHECK(mk_appendContextData(w->cluster.context, &flags, 1));
248 if (track->frame.lacing) {
249 if (track->frame.lacing == MK_LACING_EBML) {
250 CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1));
251 CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0]));
252 for (i = 1; i < track->frame.lacing_num_frames; i++)
254 CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1]));
256 } else if (length > 0 && laced != NULL) {
257 CHECK(mk_appendContextData(w->cluster.context, laced, length));
258 free(laced);
259 laced = NULL;
263 if (track->frame.data) {
264 CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, track->frame.data->d_cur));
265 track->frame.data->d_cur = 0;
267 if (!track->frame.keyframe)
268 CHECK(mk_writeSInt(w->cluster.context, 0xfb, ref)); // ReferenceBlock
270 if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && (track->prev_cue_pos + 3*CLSIZE) >= w->f_pos) {
271 if ((c = mk_createContext(w, w->cues, 0xbb)) == NULL) // CuePoint
272 return -1;
273 CHECK(mk_writeUInt(c, 0xb3, track->frame.timecode)); // CueTime
275 if ((tp = mk_createContext(w, c, 0xb7)) == NULL) // CueTrackPositions
276 return -1;
277 CHECK(mk_writeUInt(tp, 0xf7, track->track_id)); // CueTrack
278 CHECK(mk_writeUInt(tp, 0xf1, w->cluster.pointer)); // CueClusterPosition
279 // CHECK(mk_writeUInt(c, 0x5378, w->cluster.block_count)); // CueBlockNumber
280 CHECK(mk_closeContext(tp, 0));
281 CHECK(mk_closeContext(c, 0));
284 track->in_frame = 0;
285 track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta;
287 if (w->cluster.context->d_cur > CLSIZE)
288 CHECK(mk_closeCluster(w));
290 return 0;
293 int mk_startFrame(mk_Writer *w, mk_Track *track) {
294 if (mk_flushFrame(w, track) < 0)
295 return -1;
297 track->in_frame = 1;
298 track->frame.keyframe = 0;
299 track->frame.lacing = MK_LACING_NONE;
300 track->frame.lacing_num_frames = 0;
301 track->frame.lacing_sizes = NULL;
303 return 0;
306 int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe) {
307 if (!track->in_frame)
308 return -1;
310 track->frame.timecode = timestamp;
311 track->frame.keyframe = keyframe != 0;
313 if (track->max_frame_tc < timestamp)
314 track->max_frame_tc = timestamp;
316 return 0;
319 int mk_setFrameLacing(mk_Writer *w, mk_Track *track, uint8_t lacing, uint8_t num_frames, uint32_t sizes[]) {
320 if (!track->in_frame)
321 return -1;
322 track->frame.lacing_sizes = calloc(num_frames, sizeof(uint32_t));
324 track->frame.lacing = lacing;
325 track->frame.lacing_num_frames = num_frames;
326 memcpy(track->frame.lacing_sizes, sizes, num_frames);
328 return 0;
331 int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size) {
332 if (!track->in_frame)
333 return -1;
335 if (track->frame.data == NULL)
336 if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL)
337 return -1;
339 return mk_appendContextData(track->frame.data, data, size);
342 int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, uint64_t seek_pos) {
343 mk_Context *s;
345 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
346 return -1;
347 CHECK(mk_writeUInt(s, 0x53ab, seek_id)); // SeekID
348 CHECK(mk_writeUInt(s, 0x53ac, seek_pos)); // SeekPosition
349 CHECK(mk_closeContext(s, 0));
351 return 0;
354 /* The offset of the SeekHead is returned in *pointer. */
355 int mk_writeSeekHead(mk_Writer *w, int64_t *pointer) {
356 mk_Context *c;
357 int64_t seekhead_ptr;
359 if ((c = mk_createContext(w, w->root, 0x114d9b74)) == NULL) // SeekHead
360 return -1;
361 if (pointer != NULL)
362 seekhead_ptr = w->f_pos;
363 if (w->seek_data.seekhead)
364 CHECK(mk_writeSeek(w, c, 0x114d9b74, w->seek_data.seekhead));
365 if (w->seek_data.segmentinfo)
366 CHECK(mk_writeSeek(w, c, 0x1549a966, w->seek_data.segmentinfo));
367 if (w->seek_data.tracks)
368 CHECK(mk_writeSeek(w, c, 0x1654ae6b, w->seek_data.tracks));
369 if (w->seek_data.cues)
370 CHECK(mk_writeSeek(w, c, 0x1c53bb6b, w->seek_data.cues));
371 if (w->seek_data.attachments)
372 CHECK(mk_writeSeek(w, c, 0x1941a469, w->seek_data.attachments));
373 if (w->seek_data.chapters)
374 CHECK(mk_writeSeek(w, c, 0x1043a770, w->seek_data.chapters));
375 if (w->seek_data.tags)
376 CHECK(mk_writeSeek(w, c, 0x1254c367, w->seek_data.tags));
377 CHECK(mk_closeContext(c, 0));
379 if (pointer != NULL)
380 *pointer = seekhead_ptr;
382 return 0;
385 int mk_close(mk_Writer *w) {
386 int i, ret = 0;
387 mk_Track *tk;
388 int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc;
389 uint64_t segment_size = 0;
390 unsigned char c_size[8];
392 for (i = w->num_tracks - 1; i >= 0; i--)
394 tk = w->tracks_arr[i];
395 w->tracks_arr[i] = NULL;
396 --w->num_tracks;
397 if (mk_flushFrame(w, tk) < 0)
398 ret = -1;
399 free(tk);
400 tk = NULL;
403 if (mk_closeCluster(w) < 0)
404 ret = -1;
406 w->seek_data.cues = w->f_pos - w->segment_ptr;
407 if (mk_closeContext(w->cues, 0) < 0)
408 ret = -1;
409 if (mk_flushContextData(w->root) < 0)
410 ret = -1;
412 if (w->vlc_compat && w->cluster.seekhead) {
413 w->seek_data.seekhead = w->f_pos - w->segment_ptr;
414 if (mk_closeContext(w->cluster.seekhead, 0) < 0)
415 ret = -1;
416 if (mk_flushContextData(w->root) < 0)
417 ret = -1;
420 if (w->chapters != NULL)
422 if (w->vlc_compat) {
423 if (mk_flushContextData(w->root) < 0)
424 ret = -1;
425 if (mk_seekFile(w, w->segment_ptr + 0x100 + 3) < 0)
426 ret = -1;
428 w->seek_data.chapters = w->f_pos - w->segment_ptr;
429 mk_writeChapters(w);
430 if (w->vlc_compat) {
431 if (mk_writeVoid(w->root, (0x800 - (w->f_pos - w->segment_ptr - 0x100 - 3))) < 0)
432 ret = -1;
434 if (mk_flushContextData(w->root) < 0)
435 ret = -1;
438 if (w->wrote_header) {
439 if (w->vlc_compat) {
440 if (mk_seekFile(w, w->segment_ptr) < 0)
441 ret = -1;
444 if (mk_writeSeekHead(w, &w->seek_data.seekhead) < 0)
445 ret = -1;
446 w->seek_data.seekhead -= w->segment_ptr;
448 if (w->vlc_compat)
450 if (mk_flushContextData(w->root) < 0)
451 ret = -1;
452 if (mk_writeVoid(w->root, (0x100 - (w->f_pos - w->segment_ptr))) < 0)
453 ret = -1;
456 if (mk_flushContextData(w->root) < 0)
457 ret = -1;
459 if (!w->vlc_compat)
461 int i = w->seek_data.segmentinfo;
462 w->seek_data.segmentinfo = 0;
463 w->seek_data.tracks = 0;
464 w->seek_data.cues = 0;
465 w->seek_data.chapters = 0;
466 w->seek_data.attachments = 0;
467 w->seek_data.tags = 0;
468 if (mk_seekFile(w, w->segment_ptr) < 0)
469 ret = -1;
470 if (mk_writeSeekHead(w, NULL) < 0 ||
471 mk_flushContextData(w->root) < 0)
472 ret = -1;
473 if (((i + w->segment_ptr) - w->f_pos - 2) > 1)
474 if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0 ||
475 mk_flushContextData(w->root) < 0)
476 ret = -1;
479 if (mk_seekFile(w, w->duration_ptr) < 0)
480 ret = -1;
481 if (mk_writeFloatRaw(w->root, (float)((double)(max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
482 mk_flushContextData(w->root) < 0)
483 ret = -1;
484 if (mk_seekFile(w, w->segment_ptr - 8) < 0)
485 ret = -1;
486 segment_size = w->f_eof - w->segment_ptr;
487 for (i = 7; i > 0; --i)
488 c_size[i] = segment_size >> (8 * (7-i));
489 c_size[i] = 0x01;
490 if (mk_appendContextData(w->root, &c_size, 8) < 0 ||
491 mk_flushContextData(w->root) < 0)
492 ret = -1;
495 if (mk_closeContext(w->root, 0) < 0)
496 ret = -1;
497 mk_destroyContexts(w);
498 fclose(w->fp);
499 free(w->tracks_arr);
500 free(w);
502 return ret;