]> git.sesse.net Git - x264/blobdiff - output/matroska_ebml.c
arm: Implement x264_mbtree_propagate_{cost, list}_neon
[x264] / output / matroska_ebml.c
index 4e6bf0c7ccf7093a1f63600a194b29c5c58947a0..dc0d44d0a041ca817fca950f91d7921289d00ffb 100644 (file)
@@ -1,7 +1,9 @@
 /*****************************************************************************
- * matroska_ebml.c:
+ * matroska_ebml.c: matroska muxer utilities
  *****************************************************************************
- * Copyright (C) 2005 Mike Matsnev
+ * Copyright (C) 2005-2015 x264 project
+ *
+ * Authors: Mike Matsnev <mike@haali.su>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
+ *
+ * This program is also available under a commercial proprietary license.
+ * For more information, contact us at licensing@x264.com.
  *****************************************************************************/
 
-#include <stdlib.h>
-#include <string.h>
-#include "common/osdep.h"
+#include "output.h"
 #include "matroska_ebml.h"
 
 #define CLSIZE 1048576
@@ -55,9 +58,9 @@ struct mk_writer
     int64_t def_duration;
     int64_t timescale;
     int64_t cluster_tc_scaled;
-    int64_t frame_tc, prev_frame_tc_scaled, max_frame_tc;
+    int64_t frame_tc, max_frame_tc;
 
-    char wrote_header, in_frame, keyframe;
+    char wrote_header, in_frame, keyframe, skippable;
 };
 
 static mk_context *mk_create_context( mk_writer *w, mk_context *parent, unsigned id )
@@ -71,10 +74,9 @@ static mk_context *mk_create_context( mk_writer *w, mk_context *parent, unsigned
     }
     else
     {
-        c = malloc( sizeof(*c) );
+        c = calloc( 1, sizeof(mk_context) );
         if( !c )
             return NULL;
-        memset( c, 0, sizeof(*c) );
     }
 
     c->parent = parent;
@@ -210,16 +212,16 @@ static int mk_close_context( mk_context *c, unsigned *off )
 
 static void mk_destroy_contexts( mk_writer *w )
 {
-    mk_context *cur, *next;
+    mk_context *next;
 
-    for( cur = w->freelist; cur; cur = next )
+    for( mk_context *cur = w->freelist; cur; cur = next )
     {
         next = cur->next;
         free( cur->data );
         free( cur );
     }
 
-    for( cur = w->actlist; cur; cur = next )
+    for( mk_context *cur = w->actlist; cur; cur = next )
     {
         next = cur->next;
         free( cur->data );
@@ -260,23 +262,6 @@ static int mk_write_uint( mk_context *c, unsigned id, int64_t ui )
     return 0;
 }
 
-static int mk_write_sint( mk_context *c, unsigned id, int64_t si )
-{
-    unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
-    unsigned i = 0;
-
-    CHECK( mk_write_id( c, id ) );
-    if( si < 0 )
-        while( i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80 )
-            ++i;
-    else
-        while( i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80 ) )
-            ++i;
-    CHECK( mk_write_size( c, 8 - i ) );
-    CHECK( mk_append_context_data( c, c_si+i, 8 - i ) );
-    return 0;
-}
-
 static int mk_write_float_raw( mk_context *c, float f )
 {
     union
@@ -303,42 +288,12 @@ static int mk_write_float( mk_context *c, unsigned id, float f )
     return 0;
 }
 
