Merge branch 'file_pos'
[libmkv.git] / src / matroska.c
blob709554ebeb17ba0690337c206ee81c2cb9b4714e
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 int mk_writeVoid(mk_Context *c, uint64_t length) {
41 char *c_void = calloc(length, sizeof(char));
43 CHECK(mk_writeID(c, 0xec));
44 CHECK(mk_writeSize(c, length));
45 CHECK(mk_appendContextData(c, c_void, length));
46 free(c_void);
47 return 0;
50 char *mk_laceXiph(uint64_t *sizes, uint8_t num_frames, uint64_t *output_size) {
51 unsigned i, j;
52 uint64_t offset = 0;
53 uint64_t alloc_size = num_frames * 6; // Complete guess. We'll realloc if we need more space, though.
54 char *laced = calloc(alloc_size, sizeof(char));
55 if (laced == NULL)
56 return NULL;
58 laced[offset++] = num_frames;
59 for (i = 0; i < num_frames; i++)
61 for (j = sizes[i]; j >= 255 ; j -= 255)
63 laced[offset++] = 255;
64 if (offset + 1 >= alloc_size) {
65 int avg_sz = offset / (i - 1); // Compute approximate average bytes/frame
66 alloc_size += avg_sz * (num_frames - i); // Add our average + number of frames left to size
67 if ((laced = realloc(laced, alloc_size)) == NULL)
68 return NULL;
71 laced[offset++] = j;
74 if (output_size != NULL)
75 *output_size = offset;
77 return laced;
80 mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat) {
81 mk_Writer *w = calloc(1, sizeof(*w));
82 if (w == NULL)
83 return NULL;
85 w->root = mk_createContext(w, NULL, 0);
86 if (w->root == NULL) {
87 free(w);
88 return NULL;
91 if ((w->cues = mk_createContext(w, w->root, 0x1c53bb6b)) == NULL) // Cues
93 mk_destroyContexts(w);
94 free(w);
95 return NULL;
98 w->fp = fopen(filename, "wb");
99 if (w->fp == NULL) {
100 mk_destroyContexts(w);
101 free(w);
102 return NULL;
105 w->timescale = timescale;
106 w->vlc_compat = vlc_compat;
108 return w;
111 int mk_writeHeader(mk_Writer *w, const char *writingApp) {
112 mk_Context *c;
114 if (w->wrote_header)
115 return -1;
117 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
118 return -1;
119 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
120 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
121 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
122 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
123 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
124 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
125 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
126 CHECK(mk_closeContext(c, 0));
128 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
129 return -1;
130 CHECK(mk_flushContextID(c));
131 w->segment_ptr = c->d_cur;
132 CHECK(mk_closeContext(c, &w->segment_ptr));
134 if (w->vlc_compat)
136 CHECK(mk_writeVoid(w->root, 0x100)); // 256 bytes should be enough room for our Seek entries.
137 CHECK(mk_writeVoid(w->root, 0x800)); // 2048 bytes for Chapters.
138 CHECK(mk_writeVoid(w->root, 0x1000)); // 4096 bytes for Cues.
139 } else
141 w->seek_data.seekhead = 0x80000000;
142 CHECK(mk_writeSeek(w, &w->seekhead_ptr));
143 w->seek_data.seekhead = 0;
146 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
147 return -1;
148 w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr;
149 CHECK(mk_writeStr(c, 0x4d80, PACKAGE_STRING)); // MuxingApp
150 CHECK(mk_writeStr(c, 0x5741, writingApp)); // WritingApp
151 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale)); // TimecodeScale
152 CHECK(mk_writeFloat(c, 0x4489, 0)); // Duration
153 w->duration_ptr = c->d_cur - 4;
154 CHECK(mk_closeContext(c, &w->duration_ptr));
156 w->seek_data.tracks = w->root->d_cur - w->segment_ptr;
158 if (w->tracks) {
159 CHECK(mk_closeContext(w->tracks, 0));
162 CHECK(mk_flushContextData(w->root));
164 w->wrote_header = 1;
165 w->def_duration = w->tracks_arr[0]->default_duration;
166 return 0;
169 static int mk_closeCluster(mk_Writer *w) {
170 if (w->cluster.context == NULL)
171 return 0;
172 w->cluster.count++;
173 CHECK(mk_closeContext(w->cluster.context, 0));
174 w->cluster.context = NULL;
175 CHECK(mk_flushContextData(w->root));
176 return 0;
179 int mk_flushFrame(mk_Writer *w, mk_Track *track) {
180 mk_Context *c;
181 int64_t delta, ref = 0;
182 unsigned fsize, bgsize;
183 uint8_t flags, c_delta_flags[2];
184 int i;
185 char *laced = NULL;
186 uint64_t length = 0;
188 if (!track->in_frame)
189 return 0;
191 delta = track->frame.timecode/w->timescale - w->cluster.tc_scaled;
192 if (delta > 32767ll || delta < -32768ll)
193 CHECK(mk_closeCluster(w));
195 if (w->cluster.context == NULL) {
196 w->cluster.tc_scaled = track->frame.timecode / w->timescale;
197 w->cluster.context = mk_createContext(w, w->root, 0x1f43b675); // Cluster
198 if (w->cluster.context == NULL)
199 return -1;
201 w->cluster.pointer = w->f_pos - w->segment_ptr;
203 CHECK(mk_writeUInt(w->cluster.context, 0xe7, w->cluster.tc_scaled)); // Cluster Timecode
205 delta = 0;
206 w->cluster.block_count = 0;
209 fsize = track->frame.data ? track->frame.data->d_cur : 0;
210 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
211 if (!track->frame.keyframe) {
212 ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta;
213 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
216 CHECK(mk_writeID(w->cluster.context, 0xa0)); // BlockGroup
217 CHECK(mk_writeSize(w->cluster.context, bgsize));
218 CHECK(mk_writeID(w->cluster.context, 0xa1)); // Block
220 switch (track->frame.lacing) {
221 case MK_LACING_XIPH:
222 laced = mk_laceXiph(track->frame.lacing_sizes, track->frame.lacing_num_frames, &length);
223 break;
224 case MK_LACING_EBML:
225 length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1;
226 for (i = 1; i < track->frame.lacing_num_frames; i++)
227 length += mk_ebmlSizeSize(track->frame.lacing_sizes[i] << 1);
228 break;
229 case MK_LACING_FIXED:
231 laced = calloc(1, sizeof(char));
232 laced[0] = track->frame.lacing_num_frames;
233 ++length;
235 break;
238 CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length));
239 CHECK(mk_writeSize(w->cluster.context, track->track_id)); // track number
241 w->cluster.block_count++;
243 c_delta_flags[0] = delta >> 8;
244 c_delta_flags[1] = delta;
245 CHECK(mk_appendContextData(w->cluster.context, c_delta_flags, 2));
247 flags = ( track->frame.keyframe << 8 ) | track->frame.lacing;
248 CHECK(mk_appendContextData(w->cluster.context, &flags, 1));
249 if (track->frame.lacing) {
250 if (track->frame.lacing == MK_LACING_EBML) {
251 CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1));
252 CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0]));
253 for (i = 1; i < track->frame.lacing_num_frames; i++)
255 CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1]));
257 } else if (length > 0 && laced != NULL) {
258 CHECK(mk_appendContextData(w->cluster.context, laced, length));
259 free(laced);
260 laced = NULL;
264 if (track->frame.data) {
265 CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, track->frame.data->d_cur));
266 track->frame.data->d_cur = 0;
268 if (!track->frame.keyframe)
269 CHECK(mk_writeSInt(w->cluster.context, 0xfb, ref)); // ReferenceBlock
271 if ((track->cue_flag || (track->track_type & MK_TRACK_VIDEO)) && track->frame.keyframe) {
272 for(i = 0; i < w->num_tracks; i++)
273 w->tracks_arr[i]->cue_flag = 1;
274 if (w->cue_point.timecode != track->frame.timecode) {
275 if (w->cue_point.context != NULL) {
276 CHECK(mk_closeContext(w->cue_point.context, 0));
277 w->cue_point.context = NULL;
280 if (w->cue_point.context == NULL) {
281 if ((w->cue_point.context = mk_createContext(w, w->cues, 0xbb)) == NULL) // CuePoint
282 return -1;
283 CHECK(mk_writeUInt(w->cue_point.context, 0xb3, track->frame.timecode)); // CueTime
284 w->cue_point.timecode = track->frame.timecode;
287 if ((c = mk_createContext(w, w->cue_point.context, 0xb7)) == NULL) // CueTrackPositions
288 return -1;
289 CHECK(mk_writeUInt(c, 0xf7, track->track_id)); // CueTrack
290 CHECK(mk_writeUInt(c, 0xf1, w->cluster.pointer)); // CueClusterPosition
291 // CHECK(mk_writeUInt(c, 0x5378, w->cluster.block_count)); // CueBlockNumber
292 CHECK(mk_closeContext(c, 0));
293 track->cue_flag = 0;
296 track->in_frame = 0;
297 track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta;
299 if (w->cluster.context->d_cur > CLSIZE)
300 CHECK(mk_closeCluster(w));
302 return 0;
305 int mk_startFrame(mk_Writer *w, mk_Track *track) {
306 if (mk_flushFrame(w, track) < 0)
307 return -1;
309 track->in_frame = 1;
310 track->frame.keyframe = 0;
311 track->frame.lacing = MK_LACING_NONE;
312 track->frame.lacing_num_frames = 0;
313 track->frame.lacing_sizes = NULL;
315 return 0;
318 int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe) {
319 if (!track->in_frame)
320 return -1;
322 track->frame.timecode = timestamp;
323 track->frame.keyframe = keyframe != 0;
325 if (track->max_frame_tc < timestamp)
326 track->max_frame_tc = timestamp;
328 return 0;
331 int mk_setFrameLacing(mk_Writer *w, mk_Track *track, uint8_t lacing, uint8_t num_frames, uint32_t sizes[]) {
332 if (!track->in_frame)
333 return -1;
334 track->frame.lacing_sizes = calloc(num_frames, sizeof(uint32_t));
336 track->frame.lacing = lacing;
337 track->frame.lacing_num_frames = num_frames;
338 memcpy(track->frame.lacing_sizes, sizes, num_frames);
340 return 0;
343 int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size) {
344 if (!track->in_frame)
345 return -1;
347 if (track->frame.data == NULL)
348 if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL)
349 return -1;
351 return mk_appendContextData(track->frame.data, data, size);
354 /* The offset of the SeekHead is returned in *pointer. */
355 int mk_writeSeek(mk_Writer *w, int64_t *pointer) {
356 mk_Context *c, *s;
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 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
365 return -1;
366 CHECK(mk_writeUInt(s, 0x53ab, 0x114d9b74)); // SeekID
367 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.seekhead)); // SeekPosition
368 CHECK(mk_closeContext(s, 0));
370 if (w->seek_data.segmentinfo) {
371 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
372 return -1;
373 CHECK(mk_writeUInt(s, 0x53ab, 0x1549a966)); // SeekID
374 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.segmentinfo)); // SeekPosition
375 CHECK(mk_closeContext(s, 0));
377 if (w->seek_data.tracks) {
378 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
379 return -1;
380 CHECK(mk_writeUInt(s, 0x53ab, 0x1654ae6b)); // SeekID
381 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.tracks)); // SeekPosition
382 CHECK(mk_closeContext(s, 0));
384 if (w->seek_data.cues) {
385 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
386 return -1;
387 CHECK(mk_writeUInt(s, 0x53ab, 0x1c53bb6b)); // SeekID
388 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.cues)); // SeekPosition
389 CHECK(mk_closeContext(s, 0));
391 if (w->seek_data.attachments) {
392 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
393 return -1;
394 CHECK(mk_writeUInt(s, 0x53ab, 0x1941a469)); // SeekID
395 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.attachments)); // SeekPosition
396 CHECK(mk_closeContext(s, 0));
398 if (w->seek_data.chapters) {
399 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
400 return -1;
401 CHECK(mk_writeUInt(s, 0x53ab, 0x1043a770)); // SeekID
402 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.chapters)); // SeekPosition
403 CHECK(mk_closeContext(s, 0));
405 if (w->seek_data.tags) {
406 if ((s = mk_createContext(w, c, 0x4dbb)) == NULL) // Seek
407 return -1;
408 CHECK(mk_writeUInt(s, 0x53ab, 0x1254c367)); // SeekID
409 CHECK(mk_writeUInt(s, 0x53ac, w->seek_data.tags)); // SeekPosition
410 CHECK(mk_closeContext(s, 0));
412 CHECK(mk_closeContext(c, 0));
414 if (pointer != NULL)
415 *pointer = seekhead_ptr;
417 return 0;
420 int mk_close(mk_Writer *w) {
421 int i, ret = 0;
422 mk_Track *tk;
423 int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc;
424 uint64_t segment_size = 0;
425 unsigned char c_size[8];
427 for (i = w->num_tracks - 1; i >= 0; i--)
429 tk = w->tracks_arr[i];
430 w->tracks_arr[i] = NULL;
431 if (mk_flushFrame(w, tk) < 0)
432 ret = -1;
433 free(tk);
434 tk = NULL;
437 if (mk_closeCluster(w) < 0)
438 ret = -1;
440 if (w->chapters != NULL)
442 if (w->vlc_compat) {
443 if (mk_seekFile(w, w->segment_ptr + 0x103) < 0)
444 ret = -1;
446 w->seek_data.chapters = w->f_pos - w->segment_ptr;
447 mk_writeChapters(w);
448 if (w->vlc_compat) {
449 if (mk_flushContextData(w->root) < 0)
450 ret = -1;
451 if (mk_writeVoid(w->root, (0x800 - (w->f_pos - w->segment_ptr))) < 0)
452 ret = -1;
454 if (mk_flushContextData(w->root) < 0)
455 ret = -1;
458 w->seek_data.cues = w->f_pos - w->segment_ptr;
459 if (w->cue_point.context != NULL)
460 if (mk_closeContext(w->cue_point.context, 0) < 0)
461 ret = -1;
462 // if (w->vlc_compat) {
463 // if (mk_seekFile(w, w->segment_ptr + 259 + 2051) < 0)
464 // ret = -1;
465 // }
466 if (mk_closeContext(w->cues, 0) < 0)
467 ret = -1;
468 if (w->vlc_compat) {
469 if (mk_flushContextData(w->root) < 0)
470 ret = -1;
471 if (mk_writeVoid(w->root, (0x1000 - (w->f_pos - w->segment_ptr))) < 0)
472 ret = -1;
474 if (mk_flushContextData(w->root) < 0)
475 ret = -1;
477 if (w->wrote_header) {
478 if (w->vlc_compat) {
479 if (mk_seekFile(w, w->segment_ptr) < 0)
480 ret = -1;
483 if (mk_writeSeek(w, &w->seek_data.seekhead) < 0)
484 ret = -1;
485 w->seek_data.seekhead -= w->segment_ptr;
487 if (w->vlc_compat)
489 if (mk_flushContextData(w->root) < 0)
490 ret = -1;
491 if (mk_writeVoid(w->root, (256 - (w->f_pos - w->segment_ptr))) < 0)
492 ret = -1;
495 if (mk_flushContextData(w->root) < 0)
496 ret = -1;
498 if (!w->vlc_compat)
500 int i = w->seek_data.segmentinfo;
501 w->seek_data.segmentinfo = 0;
502 w->seek_data.tracks = 0;
503 w->seek_data.cues = 0;
504 w->seek_data.chapters = 0;
505 w->seek_data.attachments = 0;
506 w->seek_data.tags = 0;
507 if (mk_seekFile(w, w->segment_ptr) < 0)
508 ret = -1;
509 if (mk_writeSeek(w, NULL) < 0 ||
510 mk_flushContextData(w->root) < 0)
511 ret = -1;
512 if (((i + w->segment_ptr) - w->f_pos - 2) > 1)
513 if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0 ||
514 mk_flushContextData(w->root) < 0)
515 ret = -1;
518 if (mk_seekFile(w, w->duration_ptr) < 0)
519 ret = -1;
520 if (mk_writeFloatRaw(w->root, (float)((double)(max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
521 mk_flushContextData(w->root) < 0)
522 ret = -1;
523 if (mk_seekFile(w, w->segment_ptr - 8) < 0)
524 ret = -1;
525 segment_size = w->f_eof - w->segment_ptr;
526 for (i = 7; i > 0; --i)
527 c_size[i] = segment_size >> (8 * (7-i));
528 c_size[i] = 0x01;
529 if (mk_appendContextData(w->root, &c_size, 8) < 0 ||
530 mk_flushContextData(w->root) < 0)
531 ret = -1;
534 if (mk_closeContext(w->root, 0) < 0)
535 ret = -1;
536 mk_destroyContexts(w);
537 fclose(w->fp);
538 free(w->tracks_arr);
539 free(w);
541 return ret;