1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
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
39 #define CLSIZE 1048576
40 #define CHECK(x) do { if ((x) < 0) return -1; } while (0)
43 struct mk_Context *next, **prev, *parent;
44 struct mk_Writer *owner;
48 unsigned d_cur, d_max;
51 typedef struct mk_Context mk_Context;
56 unsigned duration_ptr;
58 mk_Context *root, *cluster, *frame;
64 int64_t cluster_tc_scaled;
65 int64_t frame_tc, prev_frame_tc_scaled, max_frame_tc;
67 char wrote_header, in_frame, keyframe;
70 static mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
75 w->freelist = w->freelist->next;
77 c = malloc(sizeof(*c));
78 memset(c, 0, sizeof(*c));
88 if (c->owner->actlist)
89 c->owner->actlist->prev = &c->next;
90 c->next = c->owner->actlist;
91 c->prev = &c->owner->actlist;
96 static int mk_appendContextData(mk_Context *c, const void *data, unsigned size) {
97 unsigned ns = c->d_cur + size;
101 unsigned dn = c->d_max ? c->d_max << 1 : 16;
105 dp = realloc(c->data, dn);
113 memcpy((char*)c->data + c->d_cur, data, size);
120 static int mk_writeID(mk_Context *c, unsigned id) {
121 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
124 return mk_appendContextData(c, c_id, 4);
126 return mk_appendContextData(c, c_id+1, 3);
128 return mk_appendContextData(c, c_id+2, 2);
129 return mk_appendContextData(c, c_id+3, 1);
132 static int mk_writeSize(mk_Context *c, unsigned size) {
133 unsigned char c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size };
137 return mk_appendContextData(c, c_size+4, 1);
141 return mk_appendContextData(c, c_size+3, 2);
143 if (size < 0x1fffff) {
145 return mk_appendContextData(c, c_size+2, 3);
147 if (size < 0x0fffffff) {
149 return mk_appendContextData(c, c_size+1, 4);
151 return mk_appendContextData(c, c_size, 5);
154 static int mk_flushContextID(mk_Context *c) {
155 unsigned char ff = 0xff;
160 CHECK(mk_writeID(c->parent, c->id));
161 CHECK(mk_appendContextData(c->parent, &ff, 1));
168 static int mk_flushContextData(mk_Context *c) {
173 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
175 if (fwrite(c->data, c->d_cur, 1, c->owner->fp) != 1)
183 static int mk_closeContext(mk_Context *c, unsigned *off) {
185 CHECK(mk_writeID(c->parent, c->id));
186 CHECK(mk_writeSize(c->parent, c->d_cur));
189 if (c->parent && off != NULL)
190 *off += c->parent->d_cur;
192 CHECK(mk_flushContextData(c));
195 c->next->prev = c->prev;
196 *(c->prev) = c->next;
197 c->next = c->owner->freelist;
198 c->owner->freelist = c;
203 static void mk_destroyContexts(mk_Writer *w) {
204 mk_Context *cur, *next;
206 for (cur = w->freelist; cur; cur = next) {
212 for (cur = w->actlist; cur; cur = next) {
218 w->freelist = w->actlist = w->root = NULL;
221 static int mk_writeStr(mk_Context *c, unsigned id, const char *str) {
222 size_t len = strlen(str);
224 CHECK(mk_writeID(c, id));
225 CHECK(mk_writeSize(c, len));
226 CHECK(mk_appendContextData(c, str, len));
230 static int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
231 CHECK(mk_writeID(c, id));
232 CHECK(mk_writeSize(c, size));
233 CHECK(mk_appendContextData(c, data, size));
237 static int mk_writeUInt(mk_Context *c, unsigned id, int64_t ui) {
238 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
241 CHECK(mk_writeID(c, id));
242 while (i < 7 && c_ui[i] == 0)
244 CHECK(mk_writeSize(c, 8 - i));
245 CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
249 static int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
250 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
253 CHECK(mk_writeID(c, id));
255 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
258 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
260 CHECK(mk_writeSize(c, 8 - i));
261 CHECK(mk_appendContextData(c, c_si+i, 8 - i));
265 static int mk_writeFloatRaw(mk_Context *c, float f) {
270 unsigned char c_f[4];
278 return mk_appendContextData(c, c_f, 4);
281 static int mk_writeFloat(mk_Context *c, unsigned id, float f) {
282 CHECK(mk_writeID(c, id));
283 CHECK(mk_writeSize(c, 4));
284 CHECK(mk_writeFloatRaw(c, f));
288 static unsigned mk_ebmlSizeSize(unsigned s) {
300 static unsigned mk_ebmlSIntSize(int64_t si) {
301 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
305 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
308 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
314 mk_Writer *mk_createWriter(const char *filename) {
315 mk_Writer *w = malloc(sizeof(*w));
319 memset(w, 0, sizeof(*w));
321 w->root = mk_createContext(w, NULL, 0);
322 if (w->root == NULL) {
327 w->fp = fopen(filename, "wb");
329 mk_destroyContexts(w);
334 w->timescale = 1000000;
339 int mk_writeHeader(mk_Writer *w, const char *writingApp,
341 const void *codecPrivate, unsigned codecPrivateSize,
342 int64_t default_frame_duration,
344 unsigned width, unsigned height,
345 unsigned d_width, unsigned d_height)
347 mk_Context *c, *ti, *v;
352 w->timescale = timescale;
353 w->def_duration = default_frame_duration;
355 if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
357 CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
358 CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
359 CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
360 CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
361 CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
362 CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
363 CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
364 CHECK(mk_closeContext(c, 0));
366 if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
368 CHECK(mk_flushContextID(c));
369 CHECK(mk_closeContext(c, 0));
371 if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
373 CHECK(mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0"));
374 CHECK(mk_writeStr(c, 0x5741, writingApp));
375 CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale));
376 CHECK(mk_writeFloat(c, 0x4489, 0));
377 w->duration_ptr = c->d_cur - 4;
378 CHECK(mk_closeContext(c, &w->duration_ptr));
380 if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks
382 if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry
384 CHECK(mk_writeUInt(ti, 0xd7, 1)); // TrackNumber
385 CHECK(mk_writeUInt(ti, 0x73c5, 1)); // TrackUID
386 CHECK(mk_writeUInt(ti, 0x83, 1)); // TrackType
387 CHECK(mk_writeUInt(ti, 0x9c, 0)); // FlagLacing
388 CHECK(mk_writeStr(ti, 0x86, codecID)); // CodecID
389 if (codecPrivateSize)
390 CHECK(mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize)); // CodecPrivate
391 if (default_frame_duration)
392 CHECK(mk_writeUInt(ti, 0x23e383, default_frame_duration)); // DefaultDuration
394 if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video
396 CHECK(mk_writeUInt(v, 0xb0, width));
397 CHECK(mk_writeUInt(v, 0xba, height));
398 CHECK(mk_writeUInt(v, 0x54b0, d_width));
399 CHECK(mk_writeUInt(v, 0x54ba, d_height));
400 CHECK(mk_closeContext(v, 0));
402 CHECK(mk_closeContext(ti, 0));
404 CHECK(mk_closeContext(c, 0));
406 CHECK(mk_flushContextData(w->root));
413 static int mk_closeCluster(mk_Writer *w) {
414 if (w->cluster == NULL)
416 CHECK(mk_closeContext(w->cluster, 0));
418 CHECK(mk_flushContextData(w->root));
422 int mk_flushFrame(mk_Writer *w) {
423 int64_t delta, ref = 0;
424 unsigned fsize, bgsize;
425 unsigned char c_delta_flags[3];
430 delta = w->frame_tc/w->timescale - w->cluster_tc_scaled;
431 if (delta > 32767ll || delta < -32768ll)
432 CHECK(mk_closeCluster(w));
434 if (w->cluster == NULL) {
435 w->cluster_tc_scaled = w->frame_tc / w->timescale;
436 w->cluster = mk_createContext(w, w->root, 0x1f43b675); // Cluster
437 if (w->cluster == NULL)
440 CHECK(mk_writeUInt(w->cluster, 0xe7, w->cluster_tc_scaled)); // Timecode
445 fsize = w->frame ? w->frame->d_cur : 0;
446 bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
448 ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta;
449 bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
452 CHECK(mk_writeID(w->cluster, 0xa0)); // BlockGroup
453 CHECK(mk_writeSize(w->cluster, bgsize));
454 CHECK(mk_writeID(w->cluster, 0xa1)); // Block
455 CHECK(mk_writeSize(w->cluster, fsize + 4));
456 CHECK(mk_writeSize(w->cluster, 1)); // track number
458 c_delta_flags[0] = delta >> 8;
459 c_delta_flags[1] = delta;
460 c_delta_flags[2] = 0;
461 CHECK(mk_appendContextData(w->cluster, c_delta_flags, 3));
463 CHECK(mk_appendContextData(w->cluster, w->frame->data, w->frame->d_cur));
467 CHECK(mk_writeSInt(w->cluster, 0xfb, ref)); // ReferenceBlock
470 w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta;
472 if (w->cluster->d_cur > CLSIZE)
473 CHECK(mk_closeCluster(w));
478 int mk_startFrame(mk_Writer *w) {
479 if (mk_flushFrame(w) < 0)
488 int mk_setFrameFlags(mk_Writer *w,int64_t timestamp, int keyframe) {
492 w->frame_tc = timestamp;
493 w->keyframe = keyframe != 0;
495 if (w->max_frame_tc < timestamp)
496 w->max_frame_tc = timestamp;
501 int mk_addFrameData(mk_Writer *w, const void *data, unsigned size) {
505 if (w->frame == NULL)
506 if ((w->frame = mk_createContext(w, NULL, 0)) == NULL)
509 return mk_appendContextData(w->frame, data, size);
512 int mk_close(mk_Writer *w) {
514 if (mk_flushFrame(w) < 0 || mk_closeCluster(w) < 0)
516 if (w->wrote_header) {
517 fseek(w->fp, w->duration_ptr, SEEK_SET);
518 if (mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
519 mk_flushContextData(w->root) < 0)
522 mk_destroyContexts(w);