1 /*****************************************************************************
2 * matroska_ebml.c: matroska muxer utilities
3 *****************************************************************************
4 * Copyright (C) 2005-2014 x264 project
6 * Authors: Mike Matsnev <mike@haali.su>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
22 * This program is also available under a commercial proprietary license.
23 * For more information, contact us at licensing@x264.com.
24 *****************************************************************************/
27 #include "matroska_ebml.h"
29 #define CLSIZE 1048576
38 struct mk_context *next, **prev, *parent;
43 unsigned d_cur, d_max;
46 typedef struct mk_context mk_context;
52 unsigned duration_ptr;
54 mk_context *root, *cluster, *frame;
60 int64_t cluster_tc_scaled;
61 int64_t frame_tc, max_frame_tc;
63 char wrote_header, in_frame, keyframe, skippable;
66 static mk_context *mk_create_context( mk_writer *w, mk_context *parent, unsigned id )
73 w->freelist = w->freelist->next;
77 c = calloc( 1, sizeof(mk_context) );
86 if( c->owner->actlist )
87 c->owner->actlist->prev = &c->next;
88 c->next = c->owner->actlist;
89 c->prev = &c->owner->actlist;
90 c->owner->actlist = c;
95 static int mk_append_context_data( mk_context *c, const void *data, unsigned size )
97 unsigned ns = c->d_cur + size;
102 unsigned dn = c->d_max ? c->d_max << 1 : 16;
106 dp = realloc( c->data, dn );
114 memcpy( (char*)c->data + c->d_cur, data, size );
121 static int mk_write_id( mk_context *c, unsigned id )
123 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
126 return mk_append_context_data( c, c_id, 4 );
128 return mk_append_context_data( c, c_id+1, 3 );
130 return mk_append_context_data( c, c_id+2, 2 );
131 return mk_append_context_data( c, c_id+3, 1 );
134 static int mk_write_size( mk_context *c, unsigned size )
136 unsigned char c_size[5] = { 0x08, size >> 24, size >> 16, size >> 8, size };
141 return mk_append_context_data( c, c_size+4, 1 );
146 return mk_append_context_data( c, c_size+3, 2 );
148 if( size < 0x1fffff )
151 return mk_append_context_data( c, c_size+2, 3 );
153 if( size < 0x0fffffff )
156 return mk_append_context_data( c, c_size+1, 4 );
158 return mk_append_context_data( c, c_size, 5 );
161 static int mk_flush_context_id( mk_context *c )
163 unsigned char ff = 0xff;
168 CHECK( mk_write_id( c->parent, c->id ) );
169 CHECK( mk_append_context_data( c->parent, &ff, 1 ) );
176 static int mk_flush_context_data( mk_context *c )
182 CHECK( mk_append_context_data( c->parent, c->data, c->d_cur ) );
183 else if( fwrite( c->data, c->d_cur, 1, c->owner->fp ) != 1 )
191 static int mk_close_context( mk_context *c, unsigned *off )
195 CHECK( mk_write_id( c->parent, c->id ) );
196 CHECK( mk_write_size( c->parent, c->d_cur ) );
199 if( c->parent && off )
200 *off += c->parent->d_cur;
202 CHECK( mk_flush_context_data( c ) );
205 c->next->prev = c->prev;
206 *(c->prev) = c->next;
207 c->next = c->owner->freelist;
208 c->owner->freelist = c;
213 static void mk_destroy_contexts( mk_writer *w )
217 for( mk_context *cur = w->freelist; cur; cur = next )
224 for( mk_context *cur = w->actlist; cur; cur = next )
231 w->freelist = w->actlist = w->root = NULL;
234 static int mk_write_string( mk_context *c, unsigned id, const char *str )
236 size_t len = strlen( str );
238 CHECK( mk_write_id( c, id ) );
239 CHECK( mk_write_size( c, len ) );
240 CHECK( mk_append_context_data( c, str, len ) );
244 static int mk_write_bin( mk_context *c, unsigned id, const void *data, unsigned size )
246 CHECK( mk_write_id( c, id ) );
247 CHECK( mk_write_size( c, size ) );
248 CHECK( mk_append_context_data( c, data, size ) ) ;
252 static int mk_write_uint( mk_context *c, unsigned id, int64_t ui )
254 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
257 CHECK( mk_write_id( c, id ) );
258 while( i < 7 && !c_ui[i] )
260 CHECK( mk_write_size( c, 8 - i ) );
261 CHECK( mk_append_context_data( c, c_ui+i, 8 - i ) );
265 static int mk_write_float_raw( mk_context *c, float f )
272 unsigned char c_f[4];
280 return mk_append_context_data( c, c_f, 4 );
283 static int mk_write_float( mk_context *c, unsigned id, float f )
285 CHECK( mk_write_id( c, id ) );
286 CHECK( mk_write_size( c, 4 ) );
287 CHECK( mk_write_float_raw( c, f ) );
291 mk_writer *mk_create_writer( const char *filename )
293 mk_writer *w = calloc( 1, sizeof(mk_writer) );
297 w->root = mk_create_context( w, NULL, 0 );
304 if( !strcmp( filename, "-" ) )
307 w->fp = x264_fopen( filename, "wb" );
310 mk_destroy_contexts( w );
315 w->timescale = 1000000;
320 static const uint8_t mk_stereo_modes[6] = {5,9,7,1,3,13};
322 int mk_write_header( mk_writer *w, const char *writing_app,
323 const char *codec_id,
324 const void *codec_private, unsigned codec_private_size,
325 int64_t default_frame_duration,
327 unsigned width, unsigned height,
328 unsigned d_width, unsigned d_height, int display_size_units, int stereo_mode )
330 mk_context *c, *ti, *v;
332 if( w->wrote_header )
335 w->timescale = timescale;
336 w->def_duration = default_frame_duration;
338 if( !(c = mk_create_context( w, w->root, 0x1a45dfa3 )) ) // EBML
340 CHECK( mk_write_uint( c, 0x4286, 1 ) ); // EBMLVersion
341 CHECK( mk_write_uint( c, 0x42f7, 1 ) ); // EBMLReadVersion
342 CHECK( mk_write_uint( c, 0x42f2, 4 ) ); // EBMLMaxIDLength
343 CHECK( mk_write_uint( c, 0x42f3, 8 ) ); // EBMLMaxSizeLength
344 CHECK( mk_write_string( c, 0x4282, "matroska") ); // DocType
345 CHECK( mk_write_uint( c, 0x4287, 2 ) ); // DocTypeVersion
346 CHECK( mk_write_uint( c, 0x4285, 2 ) ); // DocTypeReadversion
347 CHECK( mk_close_context( c, 0 ) );
349 if( !(c = mk_create_context( w, w->root, 0x18538067 )) ) // Segment
351 CHECK( mk_flush_context_id( c ) );
352 CHECK( mk_close_context( c, 0 ) );
354 if( !(c = mk_create_context( w, w->root, 0x1549a966 )) ) // SegmentInfo
356 CHECK( mk_write_string( c, 0x4d80, "Haali Matroska Writer b0" ) );
357 CHECK( mk_write_string( c, 0x5741, writing_app ) );
358 CHECK( mk_write_uint( c, 0x2ad7b1, w->timescale ) );
359 CHECK( mk_write_float( c, 0x4489, 0) );
360 w->duration_ptr = c->d_cur - 4;
361 CHECK( mk_close_context( c, &w->duration_ptr ) );
363 if( !(c = mk_create_context( w, w->root, 0x1654ae6b )) ) // tracks
365 if( !(ti = mk_create_context( w, c, 0xae )) ) // TrackEntry
367 CHECK( mk_write_uint( ti, 0xd7, 1 ) ); // TrackNumber
368 CHECK( mk_write_uint( ti, 0x73c5, 1 ) ); // TrackUID
369 CHECK( mk_write_uint( ti, 0x83, 1 ) ); // TrackType
370 CHECK( mk_write_uint( ti, 0x9c, 0 ) ); // FlagLacing
371 CHECK( mk_write_string( ti, 0x86, codec_id ) ); // codec_id
372 if( codec_private_size )
373 CHECK( mk_write_bin( ti, 0x63a2, codec_private, codec_private_size ) ); // codec_private
374 if( default_frame_duration )
375 CHECK( mk_write_uint( ti, 0x23e383, default_frame_duration ) ); // DefaultDuration
377 if( !(v = mk_create_context( w, ti, 0xe0 ) ) ) // Video
379 CHECK( mk_write_uint( v, 0xb0, width ) );
380 CHECK( mk_write_uint( v, 0xba, height ) );
381 CHECK( mk_write_uint( v, 0x54b2, display_size_units ) );
382 CHECK( mk_write_uint( v, 0x54b0, d_width ) );
383 CHECK( mk_write_uint( v, 0x54ba, d_height ) );
384 if( stereo_mode >= 0 && stereo_mode <= 5 )
385 CHECK( mk_write_uint( v, 0x53b8, mk_stereo_modes[stereo_mode] ) );
386 CHECK( mk_close_context( v, 0 ) );
388 CHECK( mk_close_context( ti, 0 ) );
390 CHECK( mk_close_context( c, 0 ) );
392 CHECK( mk_flush_context_data( w->root ) );
399 static int mk_close_cluster( mk_writer *w )
401 if( w->cluster == NULL )
403 CHECK( mk_close_context( w->cluster, 0 ) );
405 CHECK( mk_flush_context_data( w->root ) );
409 static int mk_flush_frame( mk_writer *w )
413 unsigned char c_delta_flags[3];
418 delta = w->frame_tc/w->timescale - w->cluster_tc_scaled;
419 if( delta > 32767ll || delta < -32768ll )
420 CHECK( mk_close_cluster( w ) );
424 w->cluster_tc_scaled = w->frame_tc / w->timescale;
425 w->cluster = mk_create_context( w, w->root, 0x1f43b675 ); // Cluster
429 CHECK( mk_write_uint( w->cluster, 0xe7, w->cluster_tc_scaled ) ); // Timecode
434 fsize = w->frame ? w->frame->d_cur : 0;
436 CHECK( mk_write_id( w->cluster, 0xa3 ) ); // SimpleBlock
437 CHECK( mk_write_size( w->cluster, fsize + 4 ) );
438 CHECK( mk_write_size( w->cluster, 1 ) ); // track number
440 c_delta_flags[0] = delta >> 8;
441 c_delta_flags[1] = delta;
442 c_delta_flags[2] = (w->keyframe << 7) | w->skippable;
443 CHECK( mk_append_context_data( w->cluster, c_delta_flags, 3 ) );
446 CHECK( mk_append_context_data( w->cluster, w->frame->data, w->frame->d_cur ) );
452 if( w->cluster->d_cur > CLSIZE )
453 CHECK( mk_close_cluster( w ) );
458 int mk_start_frame( mk_writer *w )
460 if( mk_flush_frame( w ) < 0 )
470 int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe, int skippable )
475 w->frame_tc = timestamp;
476 w->keyframe = keyframe != 0;
477 w->skippable = skippable != 0;
479 if( w->max_frame_tc < timestamp )
480 w->max_frame_tc = timestamp;
485 int mk_add_frame_data( mk_writer *w, const void *data, unsigned size )
491 if( !(w->frame = mk_create_context( w, NULL, 0 )) )
494 return mk_append_context_data( w->frame, data, size );
497 int mk_close( mk_writer *w, int64_t last_delta )
500 if( mk_flush_frame( w ) < 0 || mk_close_cluster( w ) < 0 )
502 if( w->wrote_header && x264_is_regular_file( w->fp ) )
504 fseek( w->fp, w->duration_ptr, SEEK_SET );
505 int64_t last_frametime = w->def_duration ? w->def_duration : last_delta;
506 int64_t total_duration = w->max_frame_tc+last_frametime;
507 if( mk_write_float_raw( w->root, (float)((double)total_duration / w->timescale) ) < 0 ||
508 mk_flush_context_data( w->root ) < 0 )
511 mk_destroy_contexts( w );