-static unsigned mk_ebml_size_size( unsigned s )
-{
-    if( s < 0x7f )
-        return 1;
-    if( s < 0x3fff )
-        return 2;
-    if( s < 0x1fffff )
-        return 3;
-    if( s < 0x0fffffff )
-        return 4;
-    return 5;
-}
-
-static unsigned mk_ebml_sint_size( int64_t si )
-{
-    unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
-    unsigned i = 0;
-
-    if( si < 0 )
-        while( i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80 )
-            ++i;
-    else
-        while( i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80) )
-            ++i;
-
-    return 8 - i;
-}
-
 mk_writer *mk_create_writer( const char *filename )
 {
-    mk_writer *w = malloc( sizeof(*w) );
+    mk_writer *w = calloc( 1, sizeof(mk_writer) );
     if( !w )
         return NULL;
 
-    memset( w, 0, sizeof(*w) );
-
     w->root = mk_create_context( w, NULL, 0 );
     if( !w->root )
     {
@@ -346,7 +301,10 @@ mk_writer *mk_create_writer( const char *filename )
         return NULL;
     }
 
-    w->fp = fopen( filename, "wb" );
+    if( !strcmp( filename, "-" ) )
+        w->fp = stdout;
+    else
+        w->fp = x264_fopen( filename, "wb" );
     if( !w->fp )
     {
         mk_destroy_contexts( w );
@@ -359,13 +317,13 @@ mk_writer *mk_create_writer( const char *filename )
     return w;
 }
 
-int mk_writeHeader( mk_writer *w, const char *writing_app,
-                    const char *codec_id,
-                    const void *codec_private, unsigned codec_private_size,
-                    int64_t default_frame_duration,
-                    int64_t timescale,
-                    unsigned width, unsigned height,
-                    unsigned d_width, unsigned d_height )
+int mk_write_header( mk_writer *w, const char *writing_app,
+                     const char *codec_id,
+                     const void *codec_private, unsigned codec_private_size,
+                     int64_t default_frame_duration,
+                     int64_t timescale,
+                     unsigned width, unsigned height,
+                     unsigned d_width, unsigned d_height, int display_size_units, int stereo_mode )
 {
     mk_context  *c, *ti, *v;
 
@@ -382,8 +340,8 @@ int mk_writeHeader( mk_writer *w, const char *writing_app,
     CHECK( mk_write_uint( c, 0x42f2, 4 ) ); // EBMLMaxIDLength
     CHECK( mk_write_uint( c, 0x42f3, 8 ) ); // EBMLMaxSizeLength
     CHECK( mk_write_string( c, 0x4282, "matroska") ); // DocType
-    CHECK( mk_write_uint( c, 0x4287, 1 ) ); // DocTypeVersion
-    CHECK( mk_write_uint( c, 0x4285, 1 ) ); // DocTypeReadversion
+    CHECK( mk_write_uint( c, 0x4287, stereo_mode >= 0 ? 3 : 2 ) ); // DocTypeVersion
+    CHECK( mk_write_uint( c, 0x4285, 2 ) ); // DocTypeReadVersion
     CHECK( mk_close_context( c, 0 ) );
 
     if( !(c = mk_create_context( w, w->root, 0x18538067 )) ) // Segment
@@ -393,14 +351,14 @@ int mk_writeHeader( mk_writer *w, const char *writing_app,
 
     if( !(c = mk_create_context( w, w->root, 0x1549a966 )) ) // SegmentInfo
         return -1;
-    CHECK( mk_write_string( c, 0x4d80, "Haali Matroska Writer b0" ) );
-    CHECK( mk_write_string( c, 0x5741, writing_app ) );
-    CHECK( mk_write_uint( c, 0x2ad7b1, w->timescale ) );
-    CHECK( mk_write_float( c, 0x4489, 0) );
+    CHECK( mk_write_string( c, 0x4d80, "Haali Matroska Writer b0" ) ); // MuxingApp
+    CHECK( mk_write_string( c, 0x5741, writing_app ) ); // WritingApp
+    CHECK( mk_write_uint( c, 0x2ad7b1, w->timescale ) ); // TimecodeScale
+    CHECK( mk_write_float( c, 0x4489, 0) ); // Duration
     w->duration_ptr = c->d_cur - 4;
     CHECK( mk_close_context( c, &w->duration_ptr ) );
 
-    if( !(c = mk_create_context( w, w->root, 0x1654ae6b )) ) // tracks
+    if( !(c = mk_create_context( w, w->root, 0x1654ae6b )) ) // Tracks
         return -1;
     if( !(ti = mk_create_context( w, c, 0xae )) ) // TrackEntry
         return -1;
@@ -408,18 +366,21 @@ int mk_writeHeader( mk_writer *w, const char *writing_app,
     CHECK( mk_write_uint( ti, 0x73c5, 1 ) ); // TrackUID
     CHECK( mk_write_uint( ti, 0x83, 1 ) ); // TrackType
     CHECK( mk_write_uint( ti, 0x9c, 0 ) ); // FlagLacing
-    CHECK( mk_write_string( ti, 0x86, codec_id ) ); // codec_id
+    CHECK( mk_write_string( ti, 0x86, codec_id ) ); // CodecID
     if( codec_private_size )
-        CHECK( mk_write_bin( ti, 0x63a2, codec_private, codec_private_size ) ); // codec_private
+        CHECK( mk_write_bin( ti, 0x63a2, codec_private, codec_private_size ) ); // CodecPrivate
     if( default_frame_duration )
         CHECK( mk_write_uint( ti, 0x23e383, default_frame_duration ) ); // DefaultDuration
 
     if( !(v = mk_create_context( w, ti, 0xe0 ) ) ) // Video
         return -1;
-    CHECK( mk_write_uint( v, 0xb0, width ) );
-    CHECK( mk_write_uint( v, 0xba, height ) );
-    CHECK( mk_write_uint( v, 0x54b0, d_width ) );
-    CHECK( mk_write_uint( v, 0x54ba, d_height ) );
+    CHECK( mk_write_uint( v, 0xb0, width ) ); // PixelWidth
+    CHECK( mk_write_uint( v, 0xba, height ) ); // PixelHeight
+    CHECK( mk_write_uint( v, 0x54b2, display_size_units ) ); // DisplayUnit
+    CHECK( mk_write_uint( v, 0x54b0, d_width ) ); // DisplayWidth
+    CHECK( mk_write_uint( v, 0x54ba, d_height ) ); // DisplayHeight
+    if( stereo_mode >= 0 )
+        CHECK( mk_write_uint( v, 0x53b8, stereo_mode ) ); // StereoMode
     CHECK( mk_close_context( v, 0 ) );
 
     CHECK( mk_close_context( ti, 0 ) );
@@ -445,8 +406,8 @@ static int mk_close_cluster( mk_writer *w )
 
 static int mk_flush_frame( mk_writer *w )
 {
-    int64_t delta, ref = 0;
-    unsigned fsize, bgsize;
+    int64_t delta;
+    unsigned fsize;
     unsigned char c_delta_flags[3];
 
     if( !w->in_frame )
@@ -469,33 +430,22 @@ static int mk_flush_frame( mk_writer *w )
     }
 
     fsize = w->frame ? w->frame->d_cur : 0;
-    bgsize = fsize + 4 + mk_ebml_size_size( fsize + 4 ) + 1;
-    if( !w->keyframe )
-    {
-        ref = w->prev_frame_tc_scaled - w->cluster_tc_scaled - delta;
-        bgsize += 1 + 1 + mk_ebml_sint_size( ref );
-    }
 
-    CHECK( mk_write_id( w->cluster, 0xa0 ) ); // BlockGroup
-    CHECK( mk_write_size( w->cluster, bgsize ) );
-    CHECK( mk_write_id( w->cluster, 0xa1 ) ); // Block
-    CHECK( mk_write_size( w->cluster, fsize + 4 ) );
-    CHECK( mk_write_size( w->cluster, 1 ) ); // track number
+    CHECK( mk_write_id( w->cluster, 0xa3 ) ); // SimpleBlock
+    CHECK( mk_write_size( w->cluster, fsize + 4 ) ); // Size
+    CHECK( mk_write_size( w->cluster, 1 ) ); // TrackNumber
 
     c_delta_flags[0] = delta >> 8;
     c_delta_flags[1] = delta;
-    c_delta_flags[2] = 0;
-    CHECK( mk_append_context_data( w->cluster, c_delta_flags, 3 ) );
+    c_delta_flags[2] = (w->keyframe << 7) | w->skippable;
+    CHECK( mk_append_context_data( w->cluster, c_delta_flags, 3 ) ); // Timecode, Flags
     if( w->frame )
     {
-        CHECK( mk_append_context_data( w->cluster, w->frame->data, w->frame->d_cur ) );
+        CHECK( mk_append_context_data( w->cluster, w->frame->data, w->frame->d_cur ) ); // Data
         w->frame->d_cur = 0;
     }
-    if( !w->keyframe )
-        CHECK( mk_write_sint( w->cluster, 0xfb, ref ) ); // ReferenceBlock
 
     w->in_frame = 0;
-    w->prev_frame_tc_scaled = w->cluster_tc_scaled + delta;
 
     if( w->cluster->d_cur > CLSIZE )
         CHECK( mk_close_cluster( w ) );
@@ -508,19 +458,21 @@ int mk_start_frame( mk_writer *w )
     if( mk_flush_frame( w ) < 0 )
         return -1;
 
-    w->in_frame = 1;
-    w->keyframe = 0;
+    w->in_frame  = 1;
+    w->keyframe  = 0;
+    w->skippable = 0;
 
     return 0;
 }
 
-int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe )
+int mk_set_frame_flags( mk_writer *w, int64_t timestamp, int keyframe, int skippable )
 {
     if( !w->in_frame )
         return -1;
 
-    w->frame_tc = timestamp;
-    w->keyframe = keyframe != 0;
+    w->frame_tc  = timestamp;
+    w->keyframe  = keyframe  != 0;
+    w->skippable = skippable != 0;
 
     if( w->max_frame_tc < timestamp )
         w->max_frame_tc = timestamp;
@@ -540,15 +492,17 @@ int mk_add_frame_data( mk_writer *w, const void *data, unsigned size )
     return mk_append_context_data( w->frame, data, size );
 }
 
-int mk_close( mk_writer *w )
+int mk_close( mk_writer *w, int64_t last_delta )
 {
     int ret = 0;
     if( mk_flush_frame( w ) < 0 || mk_close_cluster( w ) < 0 )
         ret = -1;
-    if( w->wrote_header )
+    if( w->wrote_header && x264_is_regular_file( w->fp ) )
     {
         fseek( w->fp, w->duration_ptr, SEEK_SET );
-        if( mk_write_float_raw( w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale) ) < 0 ||
+        int64_t last_frametime = w->def_duration ? w->def_duration : last_delta;
+        int64_t total_duration = w->max_frame_tc+last_frametime;
+        if( mk_write_float_raw( w->root, (float)((double)total_duration / w->timescale) ) < 0 ||
             mk_flush_context_data( w->root ) < 0 )
             ret = -1;
     }