Updating the Encoding Time Calculation
[SFUResearch.git] / matroska.c
blob35ae4cdb4a5bd4aa4523c010489ce765ab8dfa52
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 Mike Matsnev
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
19 *****************************************************************************/
21 #include <stdlib.h>
22 #include <string.h>
23 #include "common/osdep.h"
24 #include "matroska.h"
26 #define CLSIZE 1048576
27 #define CHECK(x) do { if ((x) < 0) return -1; } while (0)
29 struct mk_Context {
30 struct mk_Context *next, **prev, *parent;
31 struct mk_Writer *owner;
32 unsigned id;
34 void *data;
35 unsigned d_cur, d_max;
38 typedef struct mk_Context mk_Context;
40 struct mk_Writer {
41 FILE *fp;
43 unsigned duration_ptr;
45 mk_Context *root, *cluster, *frame;
46 mk_Context *freelist;
47 mk_Context *actlist;
49 int64_t def_duration;
50 int64_t timescale;
51 int64_t cluster_tc_scaled;
52 int64_t frame_tc, prev_frame_tc_scaled, max_frame_tc;
54 char wrote_header, in_frame, keyframe;
57 static mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
58 mk_Context *c;
60 if (w->freelist) {
61 c = w->freelist;
62 w->freelist = w->freelist->next;
63 } else {
64 c = malloc(sizeof(*c));
65 memset(c, 0, sizeof(*c));
68 if (c == NULL)
69 return NULL;
71 c->parent = parent;
72 c->owner = w;
73 c->id = id;
75 if (c->owner->actlist)
76 c->owner->actlist->prev = &c->next;
77 c->next = c->owner->actlist;
78 c->prev = &c->owner->actlist;
79 c->owner->actlist = c;
81 return c;
84 static int mk_appendContextData(mk_Context *c, const void *data, unsigned size) {
85 unsigned ns = c->d_cur + size;
87 if (ns > c->d_max) {
88 void *dp;
89 unsigned dn = c->d_max ? c->d_max << 1 : 16;
90 while (ns > dn)
91 dn <<= 1;
93 dp = realloc(c->data, dn);
94 if (dp == NULL)
95 return -1;
97 c->data = dp;
98 c->d_max = dn;
101 memcpy((char*)c->data + c->d_cur, data, size);
103 c->d_cur = ns;
105 return 0;
108 static int mk_writeID(mk_Context *c, unsigned id) {
109 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
111 if (c_id[0])
112 return mk_appendContextData(c, c_id, 4);
113 if (c_id[1])
114 return mk_appendContextData(c, c_id+1, 3);
115 if (c_id[2])
116 return mk_appendContextData(c, c_id+2, 2);
117 return mk_appendContextData(c, c_id+3, 1);
120 static int mk_writeSize(mk_Context *c, unsigned size) {
121 unsigned char c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size };
123 if (size < 0x7f) {
124 c_size[4] |= 0x80;
125 return mk_appendContextData(c, c_size+4, 1);
127 if (size < 0x3fff) {
128 c_size[3] |= 0x40;
129 return mk_appendContextData(c, c_size+3, 2);
131 if (size < 0x1fffff) {
132 c_size[2] |= 0x20;
133 return mk_appendContextData(c, c_size+2, 3);
135 if (size < 0x0fffffff) {
136 c_size[1] |= 0x10;
137 return mk_appendContextData(c, c_size+1, 4);
139 return mk_appendContextData(c, c_size, 5);
142 static int mk_flushContextID(mk_Context *c) {
143 unsigned char ff = 0xff;
145 if (c->id == 0)
146 return 0;
148 CHECK(mk_writeID(c->parent, c->id));
149 CHECK(mk_appendContextData(c->parent, &ff, 1));
151 c->id = 0;
153 return 0;
156 static int mk_flushContextData(mk_Context *c) {
157 if (c->d_cur == 0)
158 return 0;
160 if (c->parent)
161 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
162 else
163 if (fwrite(c->data, c->d_cur, 1, c->owner->fp) != 1)
164 return -1;
166 c->d_cur = 0;
168 return 0;
171 static int mk_closeContext(mk_Context *c, unsigned *off) {
172 if (c->id) {
173 CHECK(mk_writeID(c->parent, c->id));
174 CHECK(mk_writeSize(c->parent, c->d_cur));
177 if (c->parent && off != NULL)
178 *off += c->parent->d_cur;
180 CHECK(mk_flushContextData(c));
182 if (c->next)
183 c->next->prev = c->prev;
184 *(c->prev) = c->next;
185 c->next = c->owner->freelist;
186 c->owner->freelist = c;
188 return 0;
191 static void mk_destroyContexts(mk_Writer *w) {
192 mk_Context *cur, *next;
194 for (cur = w->freelist; cur; cur = next) {
195 next = cur->next;
196 free(cur->data);
197 free(cur);
200 for (cur = w->actlist; cur; cur = next) {
201 next = cur->next;
202 free(cur->data);
203 free(cur);
206 w->freelist = w->actlist = w->root = NULL;
209 static int mk_writeStr(mk_Context *c, unsigned id, const char *str) {
210 size_t len = strlen(str);
212 CHECK(mk_writeID(c, id));
213 CHECK(mk_writeSize(c, len));
214 CHECK(mk_appendContextData(c, str, len));
215 return 0;
218 static int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
219 CHECK(mk_writeID(c, id));
220 CHECK(mk_writeSize(c, size));
221 CHECK(mk_appendContextData(c, data, size));
222 return 0;
225 static int mk_writeUInt(mk_Context *c, unsigned id, int64_t ui) {
226 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
227 unsigned i = 0;
229 CHECK(mk_writeID(c, id));
230 while (i < 7 && c_ui[i] == 0)
231 ++i;
232 CHECK(mk_writeSize(c, 8 - i));
233 CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
234 return 0;
237 static int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
238 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
239 unsigned i = 0;
241 CHECK(mk_writeID(c, id));
242 if (si < 0)
243 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
244 ++i;
245 else
246 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
247 ++i;
248 CHECK(mk_writeSize(c, 8 - i));
249 CHECK(mk_appendContextData(c, c_si+i, 8 - i));
250 return 0;
253 static int mk_writeFloatRaw(mk_Context *c, float f) {
254 union {
255 float f;
256 unsigned u;
257 } u;
258 unsigned char c_f[4];
260 u.f = f;
261 c_f[0] = u.u >> 24;
262 c_f[1] = u.u >> 16;
263 c_f[2] = u.u >> 8;
264 c_f[3] = u.u;
266 return mk_appendContextData(c, c_f, 4);
269 static int mk_writeFloat(mk_Context *c, unsigned id, float f) {
270 CHECK(mk_writeID(c, id));
271 CHECK(mk_writeSize(c, 4));
272 CHECK(mk_writeFloatRaw(c, f));
273 return 0;
276 static unsigned mk_ebmlSizeSize(unsigned s) {
277 if (s < 0x7f)
278 return 1;
279 if (s < 0x3fff)
280 return 2;
281 if (s < 0x1fffff)
282 return 3;
283 if (s < 0x0fffffff)
284 return 4;
285 return 5;
288 static unsigned mk_ebmlSIntSize(int64_t si) {
289 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
290 unsigned i = 0;
292 if (si < 0)
293 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
294 ++i;
295 else
296 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
297 ++i;
299 return 8 - i;
302 mk_Writer *mk_createWriter(const char *filename) {
303 mk_Writer *w = malloc(sizeof(*w));
304 if (w == NULL)
305 return NULL;
307 memset(w, 0, sizeof(*w));
309 w->root = mk_createContext(w, NULL, 0);
310 if (w->root == NULL) {
311 free(w);
312 return NULL;
315 w->fp = fopen(filename, "wb");
316 if (w->fp == NULL) {
317 mk_destroyContexts(w);
318 free(w);
319 return NULL;
322 w->timescale = 1000000;
324 return w;
327 int mk_writeHeader(mk_Writer *w, const char *writingApp,
328 const char *codecID,
329 const void *codecPrivate, unsigned codecPrivateSize,
330 int64_t default_frame_duration,
331 int64_t timescale,
332 unsigned width, unsigned height,
333 unsigned d_width, unsigned d_height)
335 mk_Context *c, *ti, *v;
337 if (w->wrote_header)
338 return -1;
340 w->timescale = timescale;
341 w->def_duration = default_frame_duration;
343 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
344 return -1;
345 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
346 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
347 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
348 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
349 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
350 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
351 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
352 CHECK(mk_closeContext(c, 0));
354 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
355 return -1;
356 CHECK(mk_flushContextID(c));
357 CHECK(mk_closeContext(c, 0));
359 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
360 return -1;
361 CHECK(mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0"));
362 CHECK(mk_writeStr(c, 0x5741, writingApp));
363 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale));
364 CHECK(mk_writeFloat(c, 0x4489, 0));
365 w->duration_ptr = c->d_cur - 4;
366 CHECK(mk_closeContext(c, &w->duration_ptr));
368 if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks
369 return -1;
370 if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry
371 return -1;
372 CHECK(mk_writeUInt(ti, 0xd7, 1)); // TrackNumber
373 CHECK(mk_writeUInt(ti, 0x73c5, 1)); // TrackUID
374 CHECK(mk_writeUInt(ti, 0x83, 1)); // TrackType
375 CHECK(mk_writeUInt(ti, 0x9c, 0)); // FlagLacing
376 CHECK(mk_writeStr(ti, 0x86, codecID)); // CodecID
377 if (codecPrivateSize)
378 CHECK(mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize)); // CodecPrivate
379 if (default_frame_duration)
380 CHECK(mk_writeUInt(ti, 0x23e383, default_frame_duration)); // DefaultDuration
382 if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video
383 return -1;
384 CHECK(mk_writeUInt(v, 0xb0, width));
385 CHECK(mk_writeUInt(v, 0xba, height));
386 CHECK(mk_writeUInt(v, 0x54b0, d_width));
387 CHECK(mk_writeUInt(v, 0x54ba, d_height));
388 CHECK(mk_closeContext(v, 0));
390 CHECK(mk_closeContext(ti, 0));
392 CHECK(mk_closeContext(c, 0));
394 CHECK(mk_flushContextData(w->root));
396 w->wrote_header = 1;
398 return 0;
401 static int mk_closeCluster(mk_Writer *w) {
402 if (w->cluster == NULL)
403 return 0;
404 CHECK(mk_closeContext(w->cluster, 0));
405 w->cluster = NULL;
406 CHECK(mk_flushContextData(w->root));
407 return 0;
410 static int mk_flushFrame(mk_Writer *w) {
411 int64_t delta, ref = 0;
412 unsigned fsize, bgsize;
413 unsigned char c_delta_flags[3];
415 if (!w->in_frame)
416 return 0;
418 delta = w->frame_tc/w->timescale - w->cluster_tc_scaled;
419 if (delta > 32767ll || delta < -32768ll)
420 CHECK(mk_closeCluster(w));
422 if (w->cluster == NULL) {
423 w->cluster_tc_scaled = w->frame_tc / w->timescale;
424 w->cluster = mk_createContext(w, w->root, 0x1f43b675); // Cluster
425 if (w->cluster == NULL)
426 return -1;
428 CHECK(mk_writeUInt(w->cluster, 0xe7, w->cluster_tc_scaled)); // Timecode
430 delta = 0;
433 fsize = w->frame ? w->frame->d_cur : 0;
434 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
435 if (!w->keyframe) {
436 ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta;
437 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
440 CHECK(mk_writeID(w->cluster, 0xa0)); // BlockGroup
441 CHECK(mk_writeSize(w->cluster, bgsize));
442 CHECK(mk_writeID(w->cluster, 0xa1)); // Block
443 CHECK(mk_writeSize(w->cluster, fsize + 4));
444 CHECK(mk_writeSize(w->cluster, 1)); // track number
446 c_delta_flags[0] = delta >> 8;
447 c_delta_flags[1] = delta;
448 c_delta_flags[2] = 0;
449 CHECK(mk_appendContextData(w->cluster, c_delta_flags, 3));
450 if (w->frame) {
451 CHECK(mk_appendContextData(w->cluster, w->frame->data, w->frame->d_cur));
452 w->frame->d_cur = 0;
454 if (!w->keyframe)
455 CHECK(mk_writeSInt(w->cluster, 0xfb, ref)); // ReferenceBlock
457 w->in_frame = 0;
458 w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta;
460 if (w->cluster->d_cur > CLSIZE)
461 CHECK(mk_closeCluster(w));
463 return 0;
466 int mk_startFrame(mk_Writer *w) {
467 if (mk_flushFrame(w) < 0)
468 return -1;
470 w->in_frame = 1;
471 w->keyframe = 0;
473 return 0;
476 int mk_setFrameFlags(mk_Writer *w,int64_t timestamp, int keyframe) {
477 if (!w->in_frame)
478 return -1;
480 w->frame_tc = timestamp;
481 w->keyframe = keyframe != 0;
483 if (w->max_frame_tc < timestamp)
484 w->max_frame_tc = timestamp;
486 return 0;
489 int mk_addFrameData(mk_Writer *w, const void *data, unsigned size) {
490 if (!w->in_frame)
491 return -1;
493 if (w->frame == NULL)
494 if ((w->frame = mk_createContext(w, NULL, 0)) == NULL)
495 return -1;
497 return mk_appendContextData(w->frame, data, size);
500 int mk_close(mk_Writer *w) {
501 int ret = 0;
502 if (mk_flushFrame(w) < 0 || mk_closeCluster(w) < 0)
503 ret = -1;
504 if (w->wrote_header) {
505 fseek(w->fp, w->duration_ptr, SEEK_SET);
506 if (mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
507 mk_flushContextData(w->root) < 0)
508 ret = -1;
510 mk_destroyContexts(w);
511 fclose(w->fp);
512 free(w);
513 return ret;