]> git.sesse.net Git - x264/blob - matroska.c
Fixed compilation on PPC (spotted by David Wolstencroft)
[x264] / matroska.c
1 /*****************************************************************************
2  * matroska.c:
3  *****************************************************************************
4  * Copyright (C) 2005 x264 project
5  * $Id: $
6  *
7  * Authors: Mike Matsnev
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef HAVE_STDINT_H
29 #include <stdint.h>
30 #else
31 #include <inttypes.h>
32 #endif
33
34 #include "matroska.h"
35
36 #define CLSIZE    1048576
37 #define CHECK(x)  do { if ((x) < 0) return -1; } while (0)
38
39 struct mk_Context {
40   struct mk_Context *next, **prev, *parent;
41   struct mk_Writer  *owner;
42   unsigned          id;
43
44   void              *data;
45   unsigned          d_cur, d_max;
46 };
47
48 typedef struct mk_Context mk_Context;
49
50 struct mk_Writer {
51   FILE                *fp;
52
53   unsigned            duration_ptr;
54
55   mk_Context          *root, *cluster, *frame;
56   mk_Context          *freelist;
57   mk_Context          *actlist;
58
59   int64_t             def_duration;
60   int64_t             timescale;
61   int64_t             cluster_tc_scaled;
62   int64_t             frame_tc, prev_frame_tc_scaled, max_frame_tc;
63
64   char                wrote_header, in_frame, keyframe;
65 };
66
67 static mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
68   mk_Context  *c;
69
70   if (w->freelist) {
71     c = w->freelist;
72     w->freelist = w->freelist->next;
73   } else {
74     c = malloc(sizeof(*c));
75     memset(c, 0, sizeof(*c));
76   }
77
78   if (c == NULL)
79     return NULL;
80
81   c->parent = parent;
82   c->owner = w;
83   c->id = id;
84
85   if (c->owner->actlist)
86     c->owner->actlist->prev = &c->next;
87   c->next = c->owner->actlist;
88   c->prev = &c->owner->actlist;
89
90   return c;
91 }
92
93 static int        mk_appendContextData(mk_Context *c, const void *data, unsigned size) {
94   unsigned  ns = c->d_cur + size;
95
96   if (ns > c->d_max) {
97     void      *dp;
98     unsigned  dn = c->d_max ? c->d_max << 1 : 16;
99     while (ns > dn)
100       dn <<= 1;
101
102     dp = realloc(c->data, dn);
103     if (dp == NULL)
104       return -1;
105
106     c->data = dp;
107     c->d_max = dn;
108   }
109
110   memcpy((char*)c->data + c->d_cur, data, size);
111
112   c->d_cur = ns;
113
114   return 0;
115 }
116
117 static int        mk_writeID(mk_Context *c, unsigned id) {
118   unsigned char   c_id[4] = { id >> 24, id >> 16, id >> 8, id };
119
120   if (c_id[0])
121     return mk_appendContextData(c, c_id, 4);
122   if (c_id[1])
123     return mk_appendContextData(c, c_id+1, 3);
124   if (c_id[2])
125     return mk_appendContextData(c, c_id+2, 2);
126   return mk_appendContextData(c, c_id+3, 1);
127 }
128
129 static int        mk_writeSize(mk_Context *c, unsigned size) {
130   unsigned char   c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size };
131
132   if (size < 0x7f) {
133     c_size[4] |= 0x80;
134     return mk_appendContextData(c, c_size+4, 1);
135   }
136   if (size < 0x3fff) {
137     c_size[3] |= 0x40;
138     return mk_appendContextData(c, c_size+3, 2);
139   }
140   if (size < 0x1fffff) {
141     c_size[2] |= 0x20;
142     return mk_appendContextData(c, c_size+2, 3);
143   }
144   if (size < 0x0fffffff) {
145     c_size[1] |= 0x10;
146     return mk_appendContextData(c, c_size+1, 4);
147   }
148   return mk_appendContextData(c, c_size, 5);
149 }
150
151 static int        mk_flushContextID(mk_Context *c) {
152   unsigned char ff = 0xff;
153
154   if (c->id == 0)
155     return 0;
156
157   CHECK(mk_writeID(c->parent, c->id));
158   CHECK(mk_appendContextData(c->parent, &ff, 1));
159
160   c->id = 0;
161
162   return 0;
163 }
164
165 static int        mk_flushContextData(mk_Context *c) {
166   if (c->d_cur == 0)
167     return 0;
168
169   if (c->parent)
170     CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
171   else
172     if (fwrite(c->data, c->d_cur, 1, c->owner->fp) != 1)
173       return -1;
174
175   c->d_cur = 0;
176
177   return 0;
178 }
179
180 static int        mk_closeContext(mk_Context *c, unsigned *off) {
181   if (c->id) {
182     CHECK(mk_writeID(c->parent, c->id));
183     CHECK(mk_writeSize(c->parent, c->d_cur));
184   }
185
186   if (c->parent && off != NULL)
187     *off += c->parent->d_cur;
188
189   CHECK(mk_flushContextData(c));
190
191   if (c->next)
192     c->next->prev = c->prev;
193   *(c->prev) = c->next;
194   c->next = c->owner->freelist;
195   c->owner->freelist = c;
196
197   return 0;
198 }
199
200 static void       mk_destroyContexts(mk_Writer *w) {
201   mk_Context  *cur, *next;
202
203   for (cur = w->freelist; cur; cur = next) {
204     next = cur->next;
205     free(cur->data);
206     free(cur);
207   }
208
209   for (cur = w->actlist; cur; cur = next) {
210     next = cur->next;
211     free(cur->data);
212     free(cur);
213   }
214
215   w->freelist = w->actlist = w->root = NULL;
216 }
217
218 static int        mk_writeStr(mk_Context *c, unsigned id, const char *str) {
219   size_t  len = strlen(str);
220
221   CHECK(mk_writeID(c, id));
222   CHECK(mk_writeSize(c, len));
223   CHECK(mk_appendContextData(c, str, len));
224   return 0;
225 }
226
227 static int        mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
228   CHECK(mk_writeID(c, id));
229   CHECK(mk_writeSize(c, size));
230   CHECK(mk_appendContextData(c, data, size));
231   return 0;
232 }
233
234 static int        mk_writeUInt(mk_Context *c, unsigned id, int64_t ui) {
235   unsigned char   c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
236   unsigned        i = 0;
237
238   CHECK(mk_writeID(c, id));
239   while (i < 7 && c_ui[i] == 0)
240     ++i;
241   CHECK(mk_writeSize(c, 8 - i));
242   CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
243   return 0;
244 }
245
246 static int        mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
247   unsigned char   c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
248   unsigned        i = 0;
249
250   CHECK(mk_writeID(c, id));
251   if (si < 0)
252     while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
253       ++i;
254   else
255     while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
256       ++i;
257   CHECK(mk_writeSize(c, 8 - i));
258   CHECK(mk_appendContextData(c, c_si+i, 8 - i));
259   return 0;
260 }
261
262 static int        mk_writeFloatRaw(mk_Context *c, float f) {
263   union {
264     float f;
265     unsigned u;
266   } u;
267   unsigned char c_f[4];
268
269   u.f = f;
270   c_f[0] = u.u >> 24;
271   c_f[1] = u.u >> 16;
272   c_f[2] = u.u >> 8;
273   c_f[3] = u.u;
274
275   return mk_appendContextData(c, c_f, 4);
276 }
277
278 static int        mk_writeFloat(mk_Context *c, unsigned id, float f) {
279   CHECK(mk_writeID(c, id));
280   CHECK(mk_writeSize(c, 4));
281   CHECK(mk_writeFloatRaw(c, f));
282   return 0;
283 }
284
285 static unsigned   mk_ebmlSizeSize(unsigned s) {
286   if (s < 0x7f)
287     return 1;
288   if (s < 0x3fff)
289     return 2;
290   if (s < 0x1fffff)
291     return 3;
292   if (s < 0x0fffffff)
293     return 4;
294   return 5;
295 }
296
297 static unsigned   mk_ebmlSIntSize(int64_t si) {
298   unsigned char   c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
299   unsigned        i = 0;
300
301   if (si < 0)
302     while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
303       ++i;
304   else
305     while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
306       ++i;
307
308   return 8 - i;
309 }
310
311 mk_Writer *mk_createWriter(const char *filename) {
312   mk_Writer *w = malloc(sizeof(*w));
313   if (w == NULL)
314     return NULL;
315
316   memset(w, 0, sizeof(*w));
317
318   w->root = mk_createContext(w, NULL, 0);
319   if (w->root == NULL) {
320     free(w);
321     return NULL;
322   }
323
324   w->fp = fopen(filename, "wb");
325   if (w->fp == NULL) {
326     mk_destroyContexts(w);
327     free(w);
328     return NULL;
329   }
330
331   w->timescale = 1000000;
332
333   return w;
334 }
335
336 int       mk_writeHeader(mk_Writer *w, const char *writingApp,
337                          const char *codecID,
338                          const void *codecPrivate, unsigned codecPrivateSize,
339                          int64_t default_frame_duration,
340                          int64_t timescale,
341                          unsigned width, unsigned height,
342                          unsigned d_width, unsigned d_height)
343 {
344   mk_Context  *c, *ti, *v;
345
346   if (w->wrote_header)
347     return -1;
348
349   w->timescale = timescale;
350   w->def_duration = default_frame_duration;
351
352   if ((c = mk_createContext(w, w->root, 0x1a45dfa3)) == NULL) // EBML
353     return -1;
354   CHECK(mk_writeUInt(c, 0x4286, 1)); // EBMLVersion
355   CHECK(mk_writeUInt(c, 0x42f7, 1)); // EBMLReadVersion
356   CHECK(mk_writeUInt(c, 0x42f2, 4)); // EBMLMaxIDLength
357   CHECK(mk_writeUInt(c, 0x42f3, 8)); // EBMLMaxSizeLength
358   CHECK(mk_writeStr(c, 0x4282, "matroska")); // DocType
359   CHECK(mk_writeUInt(c, 0x4287, 1)); // DocTypeVersion
360   CHECK(mk_writeUInt(c, 0x4285, 1)); // DocTypeReadversion
361   CHECK(mk_closeContext(c, 0));
362
363   if ((c = mk_createContext(w, w->root, 0x18538067)) == NULL) // Segment
364     return -1;
365   CHECK(mk_flushContextID(c));
366   CHECK(mk_closeContext(c, 0));
367
368   if ((c = mk_createContext(w, w->root, 0x1549a966)) == NULL) // SegmentInfo
369     return -1;
370   CHECK(mk_writeStr(c, 0x4d80, "Haali Matroska Writer b0"));
371   CHECK(mk_writeStr(c, 0x5741, writingApp));
372   CHECK(mk_writeUInt(c, 0x2ad7b1, w->timescale));
373   CHECK(mk_writeFloat(c, 0x4489, 0));
374   w->duration_ptr = c->d_cur - 4;
375   CHECK(mk_closeContext(c, &w->duration_ptr));
376
377   if ((c = mk_createContext(w, w->root, 0x1654ae6b)) == NULL) // tracks
378     return -1;
379   if ((ti = mk_createContext(w, c, 0xae)) == NULL) // TrackEntry
380     return -1;
381   CHECK(mk_writeUInt(ti, 0xd7, 1)); // TrackNumber
382   CHECK(mk_writeUInt(ti, 0x73c5, 1)); // TrackUID
383   CHECK(mk_writeUInt(ti, 0x83, 1)); // TrackType
384   CHECK(mk_writeUInt(ti, 0x9c, 0)); // FlagLacing
385   CHECK(mk_writeStr(ti, 0x86, codecID)); // CodecID
386   if (codecPrivateSize)
387     CHECK(mk_writeBin(ti, 0x63a2, codecPrivate, codecPrivateSize)); // CodecPrivate
388   if (default_frame_duration)
389     CHECK(mk_writeUInt(ti, 0x23e383, default_frame_duration)); // DefaultDuration
390
391   if ((v = mk_createContext(w, ti, 0xe0)) == NULL) // Video
392     return -1;
393   CHECK(mk_writeUInt(v, 0xb0, width));
394   CHECK(mk_writeUInt(v, 0xba, height));
395   CHECK(mk_writeUInt(v, 0x54b0, d_width));
396   CHECK(mk_writeUInt(v, 0x54ba, d_height));
397   CHECK(mk_closeContext(v, 0));
398
399   CHECK(mk_closeContext(ti, 0));
400
401   CHECK(mk_closeContext(c, 0));
402
403   CHECK(mk_flushContextData(w->root));
404
405   w->wrote_header = 1;
406
407   return 0;
408 }
409
410 static int mk_closeCluster(mk_Writer *w) {
411   if (w->cluster == NULL)
412     return 0;
413   CHECK(mk_closeContext(w->cluster, 0));
414   w->cluster = NULL;
415   CHECK(mk_flushContextData(w->root));
416   return 0;
417 }
418
419 int       mk_flushFrame(mk_Writer *w) {
420   int64_t       delta, ref = 0;
421   unsigned      fsize, bgsize;
422   unsigned char c_delta_flags[3];
423
424   if (!w->in_frame)
425     return 0;
426
427   delta = w->frame_tc/w->timescale - w->cluster_tc_scaled;
428   if (delta > 32767ll || delta < -32768ll)
429     CHECK(mk_closeCluster(w));
430
431   if (w->cluster == NULL) {
432     w->cluster_tc_scaled = w->frame_tc / w->timescale;
433     w->cluster = mk_createContext(w, w->root, 0x1f43b675); // Cluster
434     if (w->cluster == NULL)
435       return -1;
436
437     CHECK(mk_writeUInt(w->cluster, 0xe7, w->cluster_tc_scaled)); // Timecode
438
439     delta = 0;
440   }
441
442   fsize = w->frame ? w->frame->d_cur : 0;
443   bgsize = fsize + 4 + mk_ebmlSizeSize(fsize + 4) + 1;
444   if (!w->keyframe) {
445     ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta;
446     bgsize += 1 + 1 + mk_ebmlSIntSize(ref);
447   }
448
449   CHECK(mk_writeID(w->cluster, 0xa0)); // BlockGroup
450   CHECK(mk_writeSize(w->cluster, bgsize));
451   CHECK(mk_writeID(w->cluster, 0xa1)); // Block
452   CHECK(mk_writeSize(w->cluster, fsize + 4));
453   CHECK(mk_writeSize(w->cluster, 1)); // track number
454
455   c_delta_flags[0] = delta >> 8;
456   c_delta_flags[1] = delta;
457   c_delta_flags[2] = 0;
458   CHECK(mk_appendContextData(w->cluster, c_delta_flags, 3));
459   if (w->frame) {
460     CHECK(mk_appendContextData(w->cluster, w->frame->data, w->frame->d_cur));
461     w->frame->d_cur = 0;
462   }
463   if (!w->keyframe)
464     CHECK(mk_writeSInt(w->cluster, 0xfb, ref)); // ReferenceBlock
465
466   w->in_frame = 0;
467   w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta;
468
469   if (w->cluster->d_cur > CLSIZE)
470     CHECK(mk_closeCluster(w));
471
472   return 0;
473 }
474
475 int       mk_startFrame(mk_Writer *w) {
476   if (mk_flushFrame(w) < 0)
477     return -1;
478
479   w->in_frame = 1;
480   w->keyframe = 0;
481
482   return 0;
483 }
484
485 int       mk_setFrameFlags(mk_Writer *w,int64_t timestamp, int keyframe) {
486   if (!w->in_frame)
487     return -1;
488
489   w->frame_tc = timestamp;
490   w->keyframe = keyframe != 0;
491
492   if (w->max_frame_tc < timestamp)
493     w->max_frame_tc = timestamp;
494
495   return 0;
496 }
497
498 int       mk_addFrameData(mk_Writer *w, const void *data, unsigned size) {
499   if (!w->in_frame)
500     return -1;
501
502   if (w->frame == NULL)
503     if ((w->frame = mk_createContext(w, NULL, 0)) == NULL)
504       return -1;
505
506   return mk_appendContextData(w->frame, data, size);
507 }
508
509 int       mk_close(mk_Writer *w) {
510   int   ret = 0;
511   if (mk_flushFrame(w) < 0 || mk_closeCluster(w) < 0)
512     ret = -1;
513   if (w->wrote_header) {
514     fseek(w->fp, w->duration_ptr, SEEK_SET);
515     if (mk_writeFloatRaw(w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale)) < 0 ||
516         mk_flushContextData(w->root) < 0)
517       ret = -1;
518   }
519   mk_destroyContexts(w);
520   fclose(w->fp);
521   free(w);
522   return ret;
523 }
524