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