Handle AspectRatioType video element.
[libmkv.git] / src / matroska.c
blob221e176dd6dd2369764252e4f238ec42cc84aaca
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 "config.h"
25 #include "libmkv.h"
26 #include "matroska.h"
27 #include "md5.h"
29 #define RESERVED_SEEKHEAD 0x100
30 /* 256 bytes should be enough room for our Seek entries. */
31 #define RESERVED_CHAPTERS 0x800
32 /* 2048 bytes, hopefully enough for Chapters. */
34 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, uint64_t *output_size) {
47 unsigned i, j;
48 uint64_t offset = 0;
49 uint64_t alloc_size = num_frames * 6; // Complete guess. We'll realloc if we need more space, though.
50 char *laced = calloc(alloc_size, sizeof(char));
51 if (laced == NULL)
52 return NULL;
54 laced[offset++] = num_frames;
55 for (i = 0; i < num_frames; i++)
57 for (j = sizes[i]; j >= 255 ; j -= 255)
59 laced[offset++] = 255;
60 if (offset + 1 >= alloc_size) {
61 int avg_sz = offset / (i - 1); // Compute approximate average bytes/frame
62 alloc_size += avg_sz * (num_frames - i); // Add our average + number of frames left to size
63 if ((laced = realloc(laced, alloc_size)) == NULL)
64 return NULL;
67 laced[offset++] = j;
70 if (output_size != NULL)
71 *output_size = offset;
73 return laced;
76 mk_Writer *mk_createWriter(const char *filename, int64_t timescale, uint8_t vlc_compat) {
77 mk_Writer *w = calloc(1, sizeof(*w));
78 if (w == NULL)
79 return NULL;
81 w->root = mk_createContext(w, NULL, 0);
82 if (w->root == NULL) {
83 free(w);
84 return NULL;
87 if ((w->cues = mk_createContext(w, w->root, MATROSKA_ID_CUES)) == NULL) // Cues
89 mk_destroyContexts(w);
90 free(w);
91 return NULL;
94 if (vlc_compat) {
95 if ((w->cluster.seekhead = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) // SeekHead
97 mk_destroyContexts(w);
98 free(w);
99 return NULL;
103 w->fp = fopen(filename, "wb");
104 if (w->fp == NULL) {
105 mk_destroyContexts(w);
106 free(w);
107 return NULL;
110 w->timescale = timescale;
111 w->vlc_compat = vlc_compat;
113 return w;
116 int mk_writeHeader(mk_Writer *w, const char *writingApp) {
117 mk_Context *c;
118 int64_t offset = 0;
120 if (w->wrote_header)
121 return -1;
123 md5_starts(&w->segment_md5); /* Initalize MD5 */
125 CHECK(mk_writeEbmlHeader(w, "matroska", MATROSKA_VERSION, MATROSKA_VERSION));
127 if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEGMENT)) == 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, RESERVED_SEEKHEAD)); /* Reserved space for SeekHead */
135 CHECK(mk_writeVoid(w->root, RESERVED_CHAPTERS)); /* Reserved space 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, MATROSKA_ID_INFO)) == NULL) // SegmentInfo
144 return -1;
145 w->seek_data.segmentinfo = w->root->d_cur - w->segment_ptr;
146 CHECK(mk_writeVoid(c, 16)); /* Reserve space for a SegmentUID, write it later. */
147 CHECK(mk_writeStr(c, MATROSKA_ID_MUXINGAPP, PACKAGE_STRING)); // MuxingApp
148 CHECK(mk_writeStr(c, MATROSKA_ID_WRITINGAPP, writingApp)); // WritingApp
149 CHECK(mk_writeUInt(c, MATROSKA_ID_TIMECODESCALE, w->timescale)); // TimecodeScale
150 CHECK(mk_writeFloat(c, MATROSKA_ID_DURATION, 0)); // Duration
151 w->duration_ptr = c->d_cur - 4;
152 CHECK(mk_closeContext(c, &offset));
153 w->duration_ptr += offset;
154 w->segmentuid_ptr = offset;
156 w->seek_data.tracks = w->root->d_cur - w->segment_ptr;
158 if (w->tracks)
159 CHECK(mk_closeContext(w->tracks, 0));
161 CHECK(mk_flushContextData(w->root));
163 w->wrote_header = 1;
164 w->def_duration = w->tracks_arr[0]->default_duration;
165 return 0;
168 static int mk_closeCluster(mk_Writer *w) {
169 if (w->cluster.context == NULL)
170 return 0;
171 w->cluster.count++;
172 CHECK(mk_closeContext(w->cluster.context, 0));
173 w->cluster.context = NULL;
174 CHECK(mk_flushContextData(w->root));
175 return 0;
178 int mk_flushFrame(mk_Writer *w, mk_Track *track) {
179 mk_Context *c, *tp;
180 int64_t delta, ref = 0;
181 unsigned fsize, bgsize;
182 uint8_t flags, c_delta[2];
183 int i;
184 char *laced = NULL;
185 uint64_t length = 0;
187 if (!track->in_frame)
188 return 0;
190 delta = track->frame.timecode/w->timescale - w->cluster.tc_scaled;
191 if (delta > 2000ll || delta < -2000ll)
192 CHECK(mk_closeCluster(w));
194 if (w->cluster.context == NULL) {
195 w->cluster.tc_scaled = track->frame.timecode / w->timescale;
196 w->cluster.context = mk_createContext(w, w->root, MATROSKA_ID_CLUSTER); // Cluster
197 if (w->cluster.context == NULL)
198 return -1;
200 w->cluster.pointer = w->f_pos - w->segment_ptr;
202 if (w->vlc_compat)
203 CHECK(mk_writeSeek(w, w->cluster.seekhead, MATROSKA_ID_CLUSTER, w->cluster.pointer));
205 CHECK(mk_writeUInt(w->cluster.context, MATROSKA_ID_CLUSTERTIMECODE, w->cluster.tc_scaled)); // Cluster Timecode
207 delta = 0;
208 w->cluster.block_count = 0;
211 /* Calculate the encoded lacing sizes. */
212 switch (track->frame.lacing) {
213 case MK_LACING_XIPH:
214 laced = mk_laceXiph(track->frame.lacing_sizes, track->frame.lacing_num_frames, &length);
215 break;
216 case MK_LACING_EBML:
218 uint64_t u_size = 0;
219 length += mk_ebmlSizeSize(track->frame.lacing_sizes[0]) + 1; // Add one for the frame count.
220 for (i = 1; i < track->frame.lacing_num_frames; i++)
222 u_size = llabs(track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1]);
223 length += mk_ebmlSizeSize((u_size) << 1); // Shift by one so we get the right size for a signed number.
225 break;
227 case MK_LACING_FIXED:
229 laced = calloc(1, sizeof(char));
230 laced[0] = track->frame.lacing_num_frames;
231 ++length;
232 break;
234 default:
235 break;
238 fsize = track->frame.data ? track->frame.data->d_cur : 0;
239 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4 + length) + 1 + length;
240 if (!track->frame.keyframe) {
241 ref = track->prev_frame_tc_scaled - w->cluster.tc_scaled - delta;
242 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
245 CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCKGROUP)); // BlockGroup
246 CHECK(mk_writeSize(w->cluster.context, bgsize));
247 CHECK(mk_writeID(w->cluster.context, MATROSKA_ID_BLOCK)); // Block
248 CHECK(mk_writeSize(w->cluster.context, fsize + 4 + length)); // Block size
249 CHECK(mk_writeSize(w->cluster.context, track->track_id)); // track number
251 w->cluster.block_count++;
253 c_delta[0] = delta >> 8;
254 c_delta[1] = delta;
255 CHECK(mk_appendContextData(w->cluster.context, c_delta, 2)); // Timecode relative to Cluster.
257 // flags = ( track->frame.keyframe << 8 ) | track->frame.lacing;
258 flags = track->frame.lacing << 1; // Flags: Bit 5-6 describe what type of lacing to use.
259 CHECK(mk_appendContextData(w->cluster.context, &flags, 1));
260 if (track->frame.lacing) {
261 if (track->frame.lacing == MK_LACING_EBML) {
262 CHECK(mk_appendContextData(w->cluster.context, &track->frame.lacing_num_frames, 1)); // Number of frames in lace-1
263 CHECK(mk_writeSize(w->cluster.context, track->frame.lacing_sizes[0])); // Size of 1st frame.
264 for (i = 1; i < track->frame.lacing_num_frames; i++)
266 CHECK(mk_writeSSize(w->cluster.context, track->frame.lacing_sizes[i] - track->frame.lacing_sizes[i-1])); // Size difference between previous size and this size.
268 } else if (length > 0 && laced != NULL) {
269 CHECK(mk_appendContextData(w->cluster.context, laced, length));
270 free(laced);
271 laced = NULL;
275 if (track->frame.data) {
276 CHECK(mk_appendContextData(w->cluster.context, track->frame.data->data, track->frame.data->d_cur));
277 track->frame.data->d_cur = 0;
279 if (!track->frame.keyframe)
280 CHECK(mk_writeSInt(w->cluster.context, MATROSKA_ID_REFERENCEBLOCK, ref)); // ReferenceBlock
282 /* This may get a little out of hand, but it seems sane enough for now. */
283 if (track->frame.keyframe && (track->track_type == MK_TRACK_VIDEO)) {
284 // if (track->frame.keyframe && (track->track_type & MK_TRACK_VIDEO) && ((track->prev_cue_pos + 3*CLSIZE) <= w->f_pos || track->frame.timecode == 0)) {
285 if ((c = mk_createContext(w, w->cues, MATROSKA_ID_CUEPOINT)) == NULL) // CuePoint
286 return -1;
287 CHECK(mk_writeUInt(c, MATROSKA_ID_CUETIME, (track->frame.timecode / w->timescale))); // CueTime
289 if ((tp = mk_createContext(w, c, MATROSKA_ID_CUETRACKPOSITIONS)) == NULL) // CueTrackPositions
290 return -1;
291 CHECK(mk_writeUInt(tp, MATROSKA_ID_CUETRACK, track->track_id)); // CueTrack
292 CHECK(mk_writeUInt(tp, MATROSKA_ID_CUECLUSTERPOSITION, w->cluster.pointer)); // CueClusterPosition
293 // CHECK(mk_writeUInt(c, MATROSKA_ID_CUEBLOCKNUMBER, w->cluster.block_count)); // CueBlockNumber
294 CHECK(mk_closeContext(tp, 0));
295 CHECK(mk_closeContext(c, 0));
296 track->prev_cue_pos = w->f_pos;
299 track->in_frame = 0;
300 track->prev_frame_tc_scaled = w->cluster.tc_scaled + delta;
302 if (w->cluster.context->d_cur > CLSIZE)
303 CHECK(mk_closeCluster(w));
305 return 0;
308 int mk_startFrame(mk_Writer *w, mk_Track *track) {
309 if (mk_flushFrame(w, track) < 0)
310 return -1;
312 track->in_frame = 1;
313 track->frame.keyframe = 0;
314 track->frame.lacing = MK_LACING_NONE;
315 track->frame.lacing_num_frames = 0;
316 track->frame.lacing_sizes = NULL;
318 return 0;
321 int mk_setFrameFlags(mk_Writer *w, mk_Track *track, int64_t timestamp, unsigned keyframe) {
322 if (!track->in_frame)
323 return -1;
325 track->frame.timecode = timestamp;
326 track->frame.keyframe = keyframe != 0;
328 if (track->max_frame_tc < timestamp)
329 track->max_frame_tc = timestamp;
331 return 0;
334 int mk_setFrameLacing(mk_Writer *w, mk_Track *track, mk_LacingType lacing, uint8_t num_frames, uint64_t sizes[]) {
335 if (!track->in_frame)
336 return -1;
337 track->frame.lacing_sizes = calloc(num_frames, sizeof(uint64_t));
339 track->frame.lacing = lacing;
340 track->frame.lacing_num_frames = num_frames;
341 memcpy(track->frame.lacing_sizes, sizes, num_frames * sizeof(uint64_t));
343 return 0;
346 int mk_addFrameData(mk_Writer *w, mk_Track *track, const void *data, unsigned size) {
347 if (!track->in_frame)
348 return -1;
350 if (track->frame.data == NULL)
351 if ((track->frame.data = mk_createContext(w, NULL, 0)) == NULL)
352 return -1;
354 md5_update(&w->segment_md5, (unsigned char *)data, size);
356 return mk_appendContextData(track->frame.data, data, size);
359 int mk_writeSeek(mk_Writer *w, mk_Context *c, unsigned seek_id, uint64_t seek_pos) {
360 mk_Context *s;
362 if ((s = mk_createContext(w, c, MATROSKA_ID_SEEKENTRY)) == NULL) // Seek
363 return -1;
364 CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKID, seek_id)); // SeekID
365 CHECK(mk_writeUInt(s, MATROSKA_ID_SEEKPOSITION, seek_pos)); // SeekPosition
366 CHECK(mk_closeContext(s, 0));
368 return 0;
371 /* The offset of the SeekHead is returned in *pointer. */
372 int mk_writeSeekHead(mk_Writer *w, int64_t *pointer) {
373 mk_Context *c;
374 int64_t seekhead_ptr;
376 if ((c = mk_createContext(w, w->root, MATROSKA_ID_SEEKHEAD)) == NULL) // SeekHead
377 return -1;
378 if (pointer != NULL)
379 seekhead_ptr = w->f_pos;
380 if (w->seek_data.seekhead)
381 CHECK(mk_writeSeek(w, c, MATROSKA_ID_SEEKHEAD, w->seek_data.seekhead));
382 if (w->seek_data.segmentinfo)
383 CHECK(mk_writeSeek(w, c, MATROSKA_ID_INFO, w->seek_data.segmentinfo));
384 if (w->seek_data.tracks)
385 CHECK(mk_writeSeek(w, c, MATROSKA_ID_TRACKS, w->seek_data.tracks));
386 if (w->seek_data.cues)
387 CHECK(mk_writeSeek(w, c, MATROSKA_ID_CUES, w->seek_data.cues));
388 if (w->seek_data.attachments)
389 CHECK(mk_writeSeek(w, c, MATROSKA_ID_ATTACHMENTS, w->seek_data.attachments));
390 if (w->seek_data.chapters)
391 CHECK(mk_writeSeek(w, c, MATROSKA_ID_CHAPTERS, w->seek_data.chapters));
392 if (w->seek_data.tags)
393 CHECK(mk_writeSeek(w, c, MATROSKA_ID_TAGS, w->seek_data.tags));
394 CHECK(mk_closeContext(c, 0));
396 if (pointer != NULL)
397 *pointer = seekhead_ptr;
399 return 0;
402 int mk_close(mk_Writer *w) {
403 int i, ret = 0;
404 mk_Track *tk;
405 int64_t max_frame_tc = w->tracks_arr[0]->max_frame_tc;
406 uint64_t segment_size = 0;
407 unsigned char c_size[8];
408 unsigned char segment_uid[16];
410 md5_finish(&w->segment_md5, segment_uid);
412 for (i = w->num_tracks - 1; i >= 0; i--)
414 tk = w->tracks_arr[i];
415 w->tracks_arr[i] = NULL;
416 --w->num_tracks;
417 if (mk_flushFrame(w, tk) < 0)
418 ret = -1;
419 free(tk);
420 tk = NULL;
423 if (mk_closeCluster(w) < 0)
424 ret = -1;
426 w->seek_data.cues = w->f_pos - w->segment_ptr;
427 if (mk_closeContext(w->cues, 0) < 0)
428 ret = -1;
429 if (mk_flushContextData(w->root) < 0)
430 ret = -1;
432 if (w->vlc_compat && w->cluster.seekhead) {
433 w->seek_data.seekhead = w->f_pos - w->segment_ptr;
434 if (mk_closeContext(w->cluster.seekhead, 0) < 0)
435 ret = -1;
436 if (mk_flushContextData(w->root) < 0)
437 ret = -1;
440 if (w->chapters != NULL)
442 if (w->vlc_compat) {
443 if (mk_flushContextData(w->root) < 0)
444 ret = -1;
445 if (mk_seekFile(w, w->segment_ptr + 0x100 + 3) < 0)
446 ret = -1;
448 w->seek_data.chapters = w->f_pos - w->segment_ptr;
449 mk_writeChapters(w);
450 if (mk_flushContextData(w->root) < 0)
451 ret = -1;
452 if (w->vlc_compat) {
453 if (mk_writeVoid(w->root, (0x800 - (w->f_pos - w->segment_ptr - 0x100 - 3))) < 0)
454 ret = -1;
455 if (mk_flushContextData(w->root) < 0)
456 ret = -1;
460 if (w->wrote_header) {
461 if (w->vlc_compat) {
462 if (mk_seekFile(w, w->segment_ptr) < 0)
463 ret = -1;
466 if (mk_writeSeekHead(w, &w->seek_data.seekhead) < 0)
467 ret = -1;
468 w->seek_data.seekhead -= w->segment_ptr;
470 if (w->vlc_compat)
472 if (mk_flushContextData(w->root) < 0)
473 ret = -1;
474 if (mk_writeVoid(w->root, (0x100 - (w->f_pos - w->segment_ptr))) < 0)
475 ret = -1;
478 if (mk_flushContextData(w->root) < 0)
479 ret = -1;
481 if (!w->vlc_compat)
483 int i = w->seek_data.segmentinfo;
484 w->seek_data.segmentinfo = 0;
485 w->seek_data.tracks = 0;
486 w->seek_data.cues = 0;
487 w->seek_data.chapters = 0;
488 w->seek_data.attachments = 0;
489 w->seek_data.tags = 0;
490 if (mk_seekFile(w, w->segment_ptr) < 0)
491 ret = -1;
492 if (mk_writeSeekHead(w, NULL) < 0 ||
493 mk_flushContextData(w->root) < 0)
494 ret = -1;
495 if (((i + w->segment_ptr) - w->f_pos - 2) > 1)
496 if (mk_writeVoid(w->root, (i + w->segment_ptr) - w->f_pos - 2) < 0 ||
497 mk_flushContextData(w->root) < 0)
498 ret = -1;
501 if (mk_seekFile(w, w->duration_ptr) < 0)
502 ret = -1;
503 if (mk_writeFloatRaw(w->root, (float)((double)(max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
504 mk_flushContextData(w->root) < 0)
505 ret = -1;
506 if (mk_seekFile(w, w->segment_ptr - 8) < 0)
507 ret = -1;
508 segment_size = w->f_eof - w->segment_ptr;
509 for (i = 7; i > 0; --i)
510 c_size[i] = segment_size >> (8 * (7-i));
511 c_size[i] = 0x01;
512 if (mk_appendContextData(w->root, &c_size, 8) < 0 ||
513 mk_flushContextData(w->root) < 0)
514 ret = -1;
515 if (mk_seekFile(w, w->segmentuid_ptr) < 0)
516 ret = -1;
517 if (mk_writeBin(w->root, MATROSKA_ID_SEGMENTUID, segment_uid, sizeof(segment_uid)) < 0 ||
518 mk_flushContextData(w->root) < 0)
519 ret = -1;
522 if (mk_closeContext(w->root, 0) < 0)
523 ret = -1;
524 mk_destroyContexts(w);
525 fclose(w->fp);
526 free(w->tracks_arr);
527 free(w);
529 return ret;