Autotools, yay!
[libmkv.git] / src / matroska.c
blob8055b165c5771d4b8cac25810128163ba1974e9a
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
5 * $Id: $
7 * Authors: Mike Matsnev
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 *****************************************************************************/
24 #define _LARGEFILE_SOURCE
25 #define _FILE_OFFSET_BITS 64
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
31 #ifdef HAVE_STDINT_H
32 #include <stdint.h>
33 #else
34 #include <inttypes.h>
35 #endif
37 #include "libmkv.h"
39 #define CLSIZE 1048576
40 #define CHECK(x) do { if ((x) < 0) return -1; } while (0)
43 static mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
44 mk_Context *c;
46 if (w->freelist) {
47 c = w->freelist;
48 w->freelist = w->freelist->next;
49 } else {
50 c = malloc(sizeof(*c));
51 memset(c, 0, sizeof(*c));
54 if (c == NULL)
55 return NULL;
57 c->parent = parent;
58 c->owner = w;
59 c->id = id;
61 if (c->owner->actlist)
62 c->owner->actlist->prev = &c->next;
63 c->next = c->owner->actlist;
64 c->prev = &c->owner->actlist;
66 return c;
69 static int mk_appendContextData(mk_Context *c, const void *data, unsigned size) {
70 unsigned ns = c->d_cur + size;
72 if (ns > c->d_max) {
73 void *dp;
74 unsigned dn = c->d_max ? c->d_max << 1 : 16;
75 while (ns > dn)
76 dn <<= 1;
78 dp = realloc(c->data, dn);
79 if (dp == NULL)
80 return -1;
82 c->data = dp;
83 c->d_max = dn;
86 memcpy((char*)c->data + c->d_cur, data, size);
88 c->d_cur = ns;
90 return 0;
93 static int mk_writeID(mk_Context *c, unsigned id) {
94 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
96 if (c_id[0])
97 return mk_appendContextData(c, c_id, 4);
98 if (c_id[1])
99 return mk_appendContextData(c, c_id+1, 3);
100 if (c_id[2])
101 return mk_appendContextData(c, c_id+2, 2);
102 return mk_appendContextData(c, c_id+3, 1);
105 static int mk_writeSize(mk_Context *c, unsigned size) {
106 unsigned char c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size };
108 if (size < 0x7f) {
109 c_size[4] |= 0x80;
110 return mk_appendContextData(c, c_size+4, 1);
112 if (size < 0x3fff) {
113 c_size[3] |= 0x40;
114 return mk_appendContextData(c, c_size+3, 2);
116 if (size < 0x1fffff) {
117 c_size[2] |= 0x20;
118 return mk_appendContextData(c, c_size+2, 3);
120 if (size < 0x0fffffff) {
121 c_size[1] |= 0x10;
122 return mk_appendContextData(c, c_size+1, 4);
124 return mk_appendContextData(c, c_size, 5);
127 static int mk_flushContextID(mk_Context *c) {
128 unsigned char ff = 0xff;
130 if (c->id == 0)
131 return 0;
133 CHECK(mk_writeID(c->parent, c->id));
134 CHECK(mk_appendContextData(c->parent, &ff, 1));
136 c->id = 0;
138 return 0;
141 static int mk_flushContextData(mk_Context *c) {
142 if (c->d_cur == 0)
143 return 0;
145 if (c->parent)
146 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
147 else
148 if (fwrite(c->data, c->d_cur, 1, c->owner->fp) != 1)
149 return -1;
151 c->d_cur = 0;
153 return 0;
156 static int mk_closeContext(mk_Context *c, unsigned *off) {
157 if (c->id) {
158 CHECK(mk_writeID(c->parent, c->id));
159 CHECK(mk_writeSize(c->parent, c->d_cur));
162 if (c->parent && off != NULL)
163 *off += c->parent->d_cur;
165 CHECK(mk_flushContextData(c));
167 if (c->next)
168 c->next->prev = c->prev;
169 *(c->prev) = c->next;
170 c->next = c->owner->freelist;
171 c->owner->freelist = c;
173 return 0;
176 static void mk_destroyContexts(mk_Writer *w) {
177 mk_Context *cur, *next;
179 for (cur = w->freelist; cur; cur = next) {
180 next = cur->next;
181 free(cur->data);
182 free(cur);
185 for (cur = w->actlist; cur; cur = next) {
186 next = cur->next;
187 free(cur->data);
188 free(cur);
191 w->freelist = w->actlist = w->root = NULL;
194 static int mk_writeStr(mk_Context *c, unsigned id, const char *str) {
195 size_t len = strlen(str);
197 CHECK(mk_writeID(c, id));
198 CHECK(mk_writeSize(c, len));
199 CHECK(mk_appendContextData(c, str, len));
200 return 0;
203 static int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
204 CHECK(mk_writeID(c, id));
205 CHECK(mk_writeSize(c, size));
206 CHECK(mk_appendContextData(c, data, size));
207 return 0;
210 static int mk_writeUInt(mk_Context *c, unsigned id, int64_t ui) {
211 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
212 unsigned i = 0;
214 CHECK(mk_writeID(c, id));
215 while (i < 7 && c_ui[i] == 0)
216 ++i;
217 CHECK(mk_writeSize(c, 8 - i));
218 CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
219 return 0;
222 static int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
223 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
224 unsigned i = 0;
226 CHECK(mk_writeID(c, id));
227 if (si < 0)
228 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
229 ++i;
230 else
231 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
232 ++i;
233 CHECK(mk_writeSize(c, 8 - i));
234 CHECK(mk_appendContextData(c, c_si+i, 8 - i));
235 return 0;
238 static int mk_writeFloatRaw(mk_Context *c, float f) {
239 union {
240 float f;
241 unsigned u;
242 } u;
243 unsigned char c_f[4];
245 u.f = f;
246 c_f[0] = u.u >> 24;
247 c_f[1] = u.u >> 16;
248 c_f[2] = u.u >> 8;
249 c_f[3] = u.u;
251 return mk_appendContextData(c, c_f, 4);
254 static int mk_writeFloat(mk_Context *c, unsigned id, float f) {
255 CHECK(mk_writeID(c, id));
256 CHECK(mk_writeSize(c, 4));
257 CHECK(mk_writeFloatRaw(c, f));
258 return 0;
261 static unsigned mk_ebmlSizeSize(unsigned s) {
262 if (s < 0x7f)
263 return 1;
264 if (s < 0x3fff)
265 return 2;
266 if (s < 0x1fffff)
267 return 3;
268 if (s < 0x0fffffff)
269 return 4;
270 return 5;
273 static unsigned mk_ebmlSIntSize(int64_t si) {
274 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
275 unsigned i = 0;
277 if (si < 0)
278 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
279 ++i;
280 else
281 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
282 ++i;
284 return 8 - i;
287 mk_Writer *mk_createWriter(const char *filename) {
288 mk_Writer *w = malloc(sizeof(*w));
289 if (w == NULL)
290 return NULL;
292 memset(w, 0, sizeof(*w));
294 w->root = mk_createContext(w, NULL, 0);
295 if (w->root == NULL) {
296 free(w);
297 return NULL;
300 w->fp = fopen(filename, "wb");
301 if (w->fp == NULL) {
302 mk_destroyContexts(w);
303 free(w);
304 return NULL;
307 w->timescale = 1000000;
309 return w;
312 int mk_writeHeader(mk_Writer *w, const char *writingApp,
313 const char *codecID,
314 const void *codecPrivate, unsigned codecPrivateSize,
315 int64_t default_frame_duration,
316 int64_t timescale,
317 unsigned width, unsigned height,
318 unsigned d_width, unsigned d_height)
320 mk_Context *c, *ti, *v;
322 if (w->wrote_header)
323 return -1;
325 w->timescale = timescale;
326 w->def_duration = default_frame_duration;
328 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
329 return -1;
330 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
331 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
332 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
333 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
334 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
335 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
336 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
337 CHECK(mk_closeContext(c, 0));
339 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
340 return -1;
341 CHECK(mk_flushContextID(c));
342 CHECK(mk_closeContext(c, 0));
344 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
345 return -1;
346 CHECK(mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0"));
347 CHECK(mk_writeStr(c, 0x5741, writingApp));
348 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale));
349 CHECK(mk_writeFloat(c, 0x4489, 0));
350 w->duration_ptr = c->d_cur - 4;
351 CHECK(mk_closeContext(c, &w->duration_ptr));
353 if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks
354 return -1;
355 if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry
356 return -1;
357 CHECK(mk_writeUInt(ti, 0xd7, 1)); // TrackNumber
358 CHECK(mk_writeUInt(ti, 0x73c5, 1)); // TrackUID
359 CHECK(mk_writeUInt(ti, 0x83, 1)); // TrackType
360 CHECK(mk_writeUInt(ti, 0x9c, 0)); // FlagLacing
361 CHECK(mk_writeStr(ti, 0x86, codecID)); // CodecID
362 if (codecPrivateSize)
363 CHECK(mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize)); // CodecPrivate
364 if (default_frame_duration)
365 CHECK(mk_writeUInt(ti, 0x23e383, default_frame_duration)); // DefaultDuration
367 if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video
368 return -1;
369 CHECK(mk_writeUInt(v, 0xb0, width));
370 CHECK(mk_writeUInt(v, 0xba, height));
371 CHECK(mk_writeUInt(v, 0x54b0, d_width));
372 CHECK(mk_writeUInt(v, 0x54ba, d_height));
373 CHECK(mk_closeContext(v, 0));
375 CHECK(mk_closeContext(ti, 0));
377 CHECK(mk_closeContext(c, 0));
379 CHECK(mk_flushContextData(w->root));
381 w->wrote_header = 1;
383 return 0;
386 static int mk_closeCluster(mk_Writer *w) {
387 if (w->cluster == NULL)
388 return 0;
389 CHECK(mk_closeContext(w->cluster, 0));
390 w->cluster = NULL;
391 CHECK(mk_flushContextData(w->root));
392 return 0;
395 int mk_flushFrame(mk_Writer *w) {
396 int64_t delta, ref = 0;
397 unsigned fsize, bgsize;
398 unsigned char c_delta_flags[3];
400 if (!w->in_frame)
401 return 0;
403 delta = w->frame_tc/w->timescale - w->cluster_tc_scaled;
404 if (delta > 32767ll || delta < -32768ll)
405 CHECK(mk_closeCluster(w));
407 if (w->cluster == NULL) {
408 w->cluster_tc_scaled = w->frame_tc / w->timescale;
409 w->cluster = mk_createContext(w, w->root, 0x1f43b675); // Cluster
410 if (w->cluster == NULL)
411 return -1;
413 CHECK(mk_writeUInt(w->cluster, 0xe7, w->cluster_tc_scaled)); // Timecode
415 delta = 0;
418 fsize = w->frame ? w->frame->d_cur : 0;
419 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
420 if (!w->keyframe) {
421 ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta;
422 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
425 CHECK(mk_writeID(w->cluster, 0xa0)); // BlockGroup
426 CHECK(mk_writeSize(w->cluster, bgsize));
427 CHECK(mk_writeID(w->cluster, 0xa1)); // Block
428 CHECK(mk_writeSize(w->cluster, fsize + 4));
429 CHECK(mk_writeSize(w->cluster, 1)); // track number
431 c_delta_flags[0] = delta >> 8;
432 c_delta_flags[1] = delta;
433 c_delta_flags[2] = 0;
434 CHECK(mk_appendContextData(w->cluster, c_delta_flags, 3));
435 if (w->frame) {
436 CHECK(mk_appendContextData(w->cluster, w->frame->data, w->frame->d_cur));
437 w->frame->d_cur = 0;
439 if (!w->keyframe)
440 CHECK(mk_writeSInt(w->cluster, 0xfb, ref)); // ReferenceBlock
442 w->in_frame = 0;
443 w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta;
445 if (w->cluster->d_cur > CLSIZE)
446 CHECK(mk_closeCluster(w));
448 return 0;
451 int mk_startFrame(mk_Writer *w) {
452 if (mk_flushFrame(w) < 0)
453 return -1;
455 w->in_frame = 1;
456 w->keyframe = 0;
458 return 0;
461 int mk_setFrameFlags(mk_Writer *w,int64_t timestamp, int keyframe) {
462 if (!w->in_frame)
463 return -1;
465 w->frame_tc = timestamp;
466 w->keyframe = keyframe != 0;
468 if (w->max_frame_tc < timestamp)
469 w->max_frame_tc = timestamp;
471 return 0;
474 int mk_addFrameData(mk_Writer *w, const void *data, unsigned size) {
475 if (!w->in_frame)
476 return -1;
478 if (w->frame == NULL)
479 if ((w->frame = mk_createContext(w, NULL, 0)) == NULL)
480 return -1;
482 return mk_appendContextData(w->frame, data, size);
485 int mk_close(mk_Writer *w) {
486 int ret = 0;
487 if (mk_flushFrame(w) < 0 || mk_closeCluster(w) < 0)
488 ret = -1;
489 if (w->wrote_header) {
490 fseek(w->fp, w->duration_ptr, SEEK_SET);
491 if (mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
492 mk_flushContextData(w->root) < 0)
493 ret = -1;
495 mk_destroyContexts(w);
496 fclose(w->fp);
497 free(w);
498 return ret;