From 34d65ecae59d4d47e129940f46c6525b16624e8a Mon Sep 17 00:00:00 2001
From: Anton Mitrofanov <BugMaster@narod.ru>
Date: Sun, 23 Oct 2011 16:18:39 +0400
Subject: [PATCH] Use L-SMASH for mp4 muxing

---
 Makefile              |    7 +-
 configure             |   31 +-
 output/mp4.c          |  509 ++-
 output/mp4/box.h      | 2114 ++++++++++
 output/mp4/importer.c | 4358 +++++++++++++++++++++
 output/mp4/importer.h |   48 +
 output/mp4/internal.h |   37 +
 output/mp4/isom.c     |10206 +++++++++++++++++++++++++++++++++++++++++++++++++
 output/mp4/lsmash.h   | 1292 +++++++
 output/mp4/mp4a.c     |  901 +++++
 output/mp4/mp4a.h     |  130 +
 output/mp4/mp4sys.c   |  977 +++++
 output/mp4/mp4sys.h   |  205 +
 output/mp4/summary.c  |  121 +
 output/mp4/utils.c    |  728 ++++
 output/mp4/utils.h    |  177 +
 x264.c                |   20 +-
 17 files changed, 21602 insertions(+), 259 deletions(-)
 create mode 100644 output/mp4/box.h
 create mode 100644 output/mp4/importer.c
 create mode 100644 output/mp4/importer.h
 create mode 100644 output/mp4/internal.h
 create mode 100644 output/mp4/isom.c
 create mode 100644 output/mp4/lsmash.h
 create mode 100644 output/mp4/mp4a.c
 create mode 100644 output/mp4/mp4a.h
 create mode 100644 output/mp4/mp4sys.c
 create mode 100644 output/mp4/mp4sys.h
 create mode 100644 output/mp4/summary.c
 create mode 100644 output/mp4/utils.c
 create mode 100644 output/mp4/utils.h

diff --git a/Makefile b/Makefile
index 560ba90..0fd1a19 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,9 @@ SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \
          filters/video/resize.c filters/video/cache.c filters/video/fix_vfr_pts.c \
          filters/video/select_every.c filters/video/crop.c filters/video/depth.c
 
+SRCCLI += output/mp4.c
+SRCCLI += $(addprefix output/mp4/, isom.c utils.c mp4sys.c mp4a.c summary.c importer.c)
+
 SRCSO =
 
 CONFIG := $(shell cat config.h)
@@ -51,10 +54,6 @@ ifneq ($(findstring HAVE_FFMS 1, $(CONFIG)),)
 SRCCLI += input/ffms.c
 endif
 
-ifneq ($(findstring HAVE_GPAC 1, $(CONFIG)),)
-SRCCLI += output/mp4.c
-endif
-
 # Visualization sources
 ifneq ($(findstring HAVE_VISUALIZE 1, $(CONFIG)),)
 SRCS   += common/visualize.c common/display-x11.c
diff --git a/configure b/configure
index de6afd9..5d0e092 100755
--- a/configure
+++ b/configure
@@ -49,7 +49,6 @@ External library support:
   --disable-swscale        disable swscale support
   --disable-lavf           disable libavformat support
   --disable-ffms           disable ffmpegsource support
-  --disable-gpac           disable gpac support
 
 EOF
 exit 1
@@ -104,7 +103,6 @@ icl_ldflags() {
 
         arg=${arg/pthreadGC/pthreadVC}
         [ "$arg" = avifil32.lib ] && arg=vfw32.lib
-        [ "$arg" = gpac_static.lib ] && arg=libgpac_static.lib
 
         [ -n "$arg" ] && echo -n "$arg "
     done
@@ -226,7 +224,6 @@ static="no"
 avs="auto"
 lavf="auto"
 ffms="auto"
-gpac="auto"
 gpl="yes"
 thread="auto"
 swscale="auto"
@@ -251,7 +248,7 @@ cross_prefix=""
 EXE=""
 
 # list of all preprocessor HAVE values we can define
-CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS GPAC GF_MALLOC AVS GPL VECTOREXT INTERLACED CPU_COUNT"
+CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS AVS GPL VECTOREXT INTERLACED CPU_COUNT"
 
 # parse options
 
@@ -300,9 +297,6 @@ for opt do
         --disable-ffms)
             ffms="no"
             ;;
-        --disable-gpac)
-            gpac="no"
-            ;;
         --disable-gpl)
             gpl="no"
             ;;
@@ -838,28 +832,6 @@ if [ "$swscale" = "yes" ]; then
     fi
 fi
 
-if [ "$gpac" = "auto" ] ; then
-    gpac="no"
-    cc_check "" -lz && GPAC_LIBS="-lgpac_static -lz" || GPAC_LIBS="-lgpac_static"
-    if [ "$SYS" = "WINDOWS" ] ; then
-        GPAC_LIBS="$GPAC_LIBS -lwinmm"
-    fi
-    if cc_check gpac/isomedia.h "$GPAC_LIBS" ; then
-        if cc_check gpac/isomedia.h "$GPAC_LIBS" "gf_isom_set_pixel_aspect_ratio(0,0,0,0,0);" ; then
-            gpac="yes"
-        else
-            echo "Warning: gpac is too old, update to 2007-06-21 UTC or later"
-        fi
-    fi
-fi
-if [ "$gpac" = "yes" ] ; then
-    define HAVE_GPAC
-    if cc_check gpac/isomedia.h "-Werror $GPAC_LIBS" "gf_malloc(1); gf_free(NULL);" ; then
-        define HAVE_GF_MALLOC
-    fi
-    LDFLAGSCLI="$GPAC_LIBS $LDFLAGSCLI"
-fi
-
 if [ "$avs" = "auto" ] ; then
     avs="no"
     # cygwin can use avisynth if it can use LoadLibrary
@@ -1121,7 +1093,6 @@ interlaced:    $interlaced
 avs:           $avs
 lavf:          $lavf
 ffms:          $ffms
-gpac:          $gpac
 gpl:           $gpl
 thread:        $thread
 filters:       $filters
diff --git a/output/mp4.c b/output/mp4.c
index c0fb685..ac04d6c 100644
--- a/output/mp4.c
+++ b/output/mp4.c
@@ -1,10 +1,13 @@
 /*****************************************************************************
- * mp4.c: mp4 muxer
+ * mp4.c: mp4 muxer using L-SMASH
  *****************************************************************************
  * Copyright (C) 2003-2011 x264 project
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Loren Merritt <lorenm@u.washington.edu>
+ *          Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *          Takashi Hirata <silverfilain@gmail.com>
+ *          golgol7777 <golgol7777@gmail.com>
  *
  * 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
@@ -25,88 +28,84 @@
  *****************************************************************************/
 
 #include "output.h"
-#include <gpac/isomedia.h>
+#include "mp4/lsmash.h"
+#include "mp4/importer.h"
+
+/*******************/
+
+#define MP4_LOG_ERROR( ... )                x264_cli_log( "mp4", X264_LOG_ERROR, __VA_ARGS__ )
+#define MP4_LOG_WARNING( ... )              x264_cli_log( "mp4", X264_LOG_WARNING, __VA_ARGS__ )
+#define MP4_LOG_INFO( ... )                 x264_cli_log( "mp4", X264_LOG_INFO, __VA_ARGS__ )
+//#define MP4_RETURN_IF_ERR( cond, ret, ... ) RETURN_IF_ERR( cond, "mp4", ret, __VA_ARGS__ )
+#define MP4_FAIL_IF_ERR( cond, ... )        FAIL_IF_ERR( cond, "mp4", __VA_ARGS__ )
+
+/* For close_file() */
+#define MP4_LOG_IF_ERR( cond, ... )\
+if( cond )\
+{\
+    MP4_LOG_ERROR( __VA_ARGS__ );\
+}
+
+/* For open_file() */
+#define MP4_FAIL_IF_ERR_EX( cond, ... )\
+if( cond )\
+{\
+    remove_mp4_hnd( p_mp4 );\
+    MP4_LOG_ERROR( __VA_ARGS__ );\
+    return -1;\
+}
 
-#if HAVE_GF_MALLOC
-#undef malloc
-#undef free
-#undef realloc
-#define malloc gf_malloc
-#define free gf_free
-#define realloc gf_realloc
-#endif
+/*******************/
 
 typedef struct
 {
-    GF_ISOFile *p_file;
-    GF_AVCConfig *p_config;
-    GF_ISOSample *p_sample;
-    int i_track;
-    uint32_t i_descidx;
-    uint64_t i_time_res;
-    int64_t i_time_inc;
-    int64_t i_delay_time;
-    int64_t i_init_delta;
+    lsmash_root_t *p_root;
+    lsmash_brand_type major_brand;
+    int i_brand_3gpp;
+    int b_brand_qt;
+    int b_stdout;
+    uint32_t i_movie_timescale;
+    uint32_t i_video_timescale;
+    uint32_t i_track;
+    uint32_t i_sample_entry;
+    uint64_t i_time_inc;
+    int64_t i_start_offset;
+    uint64_t i_first_cts;
+    uint64_t i_prev_dts;
+    uint32_t i_sei_size;
+    uint8_t *p_sei_buffer;
     int i_numframe;
+    int64_t i_init_delta;
     int i_delay_frames;
     int b_dts_compress;
     int i_dts_compress_multiplier;
-    int i_data_size;
+    int b_use_recovery;
+    int b_no_pasp;
+    int b_fragments;
 } mp4_hnd_t;
 
-static void recompute_bitrate_mp4( GF_ISOFile *p_file, int i_track )
-{
-    u32 count, di, timescale, time_wnd, rate;
-    u64 offset;
-    Double br;
-    GF_ESD *esd;
+/*******************/
 
-    esd = gf_isom_get_esd( p_file, i_track, 1 );
-    if( !esd )
+static void remove_mp4_hnd( hnd_t handle )
+{
+    mp4_hnd_t *p_mp4 = handle;
+    if( !p_mp4 )
         return;
-
-    esd->decoderConfig->avgBitrate = 0;
-    esd->decoderConfig->maxBitrate = 0;
-    rate = time_wnd = 0;
-
-    timescale = gf_isom_get_media_timescale( p_file, i_track );
-    count = gf_isom_get_sample_count( p_file, i_track );
-    for( u32 i = 0; i < count; i++ )
+    if( p_mp4->p_sei_buffer )
     {
-        GF_ISOSample *samp = gf_isom_get_sample_info( p_file, i_track, i+1, &di, &offset );
-        if( !samp )
-        {
-            x264_cli_log( "mp4", X264_LOG_ERROR, "failure reading back frame %u\n", i );
-            break;
-        }
-
-        if( esd->decoderConfig->bufferSizeDB < samp->dataLength )
-            esd->decoderConfig->bufferSizeDB = samp->dataLength;
-
-        esd->decoderConfig->avgBitrate += samp->dataLength;
-        rate += samp->dataLength;
-        if( samp->DTS > time_wnd + timescale )
-        {
-            if( rate > esd->decoderConfig->maxBitrate )
-                esd->decoderConfig->maxBitrate = rate;
-            time_wnd = samp->DTS;
-            rate = 0;
-        }
-
-        gf_isom_sample_del( &samp );
+        free( p_mp4->p_sei_buffer );
+        p_mp4->p_sei_buffer = NULL;
     }
-
-    br = (Double)(s64)gf_isom_get_media_duration( p_file, i_track );
-    br /= timescale;
-    esd->decoderConfig->avgBitrate = (u32)(esd->decoderConfig->avgBitrate / br);
-    /*move to bps*/
-    esd->decoderConfig->avgBitrate *= 8;
-    esd->decoderConfig->maxBitrate *= 8;
-
-    gf_isom_change_mpeg4_description( p_file, i_track, 1, esd );
-    gf_odf_desc_del( (GF_Descriptor*)esd );
+    if( p_mp4->p_root )
+    {
+        lsmash_destroy_root( p_mp4->p_root );
+        p_mp4->p_root = NULL;
+    }
+    free( p_mp4 );
 }
 
+/*******************/
+
 static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts )
 {
     mp4_hnd_t *p_mp4 = handle;
@@ -114,56 +113,47 @@ static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest
     if( !p_mp4 )
         return 0;
 
-    if( p_mp4->p_config )
-        gf_odf_avc_cfg_del( p_mp4->p_config );
-
-    if( p_mp4->p_sample )
-    {
-        if( p_mp4->p_sample->data )
-            free( p_mp4->p_sample->data );
-
-        p_mp4->p_sample->dataLength = 0;
-        gf_isom_sample_del( &p_mp4->p_sample );
-    }
-
-    if( p_mp4->p_file )
+    if( p_mp4->p_root )
     {
+        double actual_duration = 0;     /* FIXME: This may be inside block of "if( p_mp4->i_track )" if audio does not use this. */
         if( p_mp4->i_track )
         {
-            /* The mdhd duration is defined as CTS[final] - CTS[0] + duration of last frame.
-             * The mdhd duration (in seconds) should be able to be longer than the tkhd duration since the track is managed by edts.
-             * So, if mdhd duration is equal to the last DTS or less, we give the last composition time delta to the last sample duration.
-             * And then, the mdhd duration is updated, but it time-wise doesn't give the actual duration.
-             * The tkhd duration is the actual track duration. */
-            uint64_t mdhd_duration = (2 * largest_pts - second_largest_pts) * p_mp4->i_time_inc;
-            if( mdhd_duration != gf_isom_get_media_duration( p_mp4->p_file, p_mp4->i_track ) )
-            {
-                uint64_t last_dts = gf_isom_get_sample_dts( p_mp4->p_file, p_mp4->i_track, p_mp4->i_numframe );
-                uint32_t last_duration = (uint32_t)( mdhd_duration > last_dts ? mdhd_duration - last_dts : (largest_pts - second_largest_pts) * p_mp4->i_time_inc );
-                gf_isom_set_last_sample_duration( p_mp4->p_file, p_mp4->i_track, last_duration );
-            }
+            /* Flush the rest of samples and add the last sample_delta. */
+            uint32_t last_delta = largest_pts - second_largest_pts;
+            MP4_LOG_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, (last_delta ? last_delta : 1) * p_mp4->i_time_inc ),
+                            "failed to flush the rest of samples.\n" );
+
+            if( p_mp4->i_movie_timescale != 0 && p_mp4->i_video_timescale != 0 )    /* avoid zero division */
+                actual_duration = ((double)((largest_pts + last_delta) * p_mp4->i_time_inc) / p_mp4->i_video_timescale) * p_mp4->i_movie_timescale;
+            else
+                MP4_LOG_ERROR( "timescale is broken.\n" );
 
-            /* Write an Edit Box if the first CTS offset is positive.
-             * A media_time is given by not the mvhd timescale but rather the mdhd timescale.
-             * The reason is that an Edit Box maps the presentation time-line to the media time-line.
-             * Any demuxers should follow the Edit Box if it exists. */
-            GF_ISOSample *sample = gf_isom_get_sample_info( p_mp4->p_file, p_mp4->i_track, 1, NULL, NULL );
-            if( sample && sample->CTS_Offset > 0 )
+            if( !p_mp4->b_fragments )
             {
-                uint32_t mvhd_timescale = gf_isom_get_timescale( p_mp4->p_file );
-                uint64_t tkhd_duration = (uint64_t)( mdhd_duration * ( (double)mvhd_timescale / p_mp4->i_time_res ) );
-                gf_isom_append_edit_segment( p_mp4->p_file, p_mp4->i_track, tkhd_duration, sample->CTS_Offset, GF_ISOM_EDIT_NORMAL );
+                /*
+                 * Declare the explicit time-line mapping.
+                 * A segment_duration is given by movie timescale, while a media_time that is the start time of this segment
+                 * is given by not the movie timescale but rather the media timescale.
+                 * The reason is that ISO media have two time-lines, presentation and media time-line,
+                 * and an edit maps the presentation time-line to the media time-line.
+                 * According to QuickTime file format specification and the actual playback in QuickTime Player,
+                 * if the Edit Box doesn't exist in the track, the ratio of the summation of sample durations and track's duration becomes
+                 * the track's media_rate so that the entire media can be used by the track.
+                 * So, we add Edit Box here to avoid this implicit media_rate could distort track's presentation timestamps slightly.
+                 * Note: Any demuxers should follow the Edit List Box if it exists.
+                 */
+                MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, actual_duration, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ),
+                                "failed to set timeline map for video.\n" );
             }
-            gf_isom_sample_del( &sample );
-
-            recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track );
+            else if( !p_mp4->b_stdout )
+                MP4_LOG_IF_ERR( lsmash_modify_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, 1, actual_duration, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ),
+                                "failed to update timeline map for video.\n" );
         }
-        gf_isom_set_pl_indication( p_mp4->p_file, GF_ISOM_PL_VISUAL, 0x15 );
-        gf_isom_set_storage_mode( p_mp4->p_file, GF_ISOM_STORE_FLAT );
-        gf_isom_close( p_mp4->p_file );
+
+        MP4_LOG_IF_ERR( lsmash_finish_movie( p_mp4->p_root, NULL ), "failed to finish movie.\n" );
     }
 
-    free( p_mp4 );
+    remove_mp4_hnd( p_mp4 ); /* including lsmash_destroy_root( p_mp4->p_root ); */
 
     return 0;
 }
@@ -173,27 +163,48 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt
     mp4_hnd_t *p_mp4;
 
     *p_handle = NULL;
-    FILE *fh = fopen( psz_filename, "w" );
-    if( !fh )
-        return -1;
-    FAIL_IF_ERR( !x264_is_regular_file( fh ), "mp4", "MP4 output is incompatible with non-regular file `%s'\n", psz_filename )
-    fclose( fh );
 
-    if( !(p_mp4 = malloc( sizeof(mp4_hnd_t) )) )
-        return -1;
+    int b_regular = strcmp( psz_filename, "-" );
+    b_regular = b_regular && x264_is_regular_file_path( psz_filename );
+    if( b_regular )
+    {
+        FILE *fh = fopen( psz_filename, "wb" );
+        MP4_FAIL_IF_ERR( !fh, "cannot open output file `%s'.\n", psz_filename );
+        b_regular = x264_is_regular_file( fh );
+        fclose( fh );
+    }
 
+    p_mp4 = malloc( sizeof(mp4_hnd_t) );
+    MP4_FAIL_IF_ERR( !p_mp4, "failed to allocate memory for muxer information.\n" );
     memset( p_mp4, 0, sizeof(mp4_hnd_t) );
-    p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL );
 
     p_mp4->b_dts_compress = opt->use_dts_compress;
+    p_mp4->b_use_recovery = 0;
+    p_mp4->b_no_pasp = 0;
+    p_mp4->b_fragments = !b_regular;
+    p_mp4->b_stdout = !strcmp( psz_filename, "-" );
 
-    if( !(p_mp4->p_sample = gf_isom_sample_new()) )
+    char* ext = get_filename_extension( psz_filename );
+    if( !strcmp( ext, "mov" ) || !strcmp( ext, "qt" ) )
+    {
+        p_mp4->major_brand = ISOM_BRAND_TYPE_QT;
+        p_mp4->b_brand_qt = 1;
+    }
+    else if( !strcmp( ext, "3gp" ) )
     {
-        close_file( p_mp4, 0, 0 );
-        return -1;
+        p_mp4->major_brand = ISOM_BRAND_TYPE_3GP6;
+        p_mp4->i_brand_3gpp = 1;
     }
+    else if( !strcmp( ext, "3g2" ) )
+    {
+        p_mp4->major_brand = ISOM_BRAND_TYPE_3G2A;
+        p_mp4->i_brand_3gpp = 2;
+    }
+    else
+        p_mp4->major_brand = ISOM_BRAND_TYPE_MP42;
 
-    gf_isom_set_brand_info( p_mp4->p_file, GF_ISOM_BRAND_AVC1, 0 );
+    p_mp4->p_root = lsmash_open_movie( psz_filename, p_mp4->b_fragments ? LSMASH_FILE_MODE_WRITE_FRAGMENTED : LSMASH_FILE_MODE_WRITE );
+    MP4_FAIL_IF_ERR_EX( !p_mp4->p_root, "failed to create root.\n" );
 
     *p_handle = p_mp4;
 
@@ -203,111 +214,161 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt
 static int set_param( hnd_t handle, x264_param_t *p_param )
 {
     mp4_hnd_t *p_mp4 = handle;
+    uint64_t i_media_timescale;
 
     p_mp4->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
     p_mp4->i_dts_compress_multiplier = p_mp4->b_dts_compress * p_mp4->i_delay_frames + 1;
 
-    p_mp4->i_time_res = p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier;
+    i_media_timescale = p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier;
     p_mp4->i_time_inc = p_param->i_timebase_num * p_mp4->i_dts_compress_multiplier;
-    FAIL_IF_ERR( p_mp4->i_time_res > UINT32_MAX, "mp4", "MP4 media timescale %"PRIu64" exceeds maximum\n", p_mp4->i_time_res )
-
-    p_mp4->i_track = gf_isom_new_track( p_mp4->p_file, 0, GF_ISOM_MEDIA_VISUAL,
-                                        p_mp4->i_time_res );
-
-    p_mp4->p_config = gf_odf_avc_cfg_new();
-    gf_isom_avc_config_new( p_mp4->p_file, p_mp4->i_track, p_mp4->p_config,
-                            NULL, NULL, &p_mp4->i_descidx );
+    MP4_FAIL_IF_ERR( i_media_timescale > UINT32_MAX, "MP4 media timescale %"PRIu64" exceeds maximum\n", i_media_timescale );
 
-    gf_isom_set_track_enabled( p_mp4->p_file, p_mp4->i_track, 1 );
-
-    gf_isom_set_visual_info( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx,
-                             p_param->i_width, p_param->i_height );
+    /* Select brands. */
+    lsmash_brand_type brands[10] = { 0 };
+    uint32_t minor_version = 0;
+    uint32_t brand_count = 0;
+    if( p_mp4->b_brand_qt )
+    {
+        brands[brand_count++] = ISOM_BRAND_TYPE_QT;
+        p_mp4->i_brand_3gpp = 0;
+        p_mp4->b_use_recovery = 0;      /* Disable sample grouping. */
+    }
+    else
+    {
+        if( p_mp4->i_brand_3gpp >= 1 )
+            brands[brand_count++] = ISOM_BRAND_TYPE_3GP6;
+        if( p_mp4->i_brand_3gpp == 2 )
+        {
+            brands[brand_count++] = ISOM_BRAND_TYPE_3G2A;
+            minor_version = 0x00010000;
+        }
+        brands[brand_count++] = ISOM_BRAND_TYPE_MP42;
+        brands[brand_count++] = ISOM_BRAND_TYPE_MP41;
+        brands[brand_count++] = ISOM_BRAND_TYPE_ISOM;
+        if( p_mp4->b_use_recovery )
+        {
+            brands[brand_count++] = ISOM_BRAND_TYPE_AVC1;   /* sdtp/sgpd/sbgp/random access recovery point grouping */
+            if( p_param->b_open_gop )
+            {
+                brands[brand_count++] = ISOM_BRAND_TYPE_ISO6;   /* cslg/random access point grouping */
+                brands[brand_count++] = ISOM_BRAND_TYPE_QT;     /* tapt/cslg/stps/sdtp */
+                p_mp4->b_brand_qt = 1;
+            }
+        }
+    }
 
+    /* Set movie parameters. */
+    lsmash_movie_parameters_t movie_param;
+    lsmash_initialize_movie_parameters( &movie_param );
+    movie_param.major_brand = p_mp4->major_brand;
+    movie_param.brands = brands;
+    movie_param.number_of_brands = brand_count;
+    movie_param.minor_version = minor_version;
+    MP4_FAIL_IF_ERR( lsmash_set_movie_parameters( p_mp4->p_root, &movie_param ),
+                     "failed to set movie parameters.\n" );
+    p_mp4->i_movie_timescale = lsmash_get_movie_timescale( p_mp4->p_root );
+    MP4_FAIL_IF_ERR( !p_mp4->i_movie_timescale, "movie timescale is broken.\n" );
+
+    /* Create a video track. */
+    p_mp4->i_track = lsmash_create_track( p_mp4->p_root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK );
+    MP4_FAIL_IF_ERR( !p_mp4->i_track, "failed to create a video track.\n" );
+
+    lsmash_video_summary_t summary;
+    memset( &summary, 0, sizeof(lsmash_video_summary_t) );
+    summary.width = p_param->i_width;
+    summary.height = p_param->i_height;
+    uint32_t i_display_width = p_param->i_width << 16;
+    uint32_t i_display_height = p_param->i_height << 16;
     if( p_param->vui.i_sar_width && p_param->vui.i_sar_height )
     {
-        uint64_t dw = p_param->i_width << 16;
-        uint64_t dh = p_param->i_height << 16;
         double sar = (double)p_param->vui.i_sar_width / p_param->vui.i_sar_height;
         if( sar > 1.0 )
-            dw *= sar ;
+            i_display_width *= sar;
         else
-            dh /= sar;
-        gf_isom_set_pixel_aspect_ratio( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_param->vui.i_sar_width, p_param->vui.i_sar_height );
-        gf_isom_set_track_layout_info( p_mp4->p_file, p_mp4->i_track, dw, dh, 0, 0, 0 );
+            i_display_height /= sar;
+        if( !p_mp4->b_no_pasp )
+        {
+            summary.par_h = p_param->vui.i_sar_width;
+            summary.par_v = p_param->vui.i_sar_height;
+            if( p_mp4->major_brand != ISOM_BRAND_TYPE_QT )
+                summary.scaling_method = ISOM_SCALING_METHOD_MEET;
+        }
     }
-
-    p_mp4->i_data_size = p_param->i_width * p_param->i_height * 3 / 2;
-    p_mp4->p_sample->data = malloc( p_mp4->i_data_size );
-    if( !p_mp4->p_sample->data )
+    if( p_mp4->b_brand_qt )
     {
-        p_mp4->i_data_size = 0;
-        return -1;
+        summary.primaries = p_param->vui.i_colorprim;
+        summary.transfer = p_param->vui.i_transfer;
+        summary.matrix = p_param->vui.i_colmatrix;
     }
 
-    return 0;
-}
-
-static int check_buffer( mp4_hnd_t *p_mp4, int needed_size )
-{
-    if( needed_size > p_mp4->i_data_size )
+    /* Set video track parameters. */
+    lsmash_track_parameters_t track_param;
+    lsmash_initialize_track_parameters( &track_param );
+    lsmash_track_mode track_mode = ISOM_TRACK_ENABLED | ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
+    if( p_mp4->b_brand_qt )
+        track_mode |= QT_TRACK_IN_POSTER;
+    track_param.mode = track_mode;
+    track_param.display_width = i_display_width;
+    track_param.display_height = i_display_height;
+    track_param.aperture_modes = p_mp4->b_brand_qt && !p_mp4->b_no_pasp;
+    MP4_FAIL_IF_ERR( lsmash_set_track_parameters( p_mp4->p_root, p_mp4->i_track, &track_param ),
+                     "failed to set track parameters for video.\n" );
+
+    /* Set video media parameters. */
+    lsmash_media_parameters_t media_param;
+    lsmash_initialize_media_parameters( &media_param );
+    media_param.timescale = i_media_timescale;
+    media_param.media_handler_name = "L-SMASH Video Media Handler";
+    if( p_mp4->b_brand_qt )
+        media_param.data_handler_name = "L-SMASH URL Data Handler";
+    if( p_mp4->b_use_recovery )
     {
-        void *ptr = realloc( p_mp4->p_sample->data, needed_size );
-        if( !ptr )
-            return -1;
-        p_mp4->p_sample->data = ptr;
-        p_mp4->i_data_size = needed_size;
+        media_param.roll_grouping = p_param->b_intra_refresh;
+        media_param.rap_grouping = p_param->b_open_gop;
     }
+    MP4_FAIL_IF_ERR( lsmash_set_media_parameters( p_mp4->p_root, p_mp4->i_track, &media_param ),
+                     "failed to set media parameters for video.\n" );
+    p_mp4->i_video_timescale = lsmash_get_media_timescale( p_mp4->p_root, p_mp4->i_track );
+    MP4_FAIL_IF_ERR( !p_mp4->i_video_timescale, "media timescale for video is broken.\n" );
+
+    /* Add a sample entry. */
+    p_mp4->i_sample_entry = lsmash_add_sample_entry( p_mp4->p_root, p_mp4->i_track, ISOM_CODEC_TYPE_AVC1_VIDEO, &summary );
+    MP4_FAIL_IF_ERR( !p_mp4->i_sample_entry,
+                     "failed to add sample entry for video.\n" );
+
+    if( p_mp4->major_brand != ISOM_BRAND_TYPE_QT )
+        MP4_FAIL_IF_ERR( lsmash_add_btrt( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry ),
+                         "failed to add btrt.\n" );
+
     return 0;
 }
 
 static int write_headers( hnd_t handle, x264_nal_t *p_nal )
 {
     mp4_hnd_t *p_mp4 = handle;
-    GF_AVCConfigSlot *p_slot;
 
-    int sps_size = p_nal[0].i_payload - 4;
-    int pps_size = p_nal[1].i_payload - 4;
-    int sei_size = p_nal[2].i_payload;
+    uint32_t sps_size = p_nal[0].i_payload - 4;
+    uint32_t pps_size = p_nal[1].i_payload - 4;
+    uint32_t sei_size = p_nal[2].i_payload;
 
     uint8_t *sps = p_nal[0].p_payload + 4;
     uint8_t *pps = p_nal[1].p_payload + 4;
     uint8_t *sei = p_nal[2].p_payload;
 
-    // SPS
-
-    p_mp4->p_config->configurationVersion = 1;
-    p_mp4->p_config->AVCProfileIndication = sps[1];
-    p_mp4->p_config->profile_compatibility = sps[2];
-    p_mp4->p_config->AVCLevelIndication = sps[3];
-    p_slot = malloc( sizeof(GF_AVCConfigSlot) );
-    if( !p_slot )
-        return -1;
-    p_slot->size = sps_size;
-    p_slot->data = malloc( p_slot->size );
-    if( !p_slot->data )
-        return -1;
-    memcpy( p_slot->data, sps, sps_size );
-    gf_list_add( p_mp4->p_config->sequenceParameterSets, p_slot );
-
-    // PPS
-
-    p_slot = malloc( sizeof(GF_AVCConfigSlot) );
-    if( !p_slot )
-        return -1;
-    p_slot->size = pps_size;
-    p_slot->data = malloc( p_slot->size );
-    if( !p_slot->data )
-        return -1;
-    memcpy( p_slot->data, pps, pps_size );
-    gf_list_add( p_mp4->p_config->pictureParameterSets, p_slot );
-    gf_isom_avc_config_update( p_mp4->p_file, p_mp4->i_track, 1, p_mp4->p_config );
-
-    // SEI
-
-    if( check_buffer( p_mp4, p_mp4->p_sample->dataLength + sei_size ) )
-        return -1;
-    memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, sei, sei_size );
-    p_mp4->p_sample->dataLength += sei_size;
+    MP4_FAIL_IF_ERR( lsmash_set_avc_config( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, 1, sps[1], sps[2], sps[3], 3, 1, X264_BIT_DEPTH-8, X264_BIT_DEPTH-8 ),
+                     "failed to set avc config.\n" );
+    /* SPS */
+    MP4_FAIL_IF_ERR( lsmash_add_sps_entry( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, sps, sps_size ),
+                     "failed to add sps.\n" );
+    /* PPS */
+    MP4_FAIL_IF_ERR( lsmash_add_pps_entry( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, pps, pps_size ),
+                     "failed to add pps.\n" );
+    /* SEI */
+    p_mp4->p_sei_buffer = malloc( sei_size );
+    MP4_FAIL_IF_ERR( !p_mp4->p_sei_buffer,
+                     "failed to allocate sei transition buffer.\n" );
+    memcpy( p_mp4->p_sei_buffer, sei, sei_size );
+    p_mp4->i_sei_size = sei_size;
 
     return sei_size + sps_size + pps_size;
 }
@@ -315,21 +376,35 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal )
 static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture )
 {
     mp4_hnd_t *p_mp4 = handle;
-    int64_t dts;
-    int64_t cts;
-
-    if( check_buffer( p_mp4, p_mp4->p_sample->dataLength + i_size ) )
-        return -1;
-    memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, p_nalu, i_size );
-    p_mp4->p_sample->dataLength += i_size;
+    uint64_t dts, cts;
 
     if( !p_mp4->i_numframe )
-        p_mp4->i_delay_time = p_picture->i_dts * -1;
+    {
+        p_mp4->i_start_offset = p_picture->i_dts * -1;
+        p_mp4->i_first_cts = p_mp4->b_dts_compress ? 0 : p_mp4->i_start_offset * p_mp4->i_time_inc;
+        if( p_mp4->b_fragments )
+            MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, UINT32_MAX, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ),
+                            "failed to set timeline map for video.\n" );
+    }
+
+    lsmash_sample_t *p_sample = lsmash_create_sample( i_size + p_mp4->i_sei_size );
+    MP4_FAIL_IF_ERR( !p_sample,
+                     "failed to create a video sample data.\n" );
+
+    if( p_mp4->p_sei_buffer )
+    {
+        memcpy( p_sample->data, p_mp4->p_sei_buffer, p_mp4->i_sei_size );
+        free( p_mp4->p_sei_buffer );
+        p_mp4->p_sei_buffer = NULL;
+    }
+
+    memcpy( p_sample->data + p_mp4->i_sei_size, p_nalu, i_size );
+    p_mp4->i_sei_size = 0;
 
     if( p_mp4->b_dts_compress )
     {
         if( p_mp4->i_numframe == 1 )
-            p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+            p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
         dts = p_mp4->i_numframe > p_mp4->i_delay_frames
             ? p_picture->i_dts * p_mp4->i_time_inc
             : p_mp4->i_numframe * (p_mp4->i_init_delta / p_mp4->i_dts_compress_multiplier);
@@ -337,16 +412,28 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_
     }
     else
     {
-        dts = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
-        cts = (p_picture->i_pts + p_mp4->i_delay_time) * p_mp4->i_time_inc;
+        dts = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
+        cts = (p_picture->i_pts + p_mp4->i_start_offset) * p_mp4->i_time_inc;
+    }
+
+    p_sample->dts = dts;
+    p_sample->cts = cts;
+    p_sample->index = p_mp4->i_sample_entry;
+    p_sample->prop.random_access_type = p_picture->b_keyframe ? ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP : ISOM_SAMPLE_RANDOM_ACCESS_TYPE_NONE;
+
+    if( p_mp4->b_fragments && p_mp4->i_numframe && p_sample->prop.random_access_type )
+    {
+        MP4_FAIL_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, p_sample->dts - p_mp4->i_prev_dts ),
+                         "failed to flush the rest of samples.\n" );
+        MP4_FAIL_IF_ERR( lsmash_create_fragment_movie( p_mp4->p_root ),
+                         "failed to create a movie fragment.\n" );
     }
 
-    p_mp4->p_sample->IsRAP = p_picture->b_keyframe;
-    p_mp4->p_sample->DTS = dts;
-    p_mp4->p_sample->CTS_Offset = (uint32_t)(cts - dts);
-    gf_isom_add_sample( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_mp4->p_sample );
+    /* Append data per sample. */
+    MP4_FAIL_IF_ERR( lsmash_append_sample( p_mp4->p_root, p_mp4->i_track, p_sample ),
+                     "failed to append a video frame.\n" );
 
-    p_mp4->p_sample->dataLength = 0;
+    p_mp4->i_prev_dts = dts;
     p_mp4->i_numframe++;
 
     return i_size;
diff --git a/output/mp4/box.h b/output/mp4/box.h
new file mode 100644
index 0000000..3a475a3
--- /dev/null
+++ b/output/mp4/box.h
@@ -0,0 +1,2114 @@
+/*****************************************************************************
+ * box.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef LSMASH_BOX_H
+#define LSMASH_BOX_H
+
+/* For generating creation_time and modification_time.
+ * According to ISO/IEC-14496-5-2001, the difference between Unix time and Mac OS time is 2082758400.
+ * However this is wrong and 2082844800 is correct. */
+#include <time.h>
+#define ISOM_MAC_EPOCH_OFFSET 2082844800
+
+#include "utils.h"
+
+typedef struct isom_box_tag isom_box_t;
+
+/* If size is 1, then largesize is actual size.
+ * If size is 0, then this box is the last one in the file.
+ * usertype is for uuid. */
+#define ISOM_BASEBOX_COMMON \
+        lsmash_root_t *root;    /* pointer of root */ \
+        isom_box_t *parent;     /* pointer of the parent box of this box */ \
+        uint8_t  manager;       /* flags for L-SMASH */ \
+        uint64_t pos;           /* starting position of this box in the file */ \
+    uint64_t size;              /* the number of bytes in this box */ \
+    uint32_t type;              /* four characters codes that identify box type */ \
+    uint8_t  *usertype
+
+#define ISOM_FULLBOX_COMMON \
+    ISOM_BASEBOX_COMMON; \
+    uint8_t  version;   /* Basically, version is either 0 or 1 */ \
+    uint32_t flags      /* In the actual structure of box, flags is 24 bits. */
+
+#define ISOM_BASEBOX_COMMON_SIZE       8
+#define ISOM_FULLBOX_COMMON_SIZE      12
+#define ISOM_LIST_FULLBOX_COMMON_SIZE 16
+
+#define LSMASH_UNKNOWN_BOX    0x01
+#define LSMASH_ABSENT_IN_ROOT 0x02
+#define LSMASH_QTFF_BASE      0x04
+
+struct isom_box_tag
+{
+    ISOM_FULLBOX_COMMON;
+};
+
+/* File Type Box
+ * This box identifies the specifications to which this file complies.
+ * This box shall occur before any variable-length box.
+ * In the absence of this box, the file is QuickTime file format or MP4 version 1 file format.
+ * In MP4 version 1 file format, Object Descriptor Box is mandatory.
+ * In QuickTime file format, Object Descriptor Box isn't defined.
+ * Therefore, if this box and an Object Descriptor Box are absent in the file, the file shall be QuickTime file format. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t major_brand;           /* brand identifier */
+    uint32_t minor_version;         /* the minor version of the major brand */
+    uint32_t *compatible_brands;    /* a list, to the end of the box, of brands */
+
+        uint32_t brand_count;       /* the number of factors in compatible_brands array */
+} isom_ftyp_t;
+
+/* Track Header Box
+ * This box specifies the characteristics of a single track. */
+typedef struct
+{
+    /* version is either 0 or 1
+     * flags
+     *      0x000001: Indicates that the track is enabled.
+     *                A disabled track is treated as if it were not present.
+     *      0x000002: Indicates that the track is used in the presentation.
+     *      0x000004: Indicates that the track is used when previewing the presentation.
+     *      0x000008: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format)
+     * ISOM: If in a presentation all tracks have neither track_in_movie nor track_in_preview set,
+     *       then all tracks shall be treated as if both flags were set on all tracks. */
+    ISOM_FULLBOX_COMMON;
+    /* version == 0: uint64_t -> uint32_t */
+    uint64_t creation_time;         /* the creation time of this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint64_t modification_time;     /* the most recent time the track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint32_t track_ID;              /* an integer that uniquely identifies the track
+                                     * Track IDs are never re-used and cannot be zero. */
+    uint32_t reserved1;
+    uint64_t duration;              /* the duration of this track expressed in the movie timescale units */
+    /* The following fields are treated as
+     * ISOM: template fields.
+     * MP41: reserved fields.
+     * MP42: ignored fileds since compositions are done using BIFS system.
+     * 3GPP: ignored fields except for alternate_group.
+     * QTFF: usable fields. */
+    uint32_t reserved2[2];
+    int16_t  layer;                 /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */
+    int16_t  alternate_group;       /* an integer that specifies a group or collection of tracks
+                                     * If this field is not 0, it should be the same for tracks that contain alternate data for one another
+                                     * and different for tracks belonging to different such groups.
+                                     * Only one track within an alternate group should be played or streamed at any one time. */
+    int16_t  volume;                /* fixed point 8.8 number. 0x0100 is full volume. */
+    uint16_t reserved3;
+    int32_t  matrix[9];             /* transformation matrix for the video */
+    /* track's visual presentation size
+     * All images in the sequence are scaled to this size, before any overall transformation of the track represented by the matrix.
+     * Note: these fields are treated as reserved in MP4 version 1. */
+    uint32_t width;                 /* fixed point 16.16 number */
+    uint32_t height;                /* fixed point 16.16 number */
+    /* */
+} isom_tkhd_t;
+
+/* Track Clean Aperture Dimensions Box
+ * A presentation mode where clap and pasp are reflected. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t width;     /* fixed point 16.16 number */
+    uint32_t height;    /* fixed point 16.16 number */
+} isom_clef_t;
+
+/* Track Production Aperture Dimensions Box
+ * A presentation mode where pasp is reflected. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t width;     /* fixed point 16.16 number */
+    uint32_t height;    /* fixed point 16.16 number */
+} isom_prof_t;
+
+/* Track Encoded Pixels Dimensions Box
+ * A presentation mode where clap and pasp are not reflected. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t width;     /* fixed point 16.16 number */
+    uint32_t height;    /* fixed point 16.16 number */
+} isom_enof_t;
+
+/* Track Aperture Mode Dimensions Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_clef_t *clef;      /* Track Clean Aperture Dimensions Box */
+    isom_prof_t *prof;      /* Track Production Aperture Dimensions Box */
+    isom_enof_t *enof;      /* Track Encoded Pixels Dimensions Box */
+} isom_tapt_t;
+
+/* Edit List Box
+ * This box contains an explicit timeline map.
+ * Each entry defines part of the track timeline: by mapping part of the media timeline, or by indicating 'empty' time, 
+ * or by defining a 'dwell', where a single time-point in the media is held for a period.
+ * The last edit in a track shall never be an empty edit.
+ * Any difference between the duration in the Movie Header Box, and the track's duration is expressed as an implicit empty edit at the end.
+ * It is recommended that any edits, explicit or implied, not select any portion of the composition timeline that doesn't map to a sample.
+ * Therefore, if the first sample in the track has non-zero CTS, then this track should have at least one edit and the start time in it should
+ * correspond to the value of the CTS the first sample has or more not to exceed the largest CTS in this track. */
+typedef struct
+{
+    /* version == 0: 64bits -> 32bits */
+    uint64_t segment_duration;  /* the duration of this edit expressed in the movie timescale units */
+    int64_t  media_time;        /* the starting composition time within the media of this edit segment
+                                 * If this field is set to -1, it is an empty edit. */
+    int32_t  media_rate;        /* the relative rate at which to play the media corresponding to this edit segment
+                                 * If this value is 0, then the edit is specifying a 'dwell':
+                                 * the media at media_time is presented for the segment_duration.
+                                 * This field is expressed as 16.16 fixed-point number. */
+} isom_elst_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;        /* version is either 0 or 1 */
+    lsmash_entry_list_t *list;
+} isom_elst_t;
+
+/* Edit Box
+ * This optional box maps the presentation time-line to the media time-line as it is stored in the file.
+ * In the absence of this box, there is an implicit one-to-one mapping of these time-lines,
+ * and the presentation of a track starts at the beginning of the presentation. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_elst_t *elst;     /* Edit List Box */
+} isom_edts_t;
+
+/* Track Reference Box
+ * The Track Reference Box contains Track Reference Type Boxes.
+ * Track Reference Type Boxes define relationships between tracks.
+ * They allow one track to specify how it is related to other tracks. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t *track_ID;         /* track_IDs of reference tracks / Zero value must not be used */
+
+        uint32_t ref_count;     /* number of reference tracks */
+} isom_tref_type_t;
+
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    lsmash_entry_list_t *ref_list;      /* Track Reference Type Boxes */
+} isom_tref_t;
+
+/* Media Header Box
+ * This box declares overall information that is media-independent, and relevant to characteristics of the media in a track.*/
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;            /* version is either 0 or 1 */
+    /* version == 0: uint64_t -> uint32_t */
+    uint64_t creation_time;         /* the creation time of the media in this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint64_t modification_time;     /* the most recent time the media in this track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint32_t timescale;             /* media timescale: timescale for this media */
+    uint64_t duration;              /* the duration of this media expressed in the timescale indicated in this box */
+    /* */
+    uint16_t language;              /* ISOM: ISO-639-2/T language codes. Most significant 1-bit is 0.
+                                     *       Each character is packed as the difference between its ASCII value and 0x60.
+                                     * QTFF: Macintosh language codes is usually used.
+                                     *       Mac's value is less than 0x800 while ISO's value is 0x800 or greater. */
+    int16_t quality;                /* ISOM: pre_defined / QTFF: the media's playback quality */
+} isom_mdhd_t;
+
+/* Handler Reference Box
+ * In Media Box, this box is mandatory and (ISOM: should/QTFF: must) come before Media Information Box.
+ * ISOM: this box might be also in Meta Box.
+ * QTFF: this box might be also in Media Information Box. If this box is present there, it must come before Data Information Box. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t componentType;             /* ISOM: pre_difined = 0
+                                         * QTFF: 'mhlr' for Media Handler Reference Box and 'dhlr' for Data Handler Reference Box  */
+    uint32_t componentSubtype;          /* Both ISOM and QT: when present in Media Handler Reference Box, this field defines the type of media data.
+                                         * ISOM: when present in Metadata Handler Reference Box, this field defines the format of the meta box contents.
+                                         * QTFF: when present in Data Handler Reference Box, this field defines the data reference type. */
+    /* The following fields are defined in QTFF however these fields aren't mentioned in QuickTime SDK and are reserved in the specification.
+     * In ISOM, these fields are still defined as reserved. */
+    uint32_t componentManufacturer;     /* vendor indentification / A value of 0 matches any manufacturer. */
+    uint32_t componentFlags;            /* flags describing required component capabilities
+                                         * The high-order 8 bits should be set to 0.
+                                         * The low-order 24 bits are specific to each component type. */
+    uint32_t componentFlagsMask;        /* This field indicates which flags in the componentFlags field are relevant to this operation. */
+    /* */
+    uint8_t *componentName;             /* ISOM: a null-terminated string in UTF-8 characters
+                                         * QTFF: Pascal string */
+
+        uint32_t componentName_length;
+} isom_hdlr_t;
+
+
+/** Media Information Header Boxes
+ ** There is a different media information header for each track type
+ ** (corresponding to the media handler-type); the matching header shall be present. **/
+/* Video Media Header Box
+ * This box contains general presentation information, independent of the coding, for video media. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;        /* flags is 1 */
+    uint16_t graphicsmode;      /* template: graphicsmode = 0 */
+    uint16_t opcolor[3];        /* template: opcolor = { 0, 0, 0 } */
+} isom_vmhd_t;
+
+/* Sound Media Header Box
+ * This box contains general presentation information, independent of the coding, for audio media. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    int16_t balance;        /* a fixed-point 8.8 number that places mono audio tracks in a stereo space. template: balance = 0 */
+    uint16_t reserved;
+} isom_smhd_t;
+
+/* Hint Media Header Box
+ * This box contains general information, independent of the protocol, for hint tracks. (A PDU is a Protocol Data Unit.) */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint16_t maxPDUsize;        /* the size in bytes of the largest PDU in this (hint) stream */
+    uint16_t avgPDUsize;        /* the average size of a PDU over the entire presentation */
+    uint32_t maxbitrate;        /* the maximum rate in bits/second over any window of one second */
+    uint32_t avgbitrate;        /* the average rate in bits/second over the entire presentation */
+    uint32_t reserved;
+} isom_hmhd_t;
+
+/* Null Media Header Box
+ * This box may be used for streams other than visual and audio (e.g., timed metadata streams). */
+typedef struct
+{
+    /* Streams other than visual and audio may use a Null Media Header Box */
+    ISOM_FULLBOX_COMMON;    /* flags is currently all zero */
+} isom_nmhd_t;
+
+/* Generic Media Information Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint16_t graphicsmode;
+    uint16_t opcolor[3];
+    int16_t balance;        /* This field is nomally set to 0. */
+    uint16_t reserved;      /* Reserved for use by Apple. Set this field to 0. */
+} isom_gmin_t;
+
+/* Text Media Information Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    int32_t matrix[9];      /* Unkown fields. Default values are probably:
+                             * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */
+} isom_text_t;
+
+/* Generic Media Information Header Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_gmin_t *gmin;      /* Generic Media Information Box */
+    isom_text_t *text;      /* Text Media Information Box */
+} isom_gmhd_t;
+/** **/
+
+/* Data Reference Box
+ * name and location fields are expressed in null-terminated string using UTF-8 characters. */
+typedef struct
+{
+    /* This box is DataEntryUrlBox or DataEntryUrnBox */
+    ISOM_FULLBOX_COMMON;    /* flags == 0x000001 means that the media data is in the same file
+                             * as the Movie Box containing this data reference. */
+    char *name;             /* only for DataEntryUrnBox */
+    char *location;         /* a location to find the resource with the given name */
+
+    uint32_t name_length;
+    uint32_t location_length;
+} isom_dref_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_dref_t;
+
+/* Data Information Box */
+typedef struct
+{
+    /* This box is in Media Information Box or Meta Box */
+    ISOM_BASEBOX_COMMON;
+    isom_dref_t *dref;      /* Data Reference Box */
+} isom_dinf_t;
+
+/** Sample Description **/
+/* ES Descriptor Box */
+struct mp4sys_ES_Descriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    struct mp4sys_ES_Descriptor_t *ES;
+} isom_esds_t;
+
+/* AVCDecoderConfigurationRecord */
+typedef struct
+{
+#define ISOM_REQUIRES_AVCC_EXTENSION( x ) ((x) == 100 || (x) == 110 || (x) == 122 || (x) == 144)
+    ISOM_BASEBOX_COMMON;
+    uint8_t configurationVersion;                   /* 1 */
+    uint8_t AVCProfileIndication;                   /* profile_idc in SPS */
+    uint8_t profile_compatibility;
+    uint8_t AVCLevelIndication;                     /* level_idc in SPS */
+    uint8_t lengthSizeMinusOne;                     /* in bytes of the NALUnitLength field. upper 6-bits are reserved as 111111b */
+    uint8_t numOfSequenceParameterSets;             /* upper 3-bits are reserved as 111b */
+    lsmash_entry_list_t *sequenceParameterSets;     /* SPSs */
+    uint8_t numOfPictureParameterSets;
+    lsmash_entry_list_t *pictureParameterSets;      /* PPSs */
+    /* if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) ) */
+    uint8_t chroma_format;                          /* chroma_format_idc in SPS / upper 6-bits are reserved as 111111b */
+    uint8_t bit_depth_luma_minus8;                  /* shall be in the range of 0 to 4 / upper 5-bits are reserved as 11111b */
+    uint8_t bit_depth_chroma_minus8;                /* shall be in the range of 0 to 4 / upper 5-bits are reserved as 11111b */
+    uint8_t numOfSequenceParameterSetExt;
+    lsmash_entry_list_t *sequenceParameterSetExt;   /* SPSExts */
+    /* */
+} isom_avcC_t;
+
+/* Parameter Set Entry */
+typedef struct
+{
+    uint16_t parameterSetLength;
+    uint8_t *parameterSetNALUnit;
+} isom_avcC_ps_entry_t;
+
+/* MPEG-4 Bit Rate Box
+ * This box signals the bit rate information of the AVC video stream. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t bufferSizeDB;  /* the size of the decoding buffer for the elementary stream in bytes */
+    uint32_t maxBitrate;    /* the maximum rate in bits/second over any window of one second */
+    uint32_t avgBitrate;    /* the average rate in bits/second over the entire presentation */
+} isom_btrt_t;
+
+/* Clean Aperture Box
+ * There are notionally four values in this box and these parameters are represented as a fraction N/D.
+ * Here, we refer to the pair of parameters fooN and fooD as foo.
+ * Considering the pixel dimensions as defined by the VisualSampleEntry width and height.
+ * If picture centre of the image is at pcX and pcY, then horizOff and vertOff are defined as follows:
+ *  pcX = horizOff + (width - 1)/2;
+ *  pcY = vertOff + (height - 1)/2;
+ * The leftmost/rightmost pixel and the topmost/bottommost line of the clean aperture fall at:
+ *  pcX +/- (cleanApertureWidth - 1)/2;
+ *  pcY +/- (cleanApertureHeight - 1)/2; */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t cleanApertureWidthN;
+    uint32_t cleanApertureWidthD;
+    uint32_t cleanApertureHeightN;
+    uint32_t cleanApertureHeightD;
+    int32_t  horizOffN;
+    uint32_t horizOffD;
+    int32_t  vertOffN;
+    uint32_t vertOffD;
+} isom_clap_t;
+
+/* Pixel Aspect Ratio Box
+ * This box specifies the aspect ratio of a pixel, in arbitrary units.
+ * If a pixel appears H wide and V tall, then hSpacing/vSpacing is equal to H/V.
+ * When adjusting pixel aspect ratio, normally, the horizontal dimension of the video is scaled, if needed. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t hSpacing;      /* horizontal spacing */
+    uint32_t vSpacing;      /* vertical spacing */
+} isom_pasp_t;
+
+/* Color Parameter Box
+ * This box is used to map the numerical values of pixels in the file to a common representation of color
+ * in which images can be correctly compared, combined, and displayed.
+ * This box is defined in QuickTime file format. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t color_parameter_type;          /* 'nclc' or 'prof' */
+    /* for 'nclc' */
+    uint16_t primaries_index;               /* CIE 1931 xy chromaticity coordinates */
+    uint16_t transfer_function_index;       /* nonlinear transfer function from RGB to ErEgEb */
+    uint16_t matrix_index;                  /* matrix from ErEgEb to EyEcbEcr */
+} isom_colr_t;
+
+/* Sample Scale Box
+ * If this box is present and can be interpreted by the decoder,
+ * all samples shall be displayed according to the scaling behaviour that is specified in this box.
+ * Otherwise, all samples are scaled to the size that is indicated by the width and height field in the Track Header Box. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint8_t constraint_flag;    /* Upper 7-bits are reserved.
+                                 * If this flag is set, all samples described by this sample entry shall be scaled
+                                 * according to the method specified by the field 'scale_method'. */
+    uint8_t scale_method;       /* The semantics of the values for scale_method are as specified for the 'fit' attribute of regions in SMIL 1.0. */
+    int16_t display_center_x;
+    int16_t display_center_y;
+} isom_stsl_t;
+
+/* Sample Entry */
+#define ISOM_SAMPLE_ENTRY \
+    ISOM_BASEBOX_COMMON; \
+    uint8_t reserved[6]; \
+    uint16_t data_reference_index;
+
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+} isom_sample_entry_t;
+
+/* Mpeg Sample Entry */
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+    isom_esds_t *esds;      /* ES Descriptor Box */
+} isom_mp4s_entry_t;
+
+/* ISOM: Visual Sample Entry / QTFF: Image Description */
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+    int16_t  version;           /* ISOM: pre_defined / QTFF: sample description version */
+    int16_t  revision_level;    /* ISOM: reserved / QTFF: version of the CODEC */
+    int32_t  vendor;            /* ISOM: pre_defined / QTFF: whose CODEC */
+    uint32_t temporalQuality;   /* ISOM: pre_defined / QTFF: the temporal quality factor */
+    uint32_t spatialQuality;    /* ISOM: pre_defined / QTFF: the spatial quality factor */
+    /* The width and height are the maximum pixel counts that the codec will deliver.
+     * Since these are counts they do not take into account pixel aspect ratio. */
+    uint16_t width;
+    uint16_t height;
+    /* */
+    uint32_t horizresolution;   /* 16.16 fixed-point / template: horizresolution = 0x00480000 / 72 dpi */
+    uint32_t vertresolution;    /* 16.16 fixed-point / template: vertresolution = 0x00480000 / 72 dpi */
+    uint32_t dataSize;          /* ISOM: reserved / QTFF: if known, the size of data for this descriptor */
+    uint16_t frame_count;       /* frame per sample / template: frame_count = 1 */
+    char compressorname[33];    /* a fixed 32-byte field, with the first byte set to the number of bytes to be displayed */
+    uint16_t depth;             /* ISOM: template: depth = 0x0018
+                                 * AVC : 0x0018: colour with no alpha
+                                 *       0x0028: grayscale with no alpha
+                                 *       0x0020: gray or colour with alpha
+                                 * QTFF: depth of this data (1-32) or (33-40 grayscale) */
+    int16_t color_table_ID;     /* ISOM: template: pre_defined = -1
+                                 * QTFF: color table ID
+                                 *       If this field is set to 0, the default color table should be used for the specified depth
+                                 *       If the color table ID is set to 0, a color table is contained within the sample description itself.
+                                 *       The color table immediately follows the color table ID field. */
+    /* AVC specific extensions */
+    isom_avcC_t *avcC;          /* AVCDecoderConfigurationRecord */
+    isom_btrt_t *btrt;          /* MPEG-4 Bit Rate Box @ optional */
+    /* MP4 specific extension */
+    isom_esds_t *esds;          /* ES Descriptor Box */
+    /* QuickTime specific extension */
+    isom_colr_t *colr;          /* Color Parameter Box @ optional */
+    /* ISO Base Media extension */
+    isom_stsl_t *stsl;          /* Sample Scale Box @ optional */
+    /* common extensions
+     * For maximum compatibility, these boxes should follow, not precede, any boxes defined in or required by derived specifications. */
+    isom_clap_t *clap;          /* Clean Aperture Box @ optional */
+    isom_pasp_t *pasp;          /* Pixel Aspect Ratio Box @ optional */
+
+        uint32_t exdata_length;
+        void *exdata;
+} isom_visual_entry_t;
+
+/* Format Box
+ * This box shows the data format of the stored sound media.
+ * ISO base media file format also defines the same four-character-code for the type field,
+ * however, that is used to indicate original sample description of the media when a protected sample entry is used. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t data_format;       /* copy of sample description type */
+} isom_frma_t;
+
+/* Audio Endian Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    int16_t littleEndian;
+} isom_enda_t;
+
+/* MPEG-4 Audio Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t unknown;           /* always 0? */
+} isom_mp4a_t;
+
+/* Terminator Box
+ * This box is present to indicate the end of the sound description. It contains no data. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;    /* size = 8, type = 0x00000000 */
+} isom_terminator_t;
+
+/* Sound Information Decompression Parameters Box
+ * This box provides the ability to store data specific to a given audio decompressor in the sound description.
+ * The contents of this box are dependent on the audio decompressor. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_frma_t       *frma;            /* Format Box */
+    isom_enda_t       *enda;            /* Audio Endian Box */
+    isom_mp4a_t       *mp4a;            /* MPEG-4 Audio Box */
+    isom_esds_t       *esds;            /* ES Descriptor Box */
+    isom_terminator_t *terminator;      /* Terminator Box */
+
+        uint32_t exdata_length;
+        void *exdata;
+} isom_wave_t;
+
+/* Channel Compositor Box */
+typedef struct
+{
+    uint32_t channelLabel;          /* the channelLabel that describes the channel */
+    uint32_t channelFlags;          /* flags that control the interpretation of coordinates */
+    uint32_t coordinates[3];        /* an ordered triple that specifies a precise speaker location / 32-bit floating point */
+} isom_channel_description_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t channelLayoutTag;              /* the channelLayoutTag indicates the layout */
+    uint32_t channelBitmap;                 /* If channelLayoutTag is set to 0x00010000, this field is the channel usage bitmap. */
+    uint32_t numberChannelDescriptions;     /* the number of items in the Channel Descriptions array */
+    /* Channel Descriptions array */
+    isom_channel_description_t *channelDescriptions;
+} isom_chan_t;
+
+/* ISOM: Audio Sample Entry / QTFF: Sound Description */
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+    int16_t  version;           /* ISOM: reserved
+                                 * QTFF: sample description version
+                                 *       version = 0 supports only 'raw ' or 'twos' audio format.
+                                 *       version = 1 is used to support out-of-band configuration settings for decompression.
+                                 *       version = 2 is used to support high samplerate or 3 or more multichannel audio. */
+    int16_t  revision_level;    /* ISOM: reserved / QTFF: version of the CODEC */
+    int32_t  vendor;            /* ISOM: reserved / QTFF: whose CODEC */
+    uint16_t channelcount;      /* ISOM: template: channelcount = 2
+                                 * QTFF: the number of audio channels
+                                 *       Allowable values are 1 (mono) or 2 (stereo).
+                                 *       For more than 2, set this field to 3 and use numAudioChannels instead of this field. */
+    uint16_t samplesize;        /* ISOM: template: samplesize = 16
+                                 * QTFF: the number of bits in each uncompressed sample for a single channel
+                                 *       Allowable values are 8 or 16.
+                                 *       For non-mod8, set this field to 16 and use constBitsPerChannel instead of this field.
+                                 *       For more than 16, set this field to 16 and use bytesPerPacket instead of this field. */
+    int16_t  compression_ID;    /* ISOM: pre_defined
+                                 * QTFF: version = 0 -> must be set to 0.
+                                 *       version = 2 -> must be set to -2. */
+    uint16_t packet_size;       /* ISOM: reserved / QTFF: must be set to 0. */
+    uint32_t samplerate;        /* the sampling rate expressed as a 16.16 fixed-point number
+                                 * ISOM: template: samplerate = {default samplerate of media}<<16
+                                 * QTFF: the integer portion should match the media's timescale.
+                                 *       If this field is invalid because of higher samplerate,
+                                 *       then set this field to 0x00010000 and use audioSampleRate instead of this field. */
+    /* version 1 fields
+     * These fields are for description of the compression ratio of fixed ratio audio compression algorithms.
+     * If these fields are not used, they are set to 0. */
+    uint32_t samplesPerPacket;      /* For compressed audio, be set to the number of uncompressed frames generated by a compressed frame.
+                                     * For uncompressed audio, shall be set to 1. */
+    uint32_t bytesPerPacket;        /* the number of bytes in a sample for a single channel */
+    uint32_t bytesPerFrame;         /* the number of bytes in a frame */
+    uint32_t bytesPerSample;        /* 8-bit audio: 1, other audio: 2 */
+    /* version 2 fields
+     * LPCMFrame: one sample from each channel.
+     * AudioPacket: For uncompressed audio, an AudioPacket is simply one LPCMFrame.
+     *              For compressed audio, an AudioPacket is the natural compressed access unit of that format. */
+    uint32_t sizeOfStructOnly;                  /* offset to extensions */
+    uint64_t audioSampleRate;                   /* 64-bit floating point */
+    uint32_t numAudioChannels;                  /* any channel assignment info will be in Channel Compositor Box. */
+    int32_t  always7F000000;                    /* always 0x7F000000 */
+    uint32_t constBitsPerChannel;               /* only set if constant (and only for uncompressed audio) */
+    uint32_t formatSpecificFlags;
+    uint32_t constBytesPerAudioPacket;          /* only set if constant */
+    uint32_t constLPCMFramesPerAudioPacket;     /* only set if constant */
+    /* extensions */
+    isom_esds_t *esds;      /* ISOM: ES Descriptor Box / QTFF: null */
+    isom_wave_t *wave;      /* ISOM: null / QTFF: Sound Information Decompression Parameters Box */
+    isom_chan_t *chan;      /* ISOM: null / QTFF: Channel Compositor Box @ optional */
+
+        uint32_t exdata_length;
+        void *exdata;
+        lsmash_audio_summary_t summary;
+} isom_audio_entry_t;
+
+/* Hint Sample Entry */
+#define ISOM_HINT_SAMPLE_ENTRY \
+    ISOM_SAMPLE_ENTRY; \
+    uint8_t *data;
+
+typedef struct
+{
+    ISOM_HINT_SAMPLE_ENTRY;
+    uint32_t data_length;
+} isom_hint_entry_t;
+
+/* Metadata Sample Entry */
+#define ISOM_METADATA_SAMPLE_ENTRY \
+    ISOM_SAMPLE_ENTRY;
+
+typedef struct
+{
+    ISOM_METADATA_SAMPLE_ENTRY;
+} isom_metadata_entry_t;
+
+/* QuickTime Text Sample Description */
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+    int32_t displayFlags;
+    int32_t textJustification;
+    uint16_t bgColor[3];            /* background RGB color */
+    /* defaultTextBox */
+    int16_t top;
+    int16_t left;
+    int16_t bottom;
+    int16_t right;
+    /* defaultStyle */
+    int32_t scrpStartChar;          /* starting character position */
+    int16_t scrpHeight;
+    int16_t scrpAscent;
+    int16_t scrpFont;
+    uint16_t scrpFace;              /* only first 8-bits are used */
+    int16_t scrpSize;
+    uint16_t scrpColor[3];          /* foreground RGB color */
+    /* defaultFontName is Pascal string */
+    uint8_t font_name_length;
+    char *font_name;
+} isom_text_entry_t;
+
+/* FontRecord */
+typedef struct
+{
+    uint16_t font_ID;
+    /* Pascal string */
+    uint8_t font_name_length;
+    char *font_name;
+} isom_font_record_t;
+
+/* Font Table Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    /* FontRecord
+     * entry_count is uint16_t. */
+    lsmash_entry_list_t *list;
+} isom_ftab_t;
+
+/* Timed Text Sample Entry */
+typedef struct
+{
+    ISOM_SAMPLE_ENTRY;
+    uint32_t displayFlags;
+    int8_t horizontal_justification;
+    int8_t vertical_justification;
+    uint8_t background_color_rgba[4];
+    /* BoxRecord default_text_box */
+    int16_t top;
+    int16_t left;
+    int16_t bottom;
+    int16_t right;
+    /* StyleRecord default_style */
+    uint16_t startChar;     /* always 0 */
+    uint16_t endChar;       /* always 0 */
+    uint16_t font_ID;
+    uint8_t face_style_flags;
+    uint8_t font_size;
+    uint8_t text_color_rgba[4];
+    /* Font Table Box font_table */
+    isom_ftab_t *ftab;
+} isom_tx3g_entry_t;
+
+/* Sample Description Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_stsd_t;
+/** **/
+
+/* Decoding Time to Sample Box
+ * This box contains a compact version of a table that allows indexing from decoding time to sample number.
+ * Each entry in the table gives the number of consecutive samples with the same time delta, and the delta of those samples.
+ * By adding the deltas a complete time-to-sample map may be built.
+ * All samples must have non-zero durations except for the last one.
+ * The sum of all deltas gives the media duration in the track (not mapped to the movie timescale, and not considering any edit list).
+ * DTS is an abbreviation of 'decoding time stamp'. */
+typedef struct
+{
+    uint32_t sample_count;      /* number of consecutive samples that have the given sample_delta */
+    uint32_t sample_delta;      /* DTS[0] = 0; DTS[n+1] = DTS[n] + sample_delta[n]; */
+} isom_stts_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_stts_t;
+
+/* Composition Time to Sample Box
+ * This box provides the offset between decoding time and composition time.
+ * CTS is an abbreviation of 'composition time stamp'.
+ * This box is optional and must only be present if DTS and CTS differ for any samples.
+ * ISOM: if version is set to 1, sample_offset is signed 32-bit integer.
+ * QTFF: sample_offset is always signed 32-bit integer. */
+typedef struct
+{
+    uint32_t sample_count;      /* number of consecutive samples that have the given sample_offset */
+    uint32_t sample_offset;     /* CTS[n] = DTS[n] + sample_offset[n]; */
+} isom_ctts_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_ctts_t;
+
+/* Composition to Decode Box (Composition Shift Least Greatest Box)
+ * This box may be used to relate the composition and decoding timelines,
+ * and deal with some of the ambiguities that signed composition offsets introduce. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    int32_t compositionToDTSShift;          /* If this value is added to the composition times (as calculated by the CTS offsets from the DTS),
+                                             * then for all samples, their CTS is guaranteed to be greater than or equal to their DTS,
+                                             * and the buffer model implied by the indicated profile/level will be honoured;
+                                             * if leastDecodeToDisplayDelta is positive or zero, this field can be 0;
+                                             * otherwise it should be at least (- leastDecodeToDisplayDelta). */
+    int32_t leastDecodeToDisplayDelta;      /* the smallest sample_offset in this track */
+    int32_t greatestDecodeToDisplayDelta;   /* the largest sample_offset in this track */
+    int32_t compositionStartTime;           /* the smallest CTS for any sample */
+    int32_t compositionEndTime;             /* the CTS plus the composition duration, of the sample with the largest CTS in this track */
+} isom_cslg_t;
+
+/* Sample Size Box
+ * This box contains the sample count and a table giving the size in bytes of each sample.
+ * The total number of samples in the media is always indicated in the sample_count.
+ * Note: a sample size of zero is not prohibited in general, but it must be valid and defined for the coding system,
+ *       as defined by the sample entry, that the sample belongs to. */
+typedef struct
+{
+    uint32_t entry_size;        /* the size of a sample */
+} isom_stsz_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t sample_size;           /* If this field is set to 0, then the samples have different sizes. */
+    uint32_t sample_count;          /* the number of samples in the track */
+    lsmash_entry_list_t *list;      /* available if sample_size == 0 */
+} isom_stsz_t;
+
+/* Sync Sample Box
+ * If this box is not present, every sample is a random access point.
+ * In AVC streams, this box cannot point non-IDR samples.
+ * The table is arranged in strictly increasing order of sample number. */
+typedef struct
+{
+    uint32_t sample_number;     /* the numbers of the samples that are random access points in the stream. */
+} isom_stss_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_stss_t;
+
+/* Partial Sync Sample Box
+ * Tip from QT engineering - Open-GOP intra frames need to be marked as "partial sync samples".
+ * Partial sync frames perform a partial reset of inter-frame dependencies;
+ * decoding two partial sync frames and the non-droppable difference frames between them is
+ * sufficient to prepare a decompressor for correctly decoding the difference frames that follow. */
+typedef struct
+{
+    uint32_t sample_number;     /* the numbers of the samples that are partial sync samples in the stream. */
+} isom_stps_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_stps_t;
+
+/* Independent and Disposable Samples Box */
+typedef struct
+{
+    unsigned is_leading            : 2;     /* ISOM: leading / QTFF: samples later in decode order may have earlier display times */
+    unsigned sample_depends_on     : 2;     /* independency */
+    unsigned sample_is_depended_on : 2;     /* disposable */
+    unsigned sample_has_redundancy : 2;     /* redundancy */
+} isom_sdtp_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    /* According to the specification, the size of the table, sample_count, doesn't exist in this box.
+     * Instead of this, it is taken from the sample_count in the stsz or the stz2 box. */
+    lsmash_entry_list_t *list;
+} isom_sdtp_t;
+
+/* Sample To Chunk Box
+ * This box can be used to find the chunk that contains a sample, its position, and the associated sample description.
+ * The table is compactly coded. Each entry gives the index of the first chunk of a run of chunks with the same characteristics.
+ * By subtracting one entry here from the previous one, you can compute how many chunks are in this run.
+ * You can convert this to a sample count by multiplying by the appropriate samples_per_chunk. */
+typedef struct
+{
+    uint32_t first_chunk;                   /* the index of the first chunk in this run of chunks that share the same samples_per_chunk and sample_description_index */
+    uint32_t samples_per_chunk;             /* the number of samples in each of these chunks */
+    uint32_t sample_description_index;      /* the index of the sample entry that describes the samples in this chunk */
+} isom_stsc_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_stsc_t;
+
+/* Chunk Offset Box
+ * chunk_offset is the offset of the start of a chunk into its containing media file.
+ * Offsets are file offsets, not the offset into any box within the file. */
+typedef struct
+{
+    uint32_t chunk_offset;
+} isom_stco_entry_t;
+
+typedef struct
+{
+    /* for large presentations */
+    uint64_t chunk_offset;
+} isom_co64_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;        /* type = 'stco': 32-bit chunk offsets / type = 'co64': 64-bit chunk offsets */
+    lsmash_entry_list_t *list;
+
+        uint8_t large_presentation;     /* Set 1 to this if 64-bit chunk-offset are needed. */
+} isom_stco_t;      /* share with co64 box */
+
+/* Sample Group Description Box
+ * This box gives information about the characteristics of sample groups. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;            /* Use of version 0 entries is deprecated. */
+    uint32_t grouping_type;         /* an integer that identifies the sbgp that is associated with this sample group description */
+    uint32_t default_length;        /* the length of every group entry (if the length is constant), or zero (if it is variable)
+                                     * This field is available only if version == 1. */
+    lsmash_entry_list_t *list;
+} isom_sgpd_entry_t;
+
+/* Random Access Entry
+ * Samples marked by this group must be random access points, and may also be sync points. */
+typedef struct
+{
+    /* grouping_type is 'rap ' */
+    uint32_t description_length;                /* This field is available only if version == 1 and default_length == 0. */
+    unsigned num_leading_samples_known : 1;     /* the value of one indicates that the number of leading samples is known for each sample in this group,
+                                                 * and the number is specified by num_leading_samples. */
+    unsigned num_leading_samples       : 7;     /* the number of leading samples for each sample in this group
+                                                 * Note: when num_leading_samples_known is equal to 0, this field should be ignored. */
+} isom_rap_entry_t;
+
+/* Roll Recovery Entry
+ * This grouping type is defined as that group of samples having the same roll distance. */
+typedef struct
+{
+    /* grouping_type is 'roll' */
+    uint32_t description_length;        /* This field is available only if version == 1 and default_length == 0. */
+    int16_t  roll_distance;             /* the number of samples that must be decoded in order for a sample to be decoded correctly
+                                         * A positive value indicates post-roll, and a negative value indicates pre-roll.
+                                         * The value zero must not be used. */
+} isom_roll_entry_t;
+
+/* Sample to Group Box
+ * This box is used to find the group that a sample belongs to and the associated description of that sample group. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t grouping_type;             /* Links it to its sample group description table with the same value for grouping type. */
+    uint32_t grouping_type_parameter;   /* an indication of the sub-type of the grouping
+                                         * This field is available only if version == 1. */
+    lsmash_entry_list_t *list;
+} isom_sbgp_entry_t;
+
+typedef struct
+{
+    uint32_t sample_count;                  /* the number of consecutive samples with the same sample group descriptor */
+    uint32_t group_description_index;       /* the index of the sample group entry which describes the samples in this group
+                                             * The index ranges from 1 to the number of sample group entries in the Sample Group Description Box,
+                                             * or takes the value 0 to indicate that this sample is a member of no group of this type. */
+} isom_group_assignment_entry_t;
+
+/* Sample Table Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_stsd_t *stsd;      /* Sample Description Box */
+    isom_stts_t *stts;      /* Decoding Time to Sample Box */
+    isom_ctts_t *ctts;      /* Composition Time to Sample Box */
+    isom_cslg_t *cslg;      /* ISOM: Composition to Decode Box / QTFF: Composition Shift Least Greatest Box */
+    isom_stss_t *stss;      /* Sync Sample Box */
+    isom_stps_t *stps;      /* ISOM: null / QTFF: Partial Sync Sample Box */
+    isom_sdtp_t *sdtp;      /* Independent and Disposable Samples Box */
+    isom_stsc_t *stsc;      /* Sample To Chunk Box */
+    isom_stsz_t *stsz;      /* Sample Size Box */
+    isom_stco_t *stco;      /* Chunk Offset Box */
+    lsmash_entry_list_t *sgpd_list;      /* Sample Group Description Boxes */
+    lsmash_entry_list_t *sbgp_list;      /* Sample To Group Boxes */
+} isom_stbl_t;
+
+/* Media Information Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    /* Media Information Header Boxes */
+    isom_vmhd_t *vmhd;      /* Video Media Header Box */
+    isom_smhd_t *smhd;      /* Sound Media Header Box */
+    isom_hmhd_t *hmhd;      /* ISOM: Hint Media Header Box / QTFF: null */
+    isom_nmhd_t *nmhd;      /* ISOM: Null Media Header Box / QTFF: null */
+    isom_gmhd_t *gmhd;      /* ISOM: null / QTFF: Generic Media Information Header Box */
+    /* */
+    isom_hdlr_t *hdlr;      /* ISOM: null / QTFF: Data Handler Reference Box
+                             * Note: this box must come before Data Information Box. */
+    isom_dinf_t *dinf;      /* Data Information Box */
+    isom_stbl_t *stbl;      /* Sample Table Box */
+} isom_minf_t;
+
+/* Media Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_mdhd_t *mdhd;      /* Media Header Box */
+    isom_hdlr_t *hdlr;      /* ISOM: Handler Reference Box / QTFF: Media Handler Reference Box
+                             * Note: this box must come before Media Information Box. */
+    isom_minf_t *minf;      /* Media Information Box */
+} isom_mdia_t;
+
+/* Movie Header Box
+ * This box defines overall information which is media-independent, and relevant to the entire presentation considered as a whole. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;            /* version is either 0 or 1 */
+    /* version == 0: uint64_t -> uint32_t */
+    uint64_t creation_time;         /* the creation time of the presentation (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint64_t modification_time;     /* the most recent time the presentation was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */
+    uint32_t timescale;             /* movie timescale: timescale for the entire presentation */
+    uint64_t duration;              /* the duration, expressed in movie timescale, of the longest track */
+    /* The following fields are treated as
+     * ISOM: template fields.
+     * MP41: reserved fields.
+     * MP42: ignored fileds since compositions are done using BIFS system.
+     * 3GPP: ignored fields.
+     * QTFF: usable fields. */
+    int32_t  rate;                  /* fixed point 16.16 number. 0x00010000 is normal forward playback. */
+    int16_t  volume;                /* fixed point 8.8 number. 0x0100 is full volume. */
+    int16_t  reserved;
+    int32_t  preferredLong[2];      /* ISOM: reserved / QTFF: unknown */
+    int32_t  matrix[9];             /* transformation matrix for the video */
+    /* The following fields are defined in QuickTime file format.
+     * In ISO Base Media file format, these fields are treated as pre_defined. */
+    int32_t  previewTime;           /* the time value in the movie at which the preview begins */
+    int32_t  previewDuration;       /* the duration of the movie preview in movie timescale units */
+    int32_t  posterTime;            /* the time value of the time of the movie poster */
+    int32_t  selectionTime;         /* the time value for the start time of the current selection */
+    int32_t  selectionDuration;     /* the duration of the current selection in movie timescale units */
+    int32_t  currentTime;           /* the time value for current time position within the movie */
+    /* */
+    uint32_t next_track_ID;         /* larger than the largest track-ID in use */
+} isom_mvhd_t;
+
+/* Object Descriptor Box
+ * Note that this box is mandatory under 14496-1:2001 (mp41) while not mandatory under 14496-14:2003 (mp42). */
+struct mp4sys_ObjectDescriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    struct mp4sys_ObjectDescriptor_t *OD;
+} isom_iods_t;
+
+/* Media Data Box
+ * This box contains the media data.
+ * A presentation may contain zero or more Media Data Boxes.*/
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;    /* If size is 0, then this box is the last box. */
+
+        uint64_t placeholder_pos;       /* placeholder position for largesize */
+} isom_mdat_t;
+
+/* Free Space Box
+ * The contents of a free-space box are irrelevant and may be ignored without affecting the presentation. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;    /* type is 'free' or 'skip' */
+    uint32_t length;
+    uint8_t *data;
+} isom_free_t;
+
+typedef isom_free_t isom_skip_t;
+
+/* Chapter List Box
+ * This box is NOT defined in the ISO/MPEG-4 specs. */
+typedef struct
+{
+    uint64_t start_time;    /* version = 0: expressed in movie timescale units
+                             * version = 1: expressed in 100 nanoseconds */
+    /* Chapter name is Pascal string */
+    uint8_t chapter_name_length;
+    char *chapter_name;
+} isom_chpl_entry_t;
+
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;            /* version = 0 is defined in F4V file format. */
+    uint8_t unknown;                /* only available under version = 1 */
+    lsmash_entry_list_t *list;      /* if version is set to 0, entry_count is uint8_t. */
+} isom_chpl_t;
+
+typedef struct
+{
+    char *chapter_name;
+    uint64_t start_time;
+} isom_chapter_entry_t;
+
+/* Metadata Item Keys Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    lsmash_entry_list_t *list;
+} isom_keys_t;
+
+typedef struct
+{
+    uint32_t key_size;          /* the size of the entire structure containing a key definition
+                                 * key_size = sizeof(key_size) + sizeof(key_namespace) + sizeof(key_value) */
+    uint32_t key_namespace;     /* a naming scheme used for metadata keys
+                                 * Location metadata keys, for example, use the 'mdta' key namespace. */
+    uint8_t *key_value;         /* the actual name of the metadata key
+                                 * Keys with the 'mdta' namespace use a reverse DNS naming convention. */
+} isom_keys_entry_t;
+
+/* Meaning Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint8_t *meaning_string;        /* to fill the box */
+
+        uint32_t meaning_string_length;
+} isom_mean_t;
+
+/* Name Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint8_t *name;      /* to fill the box */
+
+        uint32_t name_length;
+} isom_name_t;
+
+/* Data Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    /* type indicator */
+    uint16_t reserved;              /* always 0 */
+    uint8_t  type_set_identifier;   /* 0: type set of the common basic data types */
+    uint8_t  type_code;             /* type of data code */
+    /* */
+    uint32_t the_locale;            /* reserved to be 0 */
+    uint8_t *value;                 /* to fill the box */
+
+        uint32_t value_length;
+} isom_data_t;
+
+/* Metadata Item Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_mean_t *mean;      /* Meaning Box */
+    isom_name_t *name;      /* Name Box */
+    isom_data_t *data;      /* Data Box */
+} isom_metaitem_t;
+
+/* Metadata Item List Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    lsmash_entry_list_t *item_list;     /* Metadata Item Box List
+                                         * There is no entry_count field. */
+} isom_ilst_t;
+
+/* Meta Box */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;    /* ISOM: FullBox / QTFF: BaseBox */
+    isom_hdlr_t *hdlr;      /* Metadata Handler Reference Box */
+    isom_dinf_t *dinf;      /* ISOM: Data Information Box / QTFF: null */
+    isom_keys_t *keys;      /* ISOM: null / QTFF: Metadata Item Keys Box */
+    isom_ilst_t *ilst;      /* Metadata Item List Box only defined in Apple MPEG-4 and QTFF */
+} isom_meta_t;
+
+/* Window Location Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    /* default window location for movie */
+    uint16_t x;
+    uint16_t y;
+} isom_WLOC_t;
+
+/* Looping Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint32_t looping_mode;      /* 0 for none, 1 for looping, 2 for palindromic looping */
+} isom_LOOP_t;
+
+/* Play Selection Only Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint8_t selection_only;     /* whether only the selected area of the movie should be played */
+} isom_SelO_t;
+
+/* Play All Frames Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    uint8_t play_all_frames;    /* whether all frames of video should be played, regardless of timing */
+} isom_AllF_t;
+
+/* Copyright Box
+ * The Copyright box contains a copyright declaration which applies to the entire presentation,
+ * when contained within the Movie Box, or, when contained in a track, to that entire track.
+ * There may be multiple copyright boxes using different language codes. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint16_t language;              /* ISO-639-2/T language codes. Most significant 1-bit is 0.
+                                     * Each character is packed as the difference between its ASCII value and 0x60. */
+    uint8_t *notice;                /* a null-terminated string in either UTF-8 or UTF-16 characters, giving a copyright notice.
+                                     * If UTF-16 is used, the string shall start with the BYTE ORDER MARK (0xFEFF), to distinguish it from a UTF-8 string.
+                                     * This mark does not form part of the final string. */
+        uint32_t notice_length;
+} isom_cprt_t;
+
+/* User Data Box
+ * This box is a container box for informative user-data.
+ * This user data is formatted as a set of boxes with more specific box types, which declare more precisely their content.
+ * QTFF: for historical reasons, this box is optionally terminated by a 32-bit integer set to 0. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_chpl_t *chpl;      /* Chapter List Box */
+    isom_meta_t *meta;      /* Meta Box extended by Apple for iTunes movie */
+    /* QuickTime user data */
+    isom_WLOC_t *WLOC;      /* Window Location Box */
+    isom_LOOP_t *LOOP;      /* Looping Box */
+    isom_SelO_t *SelO;      /* Play Selection Only Box */
+    isom_AllF_t *AllF;      /* Play All Frames Box */
+    /* Copyright Box List */
+    lsmash_entry_list_t *cprt_list;     /* Copyright Boxes is defined in ISO Base Media and 3GPP file format */
+} isom_udta_t;
+
+/** Caches for handling tracks **/
+typedef struct
+{
+    uint64_t alloc;             /* total buffer size for the pool */
+    uint64_t size;              /* total size of samples in the pool */
+    uint32_t sample_count;      /* number of samples in the pool */
+    uint8_t *data;              /* actual data of samples in the pool */
+} isom_sample_pool_t;
+
+typedef struct
+{
+    uint32_t chunk_number;                  /* chunk number */
+    uint32_t sample_description_index;      /* sample description index */
+    uint64_t first_dts;                     /* the first DTS in chunk */
+    isom_sample_pool_t *pool;               /* samples pooled to interleave */
+} isom_chunk_t;
+
+typedef struct
+{
+    uint64_t dts;
+    uint64_t cts;
+    int32_t  ctd_shift;
+} isom_timestamp_t;
+
+typedef struct
+{
+    isom_group_assignment_entry_t *assignment;          /* the address corresponding to the entry in Sample to Group Box */
+    isom_group_assignment_entry_t *prev_assignment;     /* the address of the previous assignment */
+    isom_rap_entry_t              *random_access;       /* the address corresponding to the random access entry in Sample Group Description Box */
+    uint8_t                        is_prev_rap;         /* whether the previous sample is a random access point or not */
+} isom_rap_group_t;
+
+typedef struct
+{
+    isom_group_assignment_entry_t *assignment;      /* the address corresponding to the entry in Sample to Group Box */
+    uint32_t first_sample;                          /* the number of the first sample of the group */
+    uint32_t recovery_point;                        /* the identifier necessary for the recovery from its starting point to be completed */
+    uint8_t delimited;                              /* the flag if the sample_count is determined */
+    uint8_t described;                              /* the flag if the group description is determined */
+} isom_roll_group_t;
+
+typedef struct
+{
+    lsmash_entry_list_t *pool;        /* grouping pooled to delimit and describe */
+} isom_grouping_t;
+
+typedef struct
+{
+    uint8_t has_samples;
+    uint32_t traf_number;
+    uint32_t last_duration;     /* the last sample duration in this track fragment */
+    uint64_t largest_cts;       /* the largest CTS in this track fragments */
+} isom_fragment_t;
+
+typedef struct
+{
+    uint8_t all_sync;       /* if all samples are sync sample */
+    isom_chunk_t chunk;
+    isom_timestamp_t timestamp;
+    isom_grouping_t roll;
+    isom_rap_group_t *rap;
+    isom_fragment_t *fragment;
+} isom_cache_t;
+
+/** Movie Fragments Boxes **/
+/* Track Fragments Flags ('tf_flags') */
+typedef enum
+{
+    ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT               = 0x000001,    /* base_data_offset field exists. */
+    ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT       = 0x000002,    /* sample_description_index field exists. */
+    ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT        = 0x000008,    /* default_sample_duration field exists. */
+    ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT            = 0x000010,    /* default_sample_size field exists. */
+    ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT           = 0x000020,    /* default_sample_flags field exists. */
+    ISOM_TF_FLAGS_DURATION_IS_EMPTY                      = 0x010000,    /* There are no samples for this time interval. */
+} isom_tf_flags_code;
+
+/* Track Run Flags ('tr_flags') */
+typedef enum
+{
+    ISOM_TR_FLAGS_DATA_OFFSET_PRESENT                    = 0x000001,    /* data_offset field exists. */
+    ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT             = 0x000004,    /* first_sample_flags field exists. */
+    ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT                = 0x000100,    /* sample_duration field exists. */
+    ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT                    = 0x000200,    /* sample_size field exists. */
+    ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT                   = 0x000400,    /* sample_flags field exists. */
+    ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT = 0x000800,    /* sample_composition_time_offset field exists. */
+} isom_tr_flags_code;
+
+/* Sample Flags */
+typedef struct
+{
+    unsigned reserved                  : 4;
+    /* The definition of the following fields is quite the same as Independent and Disposable Samples Box. */
+    unsigned is_leading                : 2;
+    unsigned sample_depends_on         : 2;
+    unsigned sample_is_depended_on     : 2;
+    unsigned sample_has_redundancy     : 2;
+    /* */
+    unsigned sample_padding_value      : 3;     /* the number of bits at the end of this sample */
+    unsigned sample_is_non_sync_sample : 1;     /* 0 value means this sample is sync sample. */
+    uint16_t sample_degradation_priority;
+} isom_sample_flags_t;
+
+/* Movie Extends Header Box
+ * This box is omitted when used in live streaming.
+ * If this box is not present, the overall duration must be computed by examining each fragment. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    /* version == 0: uint64_t -> uint32_t */
+    uint64_t fragment_duration;     /* the duration of the longest track, in the timescale indicated in the Movie Header Box, including movie fragments. */
+} isom_mehd_t;
+
+/* Track Extends Box
+ * This box sets up default values used by the movie fragments. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t            track_ID;       /* identifier of the track; this shall be the track ID of a track in the Movie Box */
+    uint32_t            default_sample_description_index;
+    uint32_t            default_sample_duration;
+    uint32_t            default_sample_size;
+    isom_sample_flags_t default_sample_flags;
+} isom_trex_entry_t;
+
+/* Movie Extends Box
+ * This box warns readers that there might be Movie Fragment Boxes in this file. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_mehd_t         *mehd;          /* Movie Extends Header Box / omitted when used in live streaming */
+    lsmash_entry_list_t *trex_list;     /* Track Extends Box */
+
+        uint64_t placeholder_pos;       /* placeholder position for Movie Extends Header Box */ 
+} isom_mvex_t;
+
+/* Movie Fragment Header Box
+ * This box contains a sequence number, as a safety check.
+ * The sequence number 'usually' starts at 1 and must increase for each movie fragment in the file, in the order in which they occur. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t sequence_number;       /* the ordinal number of this fragment, in increasing order */
+} isom_mfhd_t;
+
+/* Track Fragment Header Box
+ * Each movie fragment can contain zero or more fragments for each track;
+ * and a track fragment can contain zero or more contiguous runs of samples.
+ * This box sets up information and defaults used for those runs of samples. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;                            /* flags field is used for 'tf_flags'. */
+    uint32_t            track_ID;
+    /* all the following are optional fields */
+    uint64_t            base_data_offset;           /* an explicit anchor for the data offsets in each track run
+                                                     * Offsets are file offsets as like as chunk_offset in Chunk Offset Box.
+                                                     * If not provided, the base_data_offset for the first track in the movie fragment is the position 
+                                                     * of the first byte of the enclosing Movie Fragment Box, and for second and subsequent track fragments,
+                                                     * the default is the end of the data defined by the preceding fragment.
+                                                     * To avoid the case this field might overflow, e.g. semi-permanent live streaming and broadcasting,
+                                                     * you shall not use this optional field. */
+    uint32_t            sample_description_index;   /* override default_sample_description_index in Track Extends Box */
+    uint32_t            default_sample_duration;    /* override default_sample_duration in Track Extends Box */
+    uint32_t            default_sample_size;        /* override default_sample_size in Track Extends Box */
+    isom_sample_flags_t default_sample_flags;       /* override default_sample_flags in Track Extends Box */
+} isom_tfhd_t;
+
+/* Track Fragment Run Box
+ * Within the Track Fragment Box, there are zero or more Track Fragment Run Boxes.
+ * If the duration-is-empty flag is set in the tf_flags, there are no track runs.
+ * A track run documents a contiguous set of samples for a track. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;                        /* flags field is used for 'tr_flags'. */
+    uint32_t            sample_count;           /* the number of samples being added in this run; also the number of rows in the following table */
+    /* The following are optional fields. */
+    int32_t             data_offset;            /* This value is added to the implicit or explicit data_offset established in the Track Fragment Header Box.
+                                                 * If this field is not present, then the data for this run starts immediately after the data of the previous run,
+                                                 * or at the base_data_offset defined by the Track Fragment Header Box if this is the first run in a track fragment. */
+    isom_sample_flags_t first_sample_flags;     /* a set of flags for the first sample only of this run */
+    lsmash_entry_list_t *optional;              /* all fields in this array are optional. */
+} isom_trun_entry_t;
+
+typedef struct
+{
+    /* If the following fields is present, each field overrides default value described in Track Fragment Header Box or Track Extends Box. */
+    uint32_t            sample_duration;                    /* override default_sample_duration */
+    uint32_t            sample_size;                        /* override default_sample_size */
+    isom_sample_flags_t sample_flags;                       /* override default_sample_flags */
+    /* */
+    uint32_t            sample_composition_time_offset;     /* composition time offset */
+} isom_trun_optional_row_t;
+
+/* Track Fragment Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_tfhd_t         *tfhd;          /* Track Fragment Header Box */
+    lsmash_entry_list_t *trun_list;     /* Track Fragment Run Box List
+                                         * If the duration-is-empty flag is set in the tf_flags, there are no track runs. */
+
+        isom_cache_t *cache;
+} isom_traf_entry_t;
+
+/* Movie Fragment Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_mfhd_t         *mfhd;          /* Movie Fragment Header Box */
+    lsmash_entry_list_t *traf_list;     /* Track Fragment Box List */
+} isom_moof_entry_t;
+
+/* Track Fragment Random Access Box
+ * Each entry in this box contains the location and the presentation time of the random accessible sample.
+ * Note that not every random accessible sample in the track needs to be listed in the table.
+ * The absence of this box does not mean that all the samples are sync samples. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t track_ID;
+    unsigned int reserved                  : 26;
+    unsigned int length_size_of_traf_num   : 2;     /* the length in byte of the traf_number field minus one */
+    unsigned int length_size_of_trun_num   : 2;     /* the length in byte of the trun_number field minus one */
+    unsigned int length_size_of_sample_num : 2;     /* the length in byte of the sample_number field minus one */
+    uint32_t number_of_entry;                       /* the number of the entries for this track
+                                                     * Value zero indicates that every sample is a random access point and no table entry follows. */
+    lsmash_entry_list_t *list;                      /* entry_count corresponds to number_of_entry. */
+} isom_tfra_entry_t;
+
+typedef struct
+{
+    /* version == 0: 64bits -> 32bits */
+    uint64_t time;              /* the presentation time of the random access sample in units defined in the Media Header Box of the associated track
+                                 * According to 14496-12:2008/FPDAM 3, presentation times are composition times. */
+    uint64_t moof_offset;       /* the offset of the Movie Fragment Box used in this entry
+                                 * Offset is the byte-offset between the beginning of the file and the beginning of the Movie Fragment Box. */
+    /* */
+    uint32_t traf_number;       /* the Track Fragment Box ('traf') number that contains the random accessible sample
+                                 * The number ranges from 1 in each Movie Fragment Box ('moof'). */
+    uint32_t trun_number;       /* the Track Fragment Run Box ('trun') number that contains the random accessible sample
+                                 * The number ranges from 1 in each Track Fragment Box ('traf'). */
+    uint32_t sample_number;     /* the sample number that contains the random accessible sample
+                                 * The number ranges from 1 in each Track Fragment Run Box ('trun'). */
+} isom_tfra_location_time_entry_t;
+
+/* Movie Fragment Random Access Offset Box
+ * This box provides a copy of the length field from the enclosing Movie Fragment Random Access Box. */
+typedef struct
+{
+    ISOM_FULLBOX_COMMON;
+    uint32_t length;        /* an integer gives the number of bytes of the enclosing Movie Fragment Random Access Box
+                             * This field is placed at the last of the enclosing box to assist readers scanning
+                             * from the end of the file in finding the Movie Fragment Random Access Box. */
+} isom_mfro_t;
+
+/* Movie Fragment Random Access Box
+ * This box provides a table which may assist readers in finding random access points in a file using movie fragments,
+ * and is usually placed at or near the end of the file.
+ * The last box within the Movie Fragment Random Access Box, which is called Movie Fragment Random Access Offset Box,
+ * provides a copy of the length field from the Movie Fragment Random Access Box. */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    lsmash_entry_list_t *tfra_list;     /* Track Fragment Random Access Box */
+    isom_mfro_t         *mfro;          /* Movie Fragment Random Access Offset Box */
+} isom_mfra_t;
+
+/* Movie fragment manager 
+ * The presence of this means we use the structure of movie fragments. */
+typedef struct
+{
+    isom_moof_entry_t *movie;       /* the address corresponding to the current Movie Fragment Box */
+    uint64_t fragment_count;        /* the number of movie fragments we created */
+    uint64_t pool_size;
+    lsmash_entry_list_t *pool;      /* samples pooled to interleave for the current movie fragment */
+} isom_fragment_manager_t;
+
+/** **/
+
+/* Movie Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_mvhd_t         *mvhd;          /* Movie Header Box */
+    isom_iods_t         *iods;          /* ISOM: Object Descriptor Box / QTFF: null */
+    lsmash_entry_list_t *trak_list;     /* Track Box List */
+    isom_udta_t         *udta;          /* User Data Box */
+    isom_meta_t         *meta;          /* Meta Box */
+    isom_mvex_t         *mvex;          /* Movie Extends Box */
+} isom_moov_t;
+
+/* ROOT */
+struct lsmash_root_tag
+{
+    ISOM_FULLBOX_COMMON;                /* the size field expresses total file size 
+                                         * the flags field expresses file mode */
+    isom_ftyp_t         *ftyp;          /* File Type Box */
+    isom_moov_t         *moov;          /* Movie Box */
+    lsmash_entry_list_t *moof_list;     /* Movie Fragment Box List */
+    isom_mdat_t         *mdat;          /* Media Data Box */
+    isom_free_t         *free;          /* Free Space Box */
+    isom_meta_t         *meta;          /* Meta Box */
+    isom_mfra_t         *mfra;          /* Movie Fragment Random Access Box */
+
+        lsmash_bs_t *bs;                    /* bytestream manager */
+        isom_fragment_manager_t *fragment;  /* movie fragment manager */
+        double max_chunk_duration;          /* max duration per chunk in seconds */
+        double max_async_tolerance;         /* max tolerance, in seconds, for amount of interleaving asynchronization between tracks */
+        uint64_t max_chunk_size;            /* max size per chunk in bytes. */
+        uint64_t max_read_size;             /* max size of reading from a chunk at a time. */
+        uint8_t file_type_written;          /* whether File Type Box was written */
+        uint8_t qt_compatible;              /* compatibility with QuickTime file format */
+        uint8_t isom_compatible;            /* compatibility with ISO Base Media file format */
+        uint8_t avc_extensions;             /* compatibility with AVC extensions */
+        uint8_t mp4_version1;               /* compatibility with MP4 ver.1 file format */
+        uint8_t mp4_version2;               /* compatibility with MP4 ver.2 file format */
+        uint8_t itunes_movie;               /* compatibility with iTunes Movie */
+        uint8_t max_3gpp_version;           /* maximum 3GPP version */
+        uint8_t max_isom_version;           /* maximum ISO Base Media file format version */
+        lsmash_entry_list_t *print;
+        lsmash_entry_list_t *timeline;
+};
+
+/* Track Box */
+typedef struct
+{
+    ISOM_BASEBOX_COMMON;
+    isom_tkhd_t *tkhd;          /* Track Header Box */
+    isom_tapt_t *tapt;          /* ISOM: null / QTFF: Track Aperture Mode Dimensions Box */
+    isom_edts_t *edts;          /* Edit Box */
+    isom_tref_t *tref;          /* Track Reference Box */
+    isom_mdia_t *mdia;          /* Media Box */
+    isom_udta_t *udta;          /* User Data Box */
+    isom_meta_t *meta;          /* Meta Box */
+
+        isom_cache_t *cache;
+        uint32_t related_track_ID;
+        uint8_t is_chapter;
+} isom_trak_entry_t;
+/** **/
+
+/* Box types */
+enum isom_box_type
+{
+    ISOM_BOX_TYPE_ID32  = LSMASH_4CC( 'I', 'D', '3', '2' ),
+    ISOM_BOX_TYPE_ALBM  = LSMASH_4CC( 'a', 'l', 'b', 'm' ),
+    ISOM_BOX_TYPE_AUTH  = LSMASH_4CC( 'a', 'u', 't', 'h' ),
+    ISOM_BOX_TYPE_BPCC  = LSMASH_4CC( 'b', 'p', 'c', 'c' ),
+    ISOM_BOX_TYPE_BUFF  = LSMASH_4CC( 'b', 'u', 'f', 'f' ),
+    ISOM_BOX_TYPE_BXML  = LSMASH_4CC( 'b', 'x', 'm', 'l' ),
+    ISOM_BOX_TYPE_CCID  = LSMASH_4CC( 'c', 'c', 'i', 'd' ),
+    ISOM_BOX_TYPE_CDEF  = LSMASH_4CC( 'c', 'd', 'e', 'f' ),
+    ISOM_BOX_TYPE_CLSF  = LSMASH_4CC( 'c', 'l', 's', 'f' ),
+    ISOM_BOX_TYPE_CMAP  = LSMASH_4CC( 'c', 'm', 'a', 'p' ),
+    ISOM_BOX_TYPE_CO64  = LSMASH_4CC( 'c', 'o', '6', '4' ),
+    ISOM_BOX_TYPE_COLR  = LSMASH_4CC( 'c', 'o', 'l', 'r' ),
+    ISOM_BOX_TYPE_CPRT  = LSMASH_4CC( 'c', 'p', 'r', 't' ),
+    ISOM_BOX_TYPE_CSLG  = LSMASH_4CC( 'c', 's', 'l', 'g' ),
+    ISOM_BOX_TYPE_CTTS  = LSMASH_4CC( 'c', 't', 't', 's' ),
+    ISOM_BOX_TYPE_CVRU  = LSMASH_4CC( 'c', 'v', 'r', 'u' ),
+    ISOM_BOX_TYPE_DCFD  = LSMASH_4CC( 'd', 'c', 'f', 'D' ),
+    ISOM_BOX_TYPE_DINF  = LSMASH_4CC( 'd', 'i', 'n', 'f' ),
+    ISOM_BOX_TYPE_DREF  = LSMASH_4CC( 'd', 'r', 'e', 'f' ),
+    ISOM_BOX_TYPE_DSCP  = LSMASH_4CC( 'd', 's', 'c', 'p' ),
+    ISOM_BOX_TYPE_DSGD  = LSMASH_4CC( 'd', 's', 'g', 'd' ),
+    ISOM_BOX_TYPE_DSTG  = LSMASH_4CC( 'd', 's', 't', 'g' ),
+    ISOM_BOX_TYPE_EDTS  = LSMASH_4CC( 'e', 'd', 't', 's' ),
+    ISOM_BOX_TYPE_ELST  = LSMASH_4CC( 'e', 'l', 's', 't' ),
+    ISOM_BOX_TYPE_FECI  = LSMASH_4CC( 'f', 'e', 'c', 'i' ),
+    ISOM_BOX_TYPE_FECR  = LSMASH_4CC( 'f', 'e', 'c', 'r' ),
+    ISOM_BOX_TYPE_FIIN  = LSMASH_4CC( 'f', 'i', 'i', 'n' ),
+    ISOM_BOX_TYPE_FIRE  = LSMASH_4CC( 'f', 'i', 'r', 'e' ),
+    ISOM_BOX_TYPE_FPAR  = LSMASH_4CC( 'f', 'p', 'a', 'r' ),
+    ISOM_BOX_TYPE_FREE  = LSMASH_4CC( 'f', 'r', 'e', 'e' ),
+    ISOM_BOX_TYPE_FRMA  = LSMASH_4CC( 'f', 'r', 'm', 'a' ),
+    ISOM_BOX_TYPE_FTYP  = LSMASH_4CC( 'f', 't', 'y', 'p' ),
+    ISOM_BOX_TYPE_GITN  = LSMASH_4CC( 'g', 'i', 't', 'n' ),
+    ISOM_BOX_TYPE_GNRE  = LSMASH_4CC( 'g', 'n', 'r', 'e' ),
+    ISOM_BOX_TYPE_GRPI  = LSMASH_4CC( 'g', 'r', 'p', 'i' ),
+    ISOM_BOX_TYPE_HDLR  = LSMASH_4CC( 'h', 'd', 'l', 'r' ),
+    ISOM_BOX_TYPE_HMHD  = LSMASH_4CC( 'h', 'm', 'h', 'd' ),
+    ISOM_BOX_TYPE_ICNU  = LSMASH_4CC( 'i', 'c', 'n', 'u' ),
+    ISOM_BOX_TYPE_IDAT  = LSMASH_4CC( 'i', 'd', 'a', 't' ),
+    ISOM_BOX_TYPE_IHDR  = LSMASH_4CC( 'i', 'h', 'd', 'r' ),
+    ISOM_BOX_TYPE_IINF  = LSMASH_4CC( 'i', 'i', 'n', 'f' ),
+    ISOM_BOX_TYPE_ILOC  = LSMASH_4CC( 'i', 'l', 'o', 'c' ),
+    ISOM_BOX_TYPE_IMIF  = LSMASH_4CC( 'i', 'm', 'i', 'f' ),
+    ISOM_BOX_TYPE_INFU  = LSMASH_4CC( 'i', 'n', 'f', 'u' ),
+    ISOM_BOX_TYPE_IODS  = LSMASH_4CC( 'i', 'o', 'd', 's' ),
+    ISOM_BOX_TYPE_IPHD  = LSMASH_4CC( 'i', 'p', 'h', 'd' ),
+    ISOM_BOX_TYPE_IPMC  = LSMASH_4CC( 'i', 'p', 'm', 'c' ),
+    ISOM_BOX_TYPE_IPRO  = LSMASH_4CC( 'i', 'p', 'r', 'o' ),
+    ISOM_BOX_TYPE_IREF  = LSMASH_4CC( 'i', 'r', 'e', 'f' ),
+    ISOM_BOX_TYPE_JP    = LSMASH_4CC( 'j', 'p', ' ', ' ' ),
+    ISOM_BOX_TYPE_JP2C  = LSMASH_4CC( 'j', 'p', '2', 'c' ),
+    ISOM_BOX_TYPE_JP2H  = LSMASH_4CC( 'j', 'p', '2', 'h' ),
+    ISOM_BOX_TYPE_JP2I  = LSMASH_4CC( 'j', 'p', '2', 'i' ),
+    ISOM_BOX_TYPE_KYWD  = LSMASH_4CC( 'k', 'y', 'w', 'd' ),
+    ISOM_BOX_TYPE_LOCI  = LSMASH_4CC( 'l', 'o', 'c', 'i' ),
+    ISOM_BOX_TYPE_LRCU  = LSMASH_4CC( 'l', 'r', 'c', 'u' ),
+    ISOM_BOX_TYPE_MDAT  = LSMASH_4CC( 'm', 'd', 'a', 't' ),
+    ISOM_BOX_TYPE_MDHD  = LSMASH_4CC( 'm', 'd', 'h', 'd' ),
+    ISOM_BOX_TYPE_MDIA  = LSMASH_4CC( 'm', 'd', 'i', 'a' ),
+    ISOM_BOX_TYPE_MDRI  = LSMASH_4CC( 'm', 'd', 'r', 'i' ),
+    ISOM_BOX_TYPE_MECO  = LSMASH_4CC( 'm', 'e', 'c', 'o' ),
+    ISOM_BOX_TYPE_MEHD  = LSMASH_4CC( 'm', 'e', 'h', 'd' ),
+    ISOM_BOX_TYPE_M7HD  = LSMASH_4CC( 'm', '7', 'h', 'd' ),
+    ISOM_BOX_TYPE_MERE  = LSMASH_4CC( 'm', 'e', 'r', 'e' ),
+    ISOM_BOX_TYPE_META  = LSMASH_4CC( 'm', 'e', 't', 'a' ),
+    ISOM_BOX_TYPE_MFHD  = LSMASH_4CC( 'm', 'f', 'h', 'd' ),
+    ISOM_BOX_TYPE_MFRA  = LSMASH_4CC( 'm', 'f', 'r', 'a' ),
+    ISOM_BOX_TYPE_MFRO  = LSMASH_4CC( 'm', 'f', 'r', 'o' ),
+    ISOM_BOX_TYPE_MINF  = LSMASH_4CC( 'm', 'i', 'n', 'f' ),
+    ISOM_BOX_TYPE_MJHD  = LSMASH_4CC( 'm', 'j', 'h', 'd' ),
+    ISOM_BOX_TYPE_MOOF  = LSMASH_4CC( 'm', 'o', 'o', 'f' ),
+    ISOM_BOX_TYPE_MOOV  = LSMASH_4CC( 'm', 'o', 'o', 'v' ),
+    ISOM_BOX_TYPE_MVCG  = LSMASH_4CC( 'm', 'v', 'c', 'g' ),
+    ISOM_BOX_TYPE_MVCI  = LSMASH_4CC( 'm', 'v', 'c', 'i' ),
+    ISOM_BOX_TYPE_MVEX  = LSMASH_4CC( 'm', 'v', 'e', 'x' ),
+    ISOM_BOX_TYPE_MVHD  = LSMASH_4CC( 'm', 'v', 'h', 'd' ),
+    ISOM_BOX_TYPE_MVRA  = LSMASH_4CC( 'm', 'v', 'r', 'a' ),
+    ISOM_BOX_TYPE_NMHD  = LSMASH_4CC( 'n', 'm', 'h', 'd' ),
+    ISOM_BOX_TYPE_OCHD  = LSMASH_4CC( 'o', 'c', 'h', 'd' ),
+    ISOM_BOX_TYPE_ODAF  = LSMASH_4CC( 'o', 'd', 'a', 'f' ),
+    ISOM_BOX_TYPE_ODDA  = LSMASH_4CC( 'o', 'd', 'd', 'a' ),
+    ISOM_BOX_TYPE_ODHD  = LSMASH_4CC( 'o', 'd', 'h', 'd' ),
+    ISOM_BOX_TYPE_ODHE  = LSMASH_4CC( 'o', 'd', 'h', 'e' ),
+    ISOM_BOX_TYPE_ODRB  = LSMASH_4CC( 'o', 'd', 'r', 'b' ),
+    ISOM_BOX_TYPE_ODRM  = LSMASH_4CC( 'o', 'd', 'r', 'm' ),
+    ISOM_BOX_TYPE_ODTT  = LSMASH_4CC( 'o', 'd', 't', 't' ),
+    ISOM_BOX_TYPE_OHDR  = LSMASH_4CC( 'o', 'h', 'd', 'r' ),
+    ISOM_BOX_TYPE_PADB  = LSMASH_4CC( 'p', 'a', 'd', 'b' ),
+    ISOM_BOX_TYPE_PAEN  = LSMASH_4CC( 'p', 'a', 'e', 'n' ),
+    ISOM_BOX_TYPE_PCLR  = LSMASH_4CC( 'p', 'c', 'l', 'r' ),
+    ISOM_BOX_TYPE_PDIN  = LSMASH_4CC( 'p', 'd', 'i', 'n' ),
+    ISOM_BOX_TYPE_PERF  = LSMASH_4CC( 'p', 'e', 'r', 'f' ),
+    ISOM_BOX_TYPE_PITM  = LSMASH_4CC( 'p', 'i', 't', 'm' ),
+    ISOM_BOX_TYPE_RES   = LSMASH_4CC( 'r', 'e', 's', ' ' ),
+    ISOM_BOX_TYPE_RESC  = LSMASH_4CC( 'r', 'e', 's', 'c' ),
+    ISOM_BOX_TYPE_RESD  = LSMASH_4CC( 'r', 'e', 's', 'd' ),
+    ISOM_BOX_TYPE_RTNG  = LSMASH_4CC( 'r', 't', 'n', 'g' ),
+    ISOM_BOX_TYPE_SBGP  = LSMASH_4CC( 's', 'b', 'g', 'p' ),
+    ISOM_BOX_TYPE_SCHI  = LSMASH_4CC( 's', 'c', 'h', 'i' ),
+    ISOM_BOX_TYPE_SCHM  = LSMASH_4CC( 's', 'c', 'h', 'm' ),
+    ISOM_BOX_TYPE_SDEP  = LSMASH_4CC( 's', 'd', 'e', 'p' ),
+    ISOM_BOX_TYPE_SDHD  = LSMASH_4CC( 's', 'd', 'h', 'd' ),
+    ISOM_BOX_TYPE_SDTP  = LSMASH_4CC( 's', 'd', 't', 'p' ),
+    ISOM_BOX_TYPE_SDVP  = LSMASH_4CC( 's', 'd', 'v', 'p' ),
+    ISOM_BOX_TYPE_SEGR  = LSMASH_4CC( 's', 'e', 'g', 'r' ),
+    ISOM_BOX_TYPE_SGPD  = LSMASH_4CC( 's', 'g', 'p', 'd' ),
+    ISOM_BOX_TYPE_SINF  = LSMASH_4CC( 's', 'i', 'n', 'f' ),
+    ISOM_BOX_TYPE_SKIP  = LSMASH_4CC( 's', 'k', 'i', 'p' ),
+    ISOM_BOX_TYPE_SMHD  = LSMASH_4CC( 's', 'm', 'h', 'd' ),
+    ISOM_BOX_TYPE_SRMB  = LSMASH_4CC( 's', 'r', 'm', 'b' ),
+    ISOM_BOX_TYPE_SRMC  = LSMASH_4CC( 's', 'r', 'm', 'c' ),
+    ISOM_BOX_TYPE_SRPP  = LSMASH_4CC( 's', 'r', 'p', 'p' ),
+    ISOM_BOX_TYPE_STBL  = LSMASH_4CC( 's', 't', 'b', 'l' ),
+    ISOM_BOX_TYPE_STCO  = LSMASH_4CC( 's', 't', 'c', 'o' ),
+    ISOM_BOX_TYPE_STDP  = LSMASH_4CC( 's', 't', 'd', 'p' ),
+    ISOM_BOX_TYPE_STSC  = LSMASH_4CC( 's', 't', 's', 'c' ),
+    ISOM_BOX_TYPE_STSD  = LSMASH_4CC( 's', 't', 's', 'd' ),
+    ISOM_BOX_TYPE_STSH  = LSMASH_4CC( 's', 't', 's', 'h' ),
+    ISOM_BOX_TYPE_STSS  = LSMASH_4CC( 's', 't', 's', 's' ),
+    ISOM_BOX_TYPE_STSZ  = LSMASH_4CC( 's', 't', 's', 'z' ),
+    ISOM_BOX_TYPE_STTS  = LSMASH_4CC( 's', 't', 't', 's' ),
+    ISOM_BOX_TYPE_STZ2  = LSMASH_4CC( 's', 't', 'z', '2' ),
+    ISOM_BOX_TYPE_SUBS  = LSMASH_4CC( 's', 'u', 'b', 's' ),
+    ISOM_BOX_TYPE_SWTC  = LSMASH_4CC( 's', 'w', 't', 'c' ),
+    ISOM_BOX_TYPE_TFHD  = LSMASH_4CC( 't', 'f', 'h', 'd' ),
+    ISOM_BOX_TYPE_TFRA  = LSMASH_4CC( 't', 'f', 'r', 'a' ),
+    ISOM_BOX_TYPE_TIBR  = LSMASH_4CC( 't', 'i', 'b', 'r' ),
+    ISOM_BOX_TYPE_TIRI  = LSMASH_4CC( 't', 'i', 'r', 'i' ),
+    ISOM_BOX_TYPE_TITL  = LSMASH_4CC( 't', 'i', 't', 'l' ),
+    ISOM_BOX_TYPE_TKHD  = LSMASH_4CC( 't', 'k', 'h', 'd' ),
+    ISOM_BOX_TYPE_TRAF  = LSMASH_4CC( 't', 'r', 'a', 'f' ),
+    ISOM_BOX_TYPE_TRAK  = LSMASH_4CC( 't', 'r', 'a', 'k' ),
+    ISOM_BOX_TYPE_TREF  = LSMASH_4CC( 't', 'r', 'e', 'f' ),
+    ISOM_BOX_TYPE_TREX  = LSMASH_4CC( 't', 'r', 'e', 'x' ),
+    ISOM_BOX_TYPE_TRGR  = LSMASH_4CC( 't', 'r', 'g', 'r' ),
+    ISOM_BOX_TYPE_TRUN  = LSMASH_4CC( 't', 'r', 'u', 'n' ),
+    ISOM_BOX_TYPE_TSEL  = LSMASH_4CC( 't', 's', 'e', 'l' ),
+    ISOM_BOX_TYPE_UDTA  = LSMASH_4CC( 'u', 'd', 't', 'a' ),
+    ISOM_BOX_TYPE_UINF  = LSMASH_4CC( 'u', 'i', 'n', 'f' ),
+    ISOM_BOX_TYPE_ULST  = LSMASH_4CC( 'u', 'l', 's', 't' ),
+    ISOM_BOX_TYPE_URL   = LSMASH_4CC( 'u', 'r', 'l', ' ' ),
+    ISOM_BOX_TYPE_URN   = LSMASH_4CC( 'u', 'r', 'n', ' ' ),
+    ISOM_BOX_TYPE_UUID  = LSMASH_4CC( 'u', 'u', 'i', 'd' ),
+    ISOM_BOX_TYPE_VMHD  = LSMASH_4CC( 'v', 'm', 'h', 'd' ),
+    ISOM_BOX_TYPE_VWDI  = LSMASH_4CC( 'v', 'w', 'd', 'i' ),
+    ISOM_BOX_TYPE_XML   = LSMASH_4CC( 'x', 'm', 'l', ' ' ),
+    ISOM_BOX_TYPE_YRRC  = LSMASH_4CC( 'y', 'r', 'r', 'c' ),
+
+    ISOM_BOX_TYPE_AVCC  = LSMASH_4CC( 'a', 'v', 'c', 'C' ),
+    ISOM_BOX_TYPE_BTRT  = LSMASH_4CC( 'b', 't', 'r', 't' ),
+    ISOM_BOX_TYPE_CLAP  = LSMASH_4CC( 'c', 'l', 'a', 'p' ),
+    ISOM_BOX_TYPE_ESDS  = LSMASH_4CC( 'e', 's', 'd', 's' ),
+    ISOM_BOX_TYPE_PASP  = LSMASH_4CC( 'p', 'a', 's', 'p' ),
+    ISOM_BOX_TYPE_STSL  = LSMASH_4CC( 's', 't', 's', 'l' ),
+
+    ISOM_BOX_TYPE_CHPL  = LSMASH_4CC( 'c', 'h', 'p', 'l' ),
+
+    ISOM_BOX_TYPE_ALAC  = LSMASH_4CC( 'a', 'l', 'a', 'c' ),
+    ISOM_BOX_TYPE_DAC3  = LSMASH_4CC( 'd', 'a', 'c', '3' ),
+    ISOM_BOX_TYPE_DAMR  = LSMASH_4CC( 'd', 'a', 'm', 'r' ),
+    ISOM_BOX_TYPE_DEC3  = LSMASH_4CC( 'd', 'e', 'c', '3' ),
+
+    ISOM_BOX_TYPE_FTAB  = LSMASH_4CC( 'f', 't', 'a', 'b' ),
+
+    ISOM_BOX_TYPE_DATA  = LSMASH_4CC( 'd', 'a', 't', 'a' ),
+    ISOM_BOX_TYPE_ILST  = LSMASH_4CC( 'i', 'l', 's', 't' ),
+    ISOM_BOX_TYPE_MEAN  = LSMASH_4CC( 'm', 'e', 'a', 'n' ),
+    ISOM_BOX_TYPE_NAME  = LSMASH_4CC( 'n', 'a', 'm', 'e' ),
+};
+
+enum qt_box_type
+{
+    QT_BOX_TYPE_ALAC    = LSMASH_4CC( 'a', 'l', 'a', 'c' ),
+    QT_BOX_TYPE_ALLF    = LSMASH_4CC( 'A', 'l', 'l', 'F' ),
+    QT_BOX_TYPE_CHAN    = LSMASH_4CC( 'c', 'h', 'a', 'n' ),
+    QT_BOX_TYPE_CLEF    = LSMASH_4CC( 'c', 'l', 'e', 'f' ),
+    QT_BOX_TYPE_CLIP    = LSMASH_4CC( 'c', 'l', 'i', 'p' ),
+    QT_BOX_TYPE_COLR    = LSMASH_4CC( 'c', 'o', 'l', 'r' ),
+    QT_BOX_TYPE_CRGN    = LSMASH_4CC( 'c', 'r', 'g', 'n' ),
+    QT_BOX_TYPE_CTAB    = LSMASH_4CC( 'c', 't', 'a', 'b' ),
+    QT_BOX_TYPE_ENDA    = LSMASH_4CC( 'e', 'n', 'd', 'a' ),
+    QT_BOX_TYPE_ENOF    = LSMASH_4CC( 'e', 'n', 'o', 'f' ),
+    QT_BOX_TYPE_FRMA    = LSMASH_4CC( 'f', 'r', 'm', 'a' ),
+    QT_BOX_TYPE_GMHD    = LSMASH_4CC( 'g', 'm', 'h', 'd' ),
+    QT_BOX_TYPE_GMIN    = LSMASH_4CC( 'g', 'm', 'i', 'n' ),
+    QT_BOX_TYPE_IMAP    = LSMASH_4CC( 'i', 'm', 'a', 'p' ),
+    QT_BOX_TYPE_KEYS    = LSMASH_4CC( 'k', 'e', 'y', 's' ),
+    QT_BOX_TYPE_KMAT    = LSMASH_4CC( 'k', 'm', 'a', 't' ),
+    QT_BOX_TYPE_LOAD    = LSMASH_4CC( 'l', 'o', 'a', 'd' ),
+    QT_BOX_TYPE_LOOP    = LSMASH_4CC( 'L', 'O', 'O', 'P' ),
+    QT_BOX_TYPE_MATT    = LSMASH_4CC( 'm', 'a', 't', 't' ),
+    QT_BOX_TYPE_META    = LSMASH_4CC( 'm', 'e', 't', 'a' ),
+    QT_BOX_TYPE_MP4A    = LSMASH_4CC( 'm', 'p', '4', 'a' ),
+    QT_BOX_TYPE_PNOT    = LSMASH_4CC( 'p', 'n', 'o', 't' ),
+    QT_BOX_TYPE_PROF    = LSMASH_4CC( 'p', 'r', 'o', 'f' ),
+    QT_BOX_TYPE_SELO    = LSMASH_4CC( 'S', 'e', 'l', 'O' ),
+    QT_BOX_TYPE_STPS    = LSMASH_4CC( 's', 't', 'p', 's' ),
+    QT_BOX_TYPE_TAPT    = LSMASH_4CC( 't', 'a', 'p', 't' ),
+    QT_BOX_TYPE_TEXT    = LSMASH_4CC( 't', 'e', 'x', 't' ),
+    QT_BOX_TYPE_WAVE    = LSMASH_4CC( 'w', 'a', 'v', 'e' ),
+    QT_BOX_TYPE_WLOC    = LSMASH_4CC( 'W', 'L', 'O', 'C' ),
+
+    QT_BOX_TYPE_TERMINATOR  = 0x00000000,
+};
+
+/* Track reference types */
+typedef enum
+{
+    ISOM_TREF_TYPE_AVCP = LSMASH_4CC( 'a', 'v', 'c', 'p' ),   /* AVC parameter set stream link */
+    ISOM_TREF_TYPE_CDSC = LSMASH_4CC( 'c', 'd', 's', 'c' ),   /* This track describes the referenced track. */
+    ISOM_TREF_TYPE_DPND = LSMASH_4CC( 'd', 'p', 'n', 'd' ),   /* This track has an MPEG-4 dependency on the referenced track. */
+    ISOM_TREF_TYPE_HIND = LSMASH_4CC( 'h', 'i', 'n', 'd' ),   /* Hint dependency */
+    ISOM_TREF_TYPE_HINT = LSMASH_4CC( 'h', 'i', 'n', 't' ),   /* Links hint track to original media track */
+    ISOM_TREF_TYPE_IPIR = LSMASH_4CC( 'i', 'p', 'i', 'r' ),   /* This track contains IPI declarations for the referenced track. */
+    ISOM_TREF_TYPE_MPOD = LSMASH_4CC( 'm', 'p', 'o', 'd' ),   /* This track is an OD track which uses the referenced track as an included elementary stream track. */
+    ISOM_TREF_TYPE_SBAS = LSMASH_4CC( 's', 'b', 'a', 's' ),   /* Scalable base */
+    ISOM_TREF_TYPE_SCAL = LSMASH_4CC( 's', 'c', 'a', 'l' ),   /* Scalable extraction */
+    ISOM_TREF_TYPE_SWFR = LSMASH_4CC( 's', 'w', 'f', 'r' ),   /* AVC Switch from */
+    ISOM_TREF_TYPE_SWTO = LSMASH_4CC( 's', 'w', 't', 'o' ),   /* AVC Switch to */
+    ISOM_TREF_TYPE_SYNC = LSMASH_4CC( 's', 'y', 'n', 'c' ),   /* This track uses the referenced track as its synchronization source. */
+    ISOM_TREF_TYPE_VDEP = LSMASH_4CC( 'v', 'd', 'e', 'p' ),   /* Auxiliary video depth */
+    ISOM_TREF_TYPE_VPLX = LSMASH_4CC( 'v', 'p', 'l', 'x' ),   /* Auxiliary video parallax */
+
+    QT_TREF_TYPE_CHAP   = LSMASH_4CC( 'c', 'h', 'a', 'p' ),   /* Chapter or scene list. Usually references a text track. */
+    QT_TREF_TYPE_SCPT   = LSMASH_4CC( 's', 'c', 'p', 't' ),   /* Transcript. Usually references a text track. */
+    QT_TREF_TYPE_SSRC   = LSMASH_4CC( 's', 's', 'r', 'c' ),   /* Nonprimary source. Indicates that the referenced track should send its data to this track, rather than presenting it. */
+    QT_TREF_TYPE_TMCD   = LSMASH_4CC( 't', 'm', 'c', 'd' ),   /* Time code. Usually references a time code track. */
+} isom_track_reference_type;
+
+/* Handler types */
+enum isom_handler_type
+{
+    QT_HANDLER_TYPE_DATA    = LSMASH_4CC( 'd', 'h', 'l', 'r' ),
+    QT_HANDLER_TYPE_MEDIA   = LSMASH_4CC( 'm', 'h', 'l', 'r' ),
+};
+
+enum isom_meta_type
+{
+    ISOM_META_HANDLER_TYPE_ITUNES_METADATA = LSMASH_4CC( 'm', 'd', 'i', 'r' ),
+};
+
+/* Data reference types */
+enum isom_data_reference_type
+{
+    ISOM_REFERENCE_HANDLER_TYPE_URL     = LSMASH_4CC( 'u', 'r', 'l', ' ' ),
+    ISOM_REFERENCE_HANDLER_TYPE_URN     = LSMASH_4CC( 'u', 'r', 'n', ' ' ),
+
+    QT_REFERENCE_HANDLER_TYPE_ALIAS     = LSMASH_4CC( 'a', 'l', 'i', 's' ),
+    QT_REFERENCE_HANDLER_TYPE_RESOURCE  = LSMASH_4CC( 'r', 's', 'r', 'c' ),
+    QT_REFERENCE_HANDLER_TYPE_URL       = LSMASH_4CC( 'u', 'r', 'l', ' ' ),
+};
+
+/* Lanuage codes */
+typedef struct
+{
+    uint16_t mac_value;
+    uint16_t iso_name;
+} isom_language_t;
+
+static const isom_language_t isom_languages[] =
+{
+    {   0, ISOM_LANGUAGE_CODE_ENGLISH },
+    {   1, ISOM_LANGUAGE_CODE_FRENCH },
+    {   2, ISOM_LANGUAGE_CODE_GERMAN },
+    {   3, ISOM_LANGUAGE_CODE_ITALIAN },
+    {   4, ISOM_LANGUAGE_CODE_DUTCH_M },
+    {   5, ISOM_LANGUAGE_CODE_SWEDISH },
+    {   6, ISOM_LANGUAGE_CODE_SPANISH },
+    {   7, ISOM_LANGUAGE_CODE_DANISH },
+    {   8, ISOM_LANGUAGE_CODE_PORTUGUESE },
+    {   9, ISOM_LANGUAGE_CODE_NORWEGIAN },
+    {  10, ISOM_LANGUAGE_CODE_HEBREW },
+    {  11, ISOM_LANGUAGE_CODE_JAPANESE },
+    {  12, ISOM_LANGUAGE_CODE_ARABIC },
+    {  13, ISOM_LANGUAGE_CODE_FINNISH },
+    {  14, ISOM_LANGUAGE_CODE_GREEK },
+    {  15, ISOM_LANGUAGE_CODE_ICELANDIC },
+    {  16, ISOM_LANGUAGE_CODE_MALTESE },
+    {  17, ISOM_LANGUAGE_CODE_TURKISH },
+    {  18, ISOM_LANGUAGE_CODE_CROATIAN },
+    {  19, ISOM_LANGUAGE_CODE_CHINESE },
+    {  20, ISOM_LANGUAGE_CODE_URDU },
+    {  21, ISOM_LANGUAGE_CODE_HINDI },
+    {  22, ISOM_LANGUAGE_CODE_THAI },
+    {  23, ISOM_LANGUAGE_CODE_KOREAN },
+    {  24, ISOM_LANGUAGE_CODE_LITHUANIAN },
+    {  25, ISOM_LANGUAGE_CODE_POLISH },
+    {  26, ISOM_LANGUAGE_CODE_HUNGARIAN },
+    {  27, ISOM_LANGUAGE_CODE_ESTONIAN },
+    {  28, ISOM_LANGUAGE_CODE_LATVIAN },
+    {  29, ISOM_LANGUAGE_CODE_SAMI },
+    {  30, ISOM_LANGUAGE_CODE_FAROESE },
+    {  32, ISOM_LANGUAGE_CODE_RUSSIAN },
+    {  33, ISOM_LANGUAGE_CODE_CHINESE },
+    {  34, ISOM_LANGUAGE_CODE_DUTCH },
+    {  35, ISOM_LANGUAGE_CODE_IRISH },
+    {  36, ISOM_LANGUAGE_CODE_ALBANIAN },
+    {  37, ISOM_LANGUAGE_CODE_ROMANIAN },
+    {  38, ISOM_LANGUAGE_CODE_CZECH },
+    {  39, ISOM_LANGUAGE_CODE_SLOVAK },
+    {  40, ISOM_LANGUAGE_CODE_SLOVENIA },
+    {  41, ISOM_LANGUAGE_CODE_YIDDISH },
+    {  42, ISOM_LANGUAGE_CODE_SERBIAN },
+    {  43, ISOM_LANGUAGE_CODE_MACEDONIAN },
+    {  44, ISOM_LANGUAGE_CODE_BULGARIAN },
+    {  45, ISOM_LANGUAGE_CODE_UKRAINIAN },
+    {  46, ISOM_LANGUAGE_CODE_BELARUSIAN },
+    {  47, ISOM_LANGUAGE_CODE_UZBEK },
+    {  48, ISOM_LANGUAGE_CODE_KAZAKH },
+    {  49, ISOM_LANGUAGE_CODE_AZERBAIJANI },
+    {  51, ISOM_LANGUAGE_CODE_ARMENIAN },
+    {  52, ISOM_LANGUAGE_CODE_GEORGIAN },
+    {  53, ISOM_LANGUAGE_CODE_MOLDAVIAN },
+    {  54, ISOM_LANGUAGE_CODE_KIRGHIZ },
+    {  55, ISOM_LANGUAGE_CODE_TAJIK },
+    {  56, ISOM_LANGUAGE_CODE_TURKMEN },
+    {  57, ISOM_LANGUAGE_CODE_MONGOLIAN },
+    {  59, ISOM_LANGUAGE_CODE_PASHTO },
+    {  60, ISOM_LANGUAGE_CODE_KURDISH },
+    {  61, ISOM_LANGUAGE_CODE_KASHMIRI },
+    {  62, ISOM_LANGUAGE_CODE_SINDHI },
+    {  63, ISOM_LANGUAGE_CODE_TIBETAN },
+    {  64, ISOM_LANGUAGE_CODE_NEPALI },
+    {  65, ISOM_LANGUAGE_CODE_SANSKRIT },
+    {  66, ISOM_LANGUAGE_CODE_MARATHI },
+    {  67, ISOM_LANGUAGE_CODE_BENGALI },
+    {  68, ISOM_LANGUAGE_CODE_ASSAMESE },
+    {  69, ISOM_LANGUAGE_CODE_GUJARATI },
+    {  70, ISOM_LANGUAGE_CODE_PUNJABI },
+    {  71, ISOM_LANGUAGE_CODE_ORIYA },
+    {  72, ISOM_LANGUAGE_CODE_MALAYALAM },
+    {  73, ISOM_LANGUAGE_CODE_KANNADA },
+    {  74, ISOM_LANGUAGE_CODE_TAMIL },
+    {  75, ISOM_LANGUAGE_CODE_TELUGU },
+    {  76, ISOM_LANGUAGE_CODE_SINHALESE },
+    {  77, ISOM_LANGUAGE_CODE_BURMESE },
+    {  78, ISOM_LANGUAGE_CODE_KHMER },
+    {  79, ISOM_LANGUAGE_CODE_LAO },
+    {  80, ISOM_LANGUAGE_CODE_VIETNAMESE },
+    {  81, ISOM_LANGUAGE_CODE_INDONESIAN },
+    {  82, ISOM_LANGUAGE_CODE_TAGALOG },
+    {  83, ISOM_LANGUAGE_CODE_MALAY_ROMAN },
+    {  84, ISOM_LANGUAGE_CODE_MAYAY_ARABIC },
+    {  85, ISOM_LANGUAGE_CODE_AMHARIC },
+    {  87, ISOM_LANGUAGE_CODE_OROMO },
+    {  88, ISOM_LANGUAGE_CODE_SOMALI },
+    {  89, ISOM_LANGUAGE_CODE_SWAHILI },
+    {  90, ISOM_LANGUAGE_CODE_KINYARWANDA },
+    {  91, ISOM_LANGUAGE_CODE_RUNDI },
+    {  92, ISOM_LANGUAGE_CODE_CHEWA },
+    {  93, ISOM_LANGUAGE_CODE_MALAGASY },
+    {  94, ISOM_LANGUAGE_CODE_ESPERANTO },
+    { 128, ISOM_LANGUAGE_CODE_WELSH },
+    { 129, ISOM_LANGUAGE_CODE_BASQUE },
+    { 130, ISOM_LANGUAGE_CODE_CATALAN },
+    { 131, ISOM_LANGUAGE_CODE_LATIN },
+    { 132, ISOM_LANGUAGE_CODE_QUECHUA },
+    { 133, ISOM_LANGUAGE_CODE_GUARANI },
+    { 134, ISOM_LANGUAGE_CODE_AYMARA },
+    { 135, ISOM_LANGUAGE_CODE_TATAR },
+    { 136, ISOM_LANGUAGE_CODE_UIGHUR },
+    { 137, ISOM_LANGUAGE_CODE_DZONGKHA },
+    { 138, ISOM_LANGUAGE_CODE_JAVANESE },
+    { UINT16_MAX, 0 }
+};
+
+/* Color parameters */
+typedef struct
+{
+    uint16_t primaries;
+    uint16_t transfer;
+    uint16_t matrix;
+} isom_color_parameter_t;
+
+static const isom_color_parameter_t isom_color_parameter_tbl[] =
+{
+    { 2, 2, 2 },        /* Not specified */
+    { 2, 2, 2 },        /* ITU-R BT.470 System M */
+    { 5, 2, 6 },        /* ITU-R BT.470 System B, G */
+    { 1, 1, 1 },        /* ITU-R BT.709 */
+    { 6, 1, 6 },        /* SMPTE 170M */
+    { 6, 7, 7 },        /* SMPTE 240M */
+    { 1, 1, 1 },        /* SMPTE 274M */
+    { 5, 1, 6 },        /* SMPTE 293M */
+    { 1, 1, 1 },        /* SMPTE 296M */
+};
+
+enum qt_color_patameter_type
+{
+    QT_COLOR_PARAMETER_TYPE_NCLC = LSMASH_4CC( 'n', 'c', 'l', 'c' ),      /* nonconstant luminance coding */
+    QT_COLOR_PARAMETER_TYPE_PROF = LSMASH_4CC( 'p', 'r', 'o', 'f' ),      /* ICC profile */
+};
+
+/* QuickTime Audio flags */
+enum qt_compression_id
+{
+    QT_COMPRESSION_ID_NOT_COMPRESSED            = 0,
+    QT_COMPRESSION_ID_FIXED_COMPRESSION         = -1,
+    QT_COMPRESSION_ID_VARIABLE_COMPRESSION      = -2,
+    QT_COMPRESSION_ID_TWO_TO_ONE                = 1,
+    QT_COMPRESSION_ID_EIGHT_TO_THREE            = 2,
+    QT_COMPRESSION_ID_THREE_TO_ONE              = 3,
+    QT_COMPRESSION_ID_SIX_TO_ONE                = 4,
+    QT_COMPRESSION_ID_SIX_TO_ONE_PACKET_SIZE    = 8,
+    QT_COMPRESSION_ID_THREE_TO_ONE_PACKET_SIZE  = 16,
+};
+
+enum qt_audio_format_flags
+{
+    QT_AUDIO_FORMAT_FLAG_FLOAT            = 1,      /* Set for floating point, clear for integer. */
+    QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN       = 1<<1,   /* Set for big endian, clear for little endian. */
+    QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER   = 1<<2,   /* Set for signed integer, clear for unsigned integer.
+                                                     * This is only valid if QT_AUDIO_FORMAT_FLAG_FLOAT is clear. */
+    QT_AUDIO_FORMAT_FLAG_PACKED           = 1<<3,   /* Set if the sample bits occupy the entire available bits for the channel,
+                                                     * clear if they are high or low aligned within the channel. */
+    QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH     = 1<<4,   /* Set if the sample bits are placed into the high bits of the channel, clear for low bit placement.
+                                                     * This is only valid if QT_AUDIO_FORMAT_FLAG_PACKED is clear. */
+    QT_AUDIO_FORMAT_FLAG_NON_INTERLEAVED  = 1<<5,   /* Set if the samples for each channel are located contiguously and the channels are layed out end to end,
+                                                     * clear if the samples for each frame are layed out contiguously and the frames layed out end to end. */
+    QT_AUDIO_FORMAT_FLAG_NON_MIXABLE      = 1<<6,   /* Set to indicate when a format is non-mixable.
+                                                     * Note that this flag is only used when interacting with the HAL's stream format information.
+                                                     * It is not a valid flag for any other uses. */
+    QT_AUDIO_FORMAT_FLAG_ALL_CLEAR        = 1<<31,  /* Set if all the flags would be clear in order to preserve 0 as the wild card value. */
+
+    QT_LPCM_FORMAT_FLAG_FLOAT             = QT_AUDIO_FORMAT_FLAG_FLOAT,
+    QT_LPCM_FORMAT_FLAG_BIG_ENDIAN        = QT_AUDIO_FORMAT_FLAG_BIG_ENDIAN,
+    QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER    = QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER,
+    QT_LPCM_FORMAT_FLAG_PACKED            = QT_AUDIO_FORMAT_FLAG_PACKED,
+    QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH      = QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH,
+    QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED   = QT_AUDIO_FORMAT_FLAG_NON_INTERLEAVED,
+    QT_LPCM_FORMAT_FLAG_NON_MIXABLE       = QT_AUDIO_FORMAT_FLAG_NON_MIXABLE,
+    QT_LPCM_FORMAT_FLAG_ALL_CLEAR         = QT_AUDIO_FORMAT_FLAG_ALL_CLEAR,
+
+    /* These flags are set for Apple Lossless data that was sourced from N bit native endian signed integer data. */
+    QT_ALAC_FORMAT_FLAG_16BIT_SOURCE_DATA = 1,
+    QT_ALAC_FORMAT_FLAG_20BIT_SOURCE_DATA = 2,
+    QT_ALAC_FORMAT_FLAG_24BIT_SOURCE_DATA = 3,
+    QT_ALAC_FORMAT_FLAG_32BIT_SOURCE_DATA = 4,
+};
+
+/* Sample grouping types */
+typedef enum
+{
+    ISOM_GROUP_TYPE_3GAG = LSMASH_4CC( '3', 'g', 'a', 'g' ),      /* Text track3GPP PSS Annex G video buffer parameters */
+    ISOM_GROUP_TYPE_ALST = LSMASH_4CC( 'a', 'l', 's', 't' ),      /* Alternative startup sequence */
+    ISOM_GROUP_TYPE_AVCB = LSMASH_4CC( 'a', 'v', 'c', 'b' ),      /* AVC HRD parameters */
+    ISOM_GROUP_TYPE_AVLL = LSMASH_4CC( 'a', 'v', 'l', 'l' ),      /* AVC Layer */
+    ISOM_GROUP_TYPE_AVSS = LSMASH_4CC( 'a', 'v', 's', 's' ),      /* AVC Sub Sequence */
+    ISOM_GROUP_TYPE_DTRT = LSMASH_4CC( 'd', 't', 'r', 't' ),      /* Decode re-timing */
+    ISOM_GROUP_TYPE_MVIF = LSMASH_4CC( 'm', 'v', 'i', 'f' ),      /* MVC Scalability Information */
+    ISOM_GROUP_TYPE_RAP  = LSMASH_4CC( 'r', 'a', 'p', ' ' ),      /* Random Access Point / This grouping type hasn't been published yet. */
+    ISOM_GROUP_TYPE_RASH = LSMASH_4CC( 'r', 'a', 's', 'h' ),      /* Rate Share */
+    ISOM_GROUP_TYPE_ROLL = LSMASH_4CC( 'r', 'o', 'l', 'l' ),      /* Random Access Recovery Point */
+    ISOM_GROUP_TYPE_SCIF = LSMASH_4CC( 's', 'c', 'i', 'f' ),      /* SVC Scalability Information */
+    ISOM_GROUP_TYPE_SCNM = LSMASH_4CC( 's', 'c', 'n', 'm' ),      /* AVC/SVC/MVC map groups */
+    ISOM_GROUP_TYPE_VIPR = LSMASH_4CC( 'v', 'i', 'p', 'r' ),      /* View priority */
+} isom_grouping_type;
+
+int isom_is_fullbox( void *box );
+int isom_is_lpcm_audio( uint32_t type );
+
+void isom_init_box_common( void *box, void *parent, uint32_t type );
+
+int isom_check_compatibility( lsmash_root_t *root );
+
+char *isom_4cc2str( uint32_t fourcc );
+
+isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID );
+isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type );
+isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type );
+
+isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size );
+void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps );
+
+int isom_add_edts( isom_trak_entry_t *trak );
+int isom_add_elst( isom_edts_t *edts );
+int isom_add_clap( isom_visual_entry_t *visual );
+int isom_add_pasp( isom_visual_entry_t *visual );
+int isom_add_colr( isom_visual_entry_t *visual );
+int isom_add_stsl( isom_visual_entry_t *visual );
+int isom_add_avcC( isom_visual_entry_t *visual );
+int isom_add_btrt( isom_visual_entry_t *visual );
+int isom_add_wave( isom_audio_entry_t *audio );
+int isom_add_frma( isom_wave_t *wave );
+int isom_add_enda( isom_wave_t *wave );
+int isom_add_mp4a( isom_wave_t *wave );
+int isom_add_terminator( isom_wave_t *wave );
+int isom_add_chan( isom_audio_entry_t *audio );
+int isom_add_ftab( isom_tx3g_entry_t *tx3g );
+
+void isom_remove_tapt( isom_tapt_t *tapt );
+void isom_remove_clap( isom_clap_t *clap );
+void isom_remove_pasp( isom_pasp_t *pasp );
+void isom_remove_colr( isom_colr_t *colr );
+void isom_remove_stsl( isom_stsl_t *stsl );
+void isom_remove_avcC( isom_avcC_t *avcC );
+void isom_remove_btrt( isom_btrt_t *btrt );
+void isom_remove_frma( isom_frma_t *frma );
+void isom_remove_enda( isom_enda_t *enda );
+void isom_remove_mp4a( isom_mp4a_t *mp4a );
+void isom_remove_terminator( isom_terminator_t *terminator );
+void isom_remove_wave( isom_wave_t *wave );
+void isom_remove_chan( isom_chan_t *chan );
+void isom_remove_ftab( isom_ftab_t *ftab );
+void isom_remove_sample_description( isom_sample_entry_t *sample );
+
+#define isom_create_box( box_name, parent_name, box_4cc ) \
+    isom_##box_name##_t *(box_name) = malloc( sizeof(isom_##box_name##_t) ); \
+    if( !box_name ) \
+        return -1; \
+    memset( box_name, 0, sizeof(isom_##box_name##_t) ); \
+    isom_init_box_common( box_name, parent_name, box_4cc )
+
+#define isom_create_list_box( box_name, parent_name, box_4cc ) \
+    isom_create_box( box_name, parent_name, box_4cc ); \
+    box_name->list = lsmash_create_entry_list(); \
+    if( !box_name->list ) \
+    { \
+        free( box_name ); \
+        return -1; \
+    }
+
+#define isom_copy_fields( dst, src, box_name ) \
+    lsmash_root_t *root   = dst->box_name->root; \
+    isom_box_t *parent    = dst->box_name->parent; \
+    uint64_t pos          = dst->box_name->pos; \
+    *dst->box_name        = *src->box_name; \
+    dst->box_name->root   = root; \
+    dst->box_name->parent = parent; \
+    dst->box_name->pos    = pos
+
+#endif
diff --git a/output/mp4/importer.c b/output/mp4/importer.c
new file mode 100644
index 0000000..c2d9e38
--- /dev/null
+++ b/output/mp4/importer.c
@@ -0,0 +1,4358 @@
+/*****************************************************************************
+ * importer.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ * Contributors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#define LSMASH_IMPORTER_INTERNAL
+#include "importer.h"
+
+#include "mp4a.h"
+#include "box.h"
+
+/***************************************************************************
+    importer framework
+***************************************************************************/
+struct mp4sys_importer_tag;
+
+typedef void     ( *mp4sys_importer_cleanup )          ( struct mp4sys_importer_tag * );
+typedef int      ( *mp4sys_importer_get_accessunit )   ( struct mp4sys_importer_tag *, uint32_t, lsmash_sample_t * );
+typedef int      ( *mp4sys_importer_probe )            ( struct mp4sys_importer_tag * );
+typedef uint32_t ( *mp4sys_importer_get_last_duration )( struct mp4sys_importer_tag *, uint32_t );
+
+typedef struct
+{
+    const char*                       name;
+    int                               detectable;
+    mp4sys_importer_probe             probe;
+    mp4sys_importer_get_accessunit    get_accessunit;
+    mp4sys_importer_get_last_duration get_last_delta;
+    mp4sys_importer_cleanup           cleanup;
+} mp4sys_importer_functions;
+
+typedef struct mp4sys_importer_tag
+{
+    FILE*                     stream;
+    int                       is_stdin;
+    void*                     info; /* importer internal status information. */
+    mp4sys_importer_functions funcs;
+    lsmash_entry_list_t*      summaries;
+} mp4sys_importer_t;
+
+typedef enum
+{
+    MP4SYS_IMPORTER_ERROR  = -1,
+    MP4SYS_IMPORTER_OK     = 0,
+    MP4SYS_IMPORTER_CHANGE = 1,
+    MP4SYS_IMPORTER_EOF    = 2,
+} mp4sys_importer_status;
+
+/***************************************************************************
+    ADTS importer
+***************************************************************************/
+#define MP4SYS_ADTS_FIXED_HEADER_LENGTH 4 /* this is partly a lie. actually 28 bits. */
+#define MP4SYS_ADTS_BASIC_HEADER_LENGTH 7
+#define MP4SYS_ADTS_MAX_FRAME_LENGTH ( ( 1 << 13 ) - 1 )
+#define MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS 4
+
+typedef struct
+{
+    uint16_t syncword;                           /* 12; */
+    uint8_t  ID;                                 /*  1; */
+    uint8_t  layer;                              /*  2; */
+    uint8_t  protection_absent;                  /*  1; */
+    uint8_t  profile_ObjectType;                 /*  2; */
+    uint8_t  sampling_frequency_index;           /*  4; */
+//  uint8_t  private_bit;                        /*  1; we don't care. */
+    uint8_t  channel_configuration;              /*  3; */
+//  uint8_t  original_copy;                      /*  1; we don't care. */
+//  uint8_t  home;                               /*  1; we don't care. */
+
+} mp4sys_adts_fixed_header_t;
+
+typedef struct
+{
+//  uint8_t  copyright_identification_bit;       /*  1; we don't care. */
+//  uint8_t  copyright_identification_start;     /*  1; we don't care. */
+    uint16_t frame_length;                       /* 13; */
+//  uint16_t adts_buffer_fullness;               /* 11; we don't care. */
+    uint8_t  number_of_raw_data_blocks_in_frame; /*  2; */
+//  uint16_t adts_error_check;                                           /* we don't support */
+//  uint16_t raw_data_block_position[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS-1]; /* we don't use this directly, and... */
+    uint16_t raw_data_block_size[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS];       /* use this instead of above. */
+//  uint16_t adts_header_error_check;                                    /* we don't support, actually crc_check within this */
+//  uint16_t adts_raw_data_block_error_check[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS]; /* we don't support */
+} mp4sys_adts_variable_header_t;
+
+static void mp4sys_adts_parse_fixed_header( uint8_t* buf, mp4sys_adts_fixed_header_t* header )
+{
+    /* FIXME: should we rewrite these code using bitstream reader? */
+    header->syncword                 = (buf[0] << 4) | (buf[1] >> 4);
+    header->ID                       = (buf[1] >> 3) & 0x1;
+    header->layer                    = (buf[1] >> 1) & 0x3;
+    header->protection_absent        = buf[1] & 0x1;
+    header->profile_ObjectType       = buf[2] >> 6;
+    header->sampling_frequency_index = (buf[2] >> 2) & 0xF;
+//  header->private_bit              = (buf[2] >> 1) & 0x1; /* we don't care currently. */
+    header->channel_configuration    = ((buf[2] << 2) | (buf[3] >> 6)) & 0x07;
+//  header->original_copy            = (buf[3] >> 5) & 0x1; /* we don't care currently. */
+//  header->home                     = (buf[3] >> 4) & 0x1; /* we don't care currently. */
+}
+
+static int mp4sys_adts_check_fixed_header( mp4sys_adts_fixed_header_t* header )
+{
+    if( header->syncword != 0xFFF )              return -1;
+//  if( header->ID != 0x0 )                      return -1; /* we don't care. */
+    if( header->layer != 0x0 )                   return -1; /* must be 0b00 for any type of AAC */
+//  if( header->protection_absent != 0x1 )       return -1; /* we don't care. */
+    if( header->profile_ObjectType != 0x1 )      return -1; /* FIXME: 0b00=Main, 0b01=LC, 0b10=SSR, 0b11=LTP. */
+    if( header->sampling_frequency_index > 0xB ) return -1; /* must not be > 0xB. */
+    if( header->channel_configuration == 0x0 )   return -1; /* FIXME: we do not support 0b000 currently. */
+    if( header->profile_ObjectType == 0x3 && header->ID != 0x0 ) return -1; /* LTP is valid only if ID==0. */
+    return 0;
+}
+
+static int mp4sys_adts_parse_variable_header( FILE* stream, uint8_t* buf, unsigned int protection_absent, mp4sys_adts_variable_header_t* header )
+{
+    /* FIXME: should we rewrite these code using bitstream reader? */
+//  header->copyright_identification_bit       = (buf[3] >> 3) & 0x1; /* we don't care. */
+//  header->copyright_identification_start     = (buf[3] >> 2) & 0x1; /* we don't care. */
+    header->frame_length                       = ((buf[3] << 11) | (buf[4] << 3) | (buf[5] >> 5)) & 0x1FFF ;
+//  header->adts_buffer_fullness               = ((buf[5] << 6) | (buf[6] >> 2)) 0x7FF ;  /* we don't care. */
+    header->number_of_raw_data_blocks_in_frame = buf[6] & 0x3;
+
+    if( header->frame_length <= MP4SYS_ADTS_BASIC_HEADER_LENGTH + 2 * (protection_absent == 0) )
+        return -1; /* easy error check */
+
+    /* protection_absent and number_of_raw_data_blocks_in_frame relatives */
+
+    uint8_t buf2[2];
+    unsigned int number_of_blocks = header->number_of_raw_data_blocks_in_frame;
+    if( number_of_blocks == 0 )
+    {
+        header->raw_data_block_size[0] = header->frame_length - MP4SYS_ADTS_BASIC_HEADER_LENGTH;
+        /* skip adts_error_check() and subtract that from block_size */
+        if( protection_absent == 0 )
+        {
+            header->raw_data_block_size[0] -= 2;
+            if( fread( buf2, 1, 2, stream ) != 2 )
+                return -1;
+        }
+        return 0;
+    }
+
+    /* now we have multiple raw_data_block()s, so evaluate adts_header_error_check() */
+
+    uint16_t raw_data_block_position[MP4SYS_ADTS_MAX_RAW_DATA_BLOCKS];
+    uint16_t first_offset = MP4SYS_ADTS_BASIC_HEADER_LENGTH;
+    if( protection_absent == 0 )
+    {
+        /* process adts_header_error_check() */
+        for( int i = 0 ; i < number_of_blocks ; i++ ) /* 1-based in the spec, but we use 0-based */
+        {
+            if( fread( buf2, 1, 2, stream ) != 2 )
+                return -1;
+            raw_data_block_position[i] = (buf2[0] << 8) | buf2[1];
+        }
+        /* skip crc_check in adts_header_error_check().
+           Or might be sizeof( adts_error_check() ) if we share with the case number_of_raw_data_blocks_in_frame == 0 */
+        if( fread( buf2, 1, 2, stream ) != 2 )
+            return -1;
+        first_offset += ( 2 * number_of_blocks ) + 2; /* according to above */
+    }
+    else
+    {
+        /*
+         * NOTE: We never support the case where number_of_raw_data_blocks_in_frame != 0 && protection_absent != 0,
+         * because we have to parse the raw AAC bitstream itself to find boundaries of raw_data_block()s in this case.
+         * Which is to say, that braindamaged spec requires us (mp4 muxer) to decode AAC once to split frames.
+         * L-SMASH is NOT AAC DECODER, so that we've just given up for this case.
+         * This is ISO/IEC 13818-7's sin which defines ADTS format originally.
+         */
+        return -1;
+    }
+
+    /* convert raw_data_block_position --> raw_data_block_size */
+
+    /* do conversion for first */
+    header->raw_data_block_size[0] = raw_data_block_position[0] - first_offset;
+    /* set dummy offset to tail for loop, do coversion for rest. */
+    raw_data_block_position[number_of_blocks] = header->frame_length;
+    for( int i = 1 ; i <= number_of_blocks ; i++ )
+        header->raw_data_block_size[i] = raw_data_block_position[i] - raw_data_block_position[i-1];
+
+    /* adjustment for adts_raw_data_block_error_check() */
+    if( protection_absent == 0 && number_of_blocks != 0 )
+        for( int i = 0 ; i <= number_of_blocks ; i++ )
+            header->raw_data_block_size[i] -= 2;
+
+    return 0;
+}
+
+static int mp4sys_adts_parse_headers( FILE* stream, uint8_t* buf, mp4sys_adts_fixed_header_t* header, mp4sys_adts_variable_header_t* variable_header )
+{
+    mp4sys_adts_parse_fixed_header( buf, header );
+    if( mp4sys_adts_check_fixed_header( header ) )
+        return -1;
+    /* get payload length & skip extra(crc) header */
+    return mp4sys_adts_parse_variable_header( stream, buf, header->protection_absent, variable_header );
+}
+
+static lsmash_audio_summary_t *mp4sys_adts_create_summary( mp4sys_adts_fixed_header_t *header )
+{
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return NULL;
+    summary->sample_type            = ISOM_CODEC_TYPE_MP4A_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3;
+    summary->max_au_length          = MP4SYS_ADTS_MAX_FRAME_LENGTH;
+    summary->frequency              = mp4a_sampling_frequency_table[header->sampling_frequency_index][1];
+    summary->channels               = header->channel_configuration + ( header->channel_configuration == 0x07 ); /* 0x07 means 7.1ch */
+    summary->bit_depth              = 16;
+    summary->samples_in_frame       = 1024;
+    summary->aot                    = header->profile_ObjectType + MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN;
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED;
+#if 0 /* FIXME: This is very unstable. Many players crash with this. */
+    if( header->ID != 0 )
+    {
+        /*
+         * NOTE: This ADTS seems of ISO/IEC 13818-7 (MPEG-2 AAC).
+         * It has special object_type_indications, depending on it's profile (Legacy Interface).
+         * If ADIF header is not available, it should not have decoder specific information, so AudioObjectType neither.
+         * see ISO/IEC 14496-1, DecoderSpecificInfo and 14496-3 Subpart 9: MPEG-1/2 Audio in MPEG-4.
+         */
+        summary->object_type_indication = header->profile_ObjectType + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile;
+        summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_NULL;
+        summary->asc                    = NULL;
+        summary->asc_length             = 0;
+        // summary->sbr_mode            = MP4A_AAC_SBR_NONE; /* MPEG-2 AAC should not be HE-AAC, but we forgive them. */
+        return summary;
+    }
+#endif
+    if( lsmash_setup_AudioSpecificConfig( summary ) )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return NULL;
+    }
+    return summary;
+}
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    unsigned int raw_data_block_idx;
+    mp4sys_adts_fixed_header_t header;
+    mp4sys_adts_variable_header_t variable_header;
+    uint32_t samples_in_frame;
+    uint32_t au_number;
+} mp4sys_adts_info_t;
+
+static int mp4sys_adts_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    mp4sys_adts_info_t* info = (mp4sys_adts_info_t*)importer->info;
+    mp4sys_importer_status current_status = info->status;
+    uint16_t raw_data_block_size = info->variable_header.raw_data_block_size[info->raw_data_block_idx];
+    if( current_status == MP4SYS_IMPORTER_ERROR || buffered_sample->length < raw_data_block_size )
+        return -1;
+    if( current_status == MP4SYS_IMPORTER_EOF )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    if( current_status == MP4SYS_IMPORTER_CHANGE )
+    {
+        lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &info->header );
+        if( !summary )
+            return -1;
+        lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number );
+        if( !entry || !entry->data )
+            return -1;
+        lsmash_cleanup_summary( entry->data );
+        entry->data = summary;
+        info->samples_in_frame = summary->samples_in_frame;
+    }
+
+    /* read a raw_data_block(), typically == payload of a ADTS frame */
+    if( fread( buffered_sample->data, 1, raw_data_block_size, importer->stream ) != raw_data_block_size )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return -1;
+    }
+    buffered_sample->length = raw_data_block_size;
+    buffered_sample->dts = info->au_number++ * info->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    buffered_sample->prop.pre_roll.distance = 1;    /* MDCT */
+
+    /* now we succeeded to read current frame, so "return" takes 0 always below. */
+
+    /* skip adts_raw_data_block_error_check() */
+    if( info->header.protection_absent == 0
+        && info->variable_header.number_of_raw_data_blocks_in_frame != 0
+        && fread( buffered_sample->data, 1, 2, importer->stream ) != 2 )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+    /* current adts_frame() has any more raw_data_block()? */
+    if( info->raw_data_block_idx < info->variable_header.number_of_raw_data_blocks_in_frame )
+    {
+        info->raw_data_block_idx++;
+        info->status = MP4SYS_IMPORTER_OK;
+        return 0;
+    }
+    info->raw_data_block_idx = 0;
+
+    /* preparation for next frame */
+
+    uint8_t buf[MP4SYS_ADTS_MAX_FRAME_LENGTH];
+    size_t ret = fread( buf, 1, MP4SYS_ADTS_BASIC_HEADER_LENGTH, importer->stream );
+    if( ret == 0 )
+    {
+        info->status = MP4SYS_IMPORTER_EOF;
+        return 0;
+    }
+    if( ret != MP4SYS_ADTS_BASIC_HEADER_LENGTH )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+    /*
+     * NOTE: About the spec of ADTS headers.
+     * By the spec definition, ADTS's fixed header cannot change in the middle of stream.
+     * But spec of MP4 allows that a stream(track) changes its properties in the middle of it.
+     */
+    /*
+     * NOTE: About detailed check for ADTS headers.
+     * We do not ommit detailed check for fixed header by simply testing bits' identification,
+     * because there're some flags which does not matter to audio_summary (so AudioSpecificConfig neither)
+     * so that we can take them as no change and never make new ObjectDescriptor.
+     * I know that can be done with/by bitmask also and that should be fast, but L-SMASH project prefers
+     * even foolishly straightforward way.
+     */
+    /*
+     * NOTE: About our reading algorithm for ADTS.
+     * It's rather simple if we retrieve payload of ADTS (i.e. raw AAC frame) at the same time to
+     * retrieve headers.
+     * But then we have to cache and memcpy every frame so that it requires more clocks and memory.
+     * To avoid them, I adopted this separate retrieving method.
+     */
+    mp4sys_adts_fixed_header_t header = {0};
+    mp4sys_adts_variable_header_t variable_header = {0};
+    if( mp4sys_adts_parse_headers( importer->stream, buf, &header, &variable_header ) )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+    info->variable_header = variable_header;
+
+    /*
+     * NOTE: About our support for change(s) of properties within an ADTS stream.
+     * We have to modify these conditions depending on the features we support.
+     * For example, if we support copyright_identification_* in any way within any feature
+     * defined by/in any specs, such as ISO/IEC 14496-1 (MPEG-4 Systems), like...
+     * "8.3 Intellectual Property Management and Protection (IPMP)", or something similar,
+     * we have to check copyright_identification_* and treat them in audio_summary.
+     * "Change(s)" may result in MP4SYS_IMPORTER_ERROR or MP4SYS_IMPORTER_CHANGE
+     * depending on the features we support, and what the spec allows.
+     * Sometimes the "change(s)" can be allowed, while sometimes they're forbidden.
+     */
+    /* currently UNsupported "change(s)". */
+    if( info->header.profile_ObjectType != header.profile_ObjectType /* currently unsupported. */
+        || info->header.ID != header.ID /* In strict, this means change of object_type_indication. */
+        || info->header.sampling_frequency_index != header.sampling_frequency_index ) /* This may change timebase. */
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+    /* currently supported "change(s)". */
+    if( info->header.channel_configuration != header.channel_configuration )
+    {
+        /*
+         * FIXME: About conditions of VALID "change(s)".
+         * we have to check whether any "change(s)" affect to audioProfileLevelIndication
+         * in InitialObjectDescriptor (MP4_IOD) or not.
+         * If another type or upper level is required by the change(s), that is forbidden.
+         * Because ObjectDescriptor does not have audioProfileLevelIndication,
+         * so that it seems impossible to change audioProfileLevelIndication in the middle of the stream.
+         * Note also any other properties, such as AudioObjectType, object_type_indication.
+         */
+        /*
+         * NOTE: updating summary must be done on next call,
+         * because user may retrieve summary right after this function call of this time,
+         * and that should be of current, before change, one.
+         */
+        info->header = header;
+        info->status = MP4SYS_IMPORTER_CHANGE;
+        return 0;
+    }
+    /* no change which matters to mp4 muxing was found */
+    info->status = MP4SYS_IMPORTER_OK;
+    return 0;
+}
+
+static void mp4sys_adts_cleanup( mp4sys_importer_t* importer )
+{
+    debug_if( importer && importer->info )
+        free( importer->info );
+}
+
+/* returns 0 if it seems adts. */
+static int mp4sys_adts_probe( mp4sys_importer_t* importer )
+{
+    uint8_t buf[MP4SYS_ADTS_MAX_FRAME_LENGTH];
+    if( fread( buf, 1, MP4SYS_ADTS_BASIC_HEADER_LENGTH, importer->stream ) != MP4SYS_ADTS_BASIC_HEADER_LENGTH )
+        return -1;
+
+    mp4sys_adts_fixed_header_t header = {0};
+    mp4sys_adts_variable_header_t variable_header = {0};
+    if( mp4sys_adts_parse_headers( importer->stream, buf, &header, &variable_header ) )
+        return -1;
+
+    /* now the stream seems valid ADTS */
+
+    lsmash_audio_summary_t* summary = mp4sys_adts_create_summary( &header );
+    if( !summary )
+        return -1;
+
+    /* importer status */
+    mp4sys_adts_info_t* info = lsmash_malloc_zero( sizeof(mp4sys_adts_info_t) );
+    if( !info )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    info->status = MP4SYS_IMPORTER_OK;
+    info->raw_data_block_idx = 0;
+    info->header = header;
+    info->variable_header = variable_header;
+    info->samples_in_frame = summary->samples_in_frame;
+
+    if( lsmash_add_entry( importer->summaries, summary ) )
+    {
+        free( info );
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    importer->info = info;
+
+    return 0;
+}
+
+static uint32_t mp4sys_adts_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_adts_info_t *info = (mp4sys_adts_info_t *)importer->info;
+    if( !info || track_number != 1 || info->status != MP4SYS_IMPORTER_EOF )
+        return 0;
+    return info->samples_in_frame;
+}
+
+const static mp4sys_importer_functions mp4sys_adts_importer =
+{
+    "adts",
+    1,
+    mp4sys_adts_probe,
+    mp4sys_adts_get_accessunit,
+    mp4sys_adts_get_last_delta,
+    mp4sys_adts_cleanup
+};
+
+/***************************************************************************
+    mp3 (Legacy Interface) importer
+***************************************************************************/
+
+static void mp4sys_mp3_cleanup( mp4sys_importer_t* importer )
+{
+    debug_if( importer && importer->info )
+        free( importer->info );
+}
+
+typedef struct
+{
+    uint16_t syncword;           /* <12> */
+    uint8_t  ID;                 /* <1> */
+    uint8_t  layer;              /* <2> */
+//  uint8_t  protection_bit;     /* <1> don't care. */
+    uint8_t  bitrate_index;      /* <4> */
+    uint8_t  sampling_frequency; /* <2> */
+    uint8_t  padding_bit;        /* <1> */
+//  uint8_t  private_bit;        /* <1> don't care. */
+    uint8_t  mode;               /* <2> */
+//  uint8_t  mode_extension;     /* <2> don't care. */
+//  uint8_t  copyright;          /* <1> don't care. */
+//  uint8_t  original_copy;      /* <1> don't care. */
+    uint8_t  emphasis;           /* <2> for error check only. */
+
+} mp4sys_mp3_header_t;
+
+static int mp4sys_mp3_parse_header( uint8_t* buf, mp4sys_mp3_header_t* header )
+{
+    /* FIXME: should we rewrite these code using bitstream reader? */
+    uint32_t data = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+    header->syncword           = (data >> 20) & 0xFFF; /* NOTE: don't consider what is called MPEG2.5, which last bit is 0. */
+    header->ID                 = (data >> 19) & 0x1;
+    header->layer              = (data >> 17) & 0x3;
+//  header->protection_bit     = (data >> 16) & 0x1; /* don't care. */
+    header->bitrate_index      = (data >> 12) & 0xF;
+    header->sampling_frequency = (data >> 10) & 0x3;
+    header->padding_bit        = (data >>  9) & 0x1;
+//  header->private_bit        = (data >>  8) & 0x1; /* don't care. */
+    header->mode               = (data >>  6) & 0x3;
+//  header->mode_extension     = (data >>  4) & 0x3;
+//  header->copyright          = (data >>  3) & 0x1; /* don't care. */
+//  header->original_copy      = (data >>  2) & 0x1; /* don't care. */
+    header->emphasis           = data         & 0x3; /* for error check only. */
+
+    if( header->syncword != 0xFFF )                                    return -1;
+    if( header->layer == 0x0 )                                         return -1;
+    if( header->bitrate_index == 0x0 || header->bitrate_index == 0xF ) return -1; /* FIXME: "free" bitrate is unsupported currently. */
+    if( header->sampling_frequency == 0x3)                             return -1;
+    if( header->emphasis == 0x2)                                       return -1;
+    return 0;
+}
+
+#define MP4SYS_MP3_MAX_FRAME_LENGTH (1152*(16/8)*2)
+#define MP4SYS_MP3_HEADER_LENGTH    4
+#define MP4SYS_MODE_IS_2CH( mode )  (!!~(mode))
+#define MP4SYS_LAYER_III            0x1
+#define MP4SYS_LAYER_I              0x3
+
+static const uint32_t mp4sys_mp3_frequency_tbl[2][3] = {
+    { 22050, 24000, 16000 }, /* MPEG-2 BC audio */
+    { 44100, 48000, 32000 }  /* MPEG-1 audio */
+};
+
+static lsmash_audio_summary_t *mp4sys_mp3_create_summary( mp4sys_mp3_header_t *header, int legacy_mode )
+{
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return NULL;
+    summary->sample_type            = ISOM_CODEC_TYPE_MP4A_AUDIO;
+    summary->object_type_indication = header->ID ? MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3 : MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3;
+    summary->max_au_length          = MP4SYS_MP3_MAX_FRAME_LENGTH;
+    summary->frequency              = mp4sys_mp3_frequency_tbl[header->ID][header->sampling_frequency];
+    summary->channels               = MP4SYS_MODE_IS_2CH( header->mode ) + 1;
+    summary->bit_depth              = 16;
+    summary->samples_in_frame       = header->layer == MP4SYS_LAYER_I ? 384 : 1152;
+    summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_Layer_1 + (MP4SYS_LAYER_I - header->layer); /* no effect with Legacy Interface. */
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */
+    summary->exdata                 = NULL;
+    summary->exdata_length          = 0;
+#if 0 /* FIXME: This is very unstable. Many players crash with this. */
+    if( !legacy_mode )
+    {
+        summary->object_type_indication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3;
+        if( lsmash_setup_AudioSpecificConfig( summary ) )
+        {
+            lsmash_cleanup_summary( summary );
+            return NULL;
+        }
+    }
+#endif
+    return summary;
+}
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    mp4sys_mp3_header_t    header;
+    uint8_t                raw_header[MP4SYS_MP3_HEADER_LENGTH];
+    uint32_t               samples_in_frame;
+    uint32_t               au_number;
+} mp4sys_mp3_info_t;
+
+static int mp4sys_mp3_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    mp4sys_mp3_info_t* info = (mp4sys_mp3_info_t*)importer->info;
+    mp4sys_mp3_header_t* header = (mp4sys_mp3_header_t*)&info->header;
+    mp4sys_importer_status current_status = info->status;
+
+    const uint32_t bitrate_tbl[2][3][16] = {
+        {   /* MPEG-2 BC audio */
+            { 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, /* Layer III */
+            { 0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, /* Layer II  */
+            { 0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  /* Layer I   */
+        },
+        {   /* MPEG-1 audio */
+            { 0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0 }, /* Layer III */
+            { 0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, /* Layer II  */
+            { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }  /* Layer I   */
+        }
+    };
+    uint32_t bitrate = bitrate_tbl[header->ID][header->layer-1][header->bitrate_index];
+    uint32_t frequency = mp4sys_mp3_frequency_tbl[header->ID][header->sampling_frequency];
+    debug_if( bitrate == 0 || frequency == 0 )
+        return -1;
+    uint32_t frame_size;
+    if( header->layer == MP4SYS_LAYER_I )
+    {
+        /* mp1's 'slot' is 4 bytes unit. see 11172-3, Audio Sequence General. */
+        frame_size = ( 12 * 1000 * bitrate / frequency + header->padding_bit ) * 4;
+    }
+    else
+    {
+        /* mp2/3's 'slot' is 1 bytes unit. */
+        frame_size = 144 * 1000 * bitrate / frequency + header->padding_bit;
+    }
+
+    if( current_status == MP4SYS_IMPORTER_ERROR || frame_size <= 4 || buffered_sample->length < frame_size )
+        return -1;
+    if( current_status == MP4SYS_IMPORTER_EOF )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    if( current_status == MP4SYS_IMPORTER_CHANGE )
+    {
+        lsmash_audio_summary_t* summary = mp4sys_mp3_create_summary( header, 1 ); /* FIXME: use legacy mode. */
+        if( !summary )
+            return -1;
+        lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number );
+        if( !entry || !entry->data )
+            return -1;
+        lsmash_cleanup_summary( entry->data );
+        entry->data = summary;
+        info->samples_in_frame = summary->samples_in_frame;
+    }
+    /* read a frame's data. */
+    memcpy( buffered_sample->data, info->raw_header, MP4SYS_MP3_HEADER_LENGTH );
+    frame_size -= MP4SYS_MP3_HEADER_LENGTH;
+    if( fread( ((uint8_t*)buffered_sample->data)+MP4SYS_MP3_HEADER_LENGTH, 1, frame_size, importer->stream ) != frame_size )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return -1;
+    }
+    buffered_sample->length = MP4SYS_MP3_HEADER_LENGTH + frame_size;
+    buffered_sample->dts = info->au_number++ * info->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    buffered_sample->prop.pre_roll.distance = header->layer == MP4SYS_LAYER_III ? 1 : 0;    /* Layer III uses MDCT */
+
+    /* now we succeeded to read current frame, so "return" takes 0 always below. */
+    /* preparation for next frame */
+
+    uint8_t buf[MP4SYS_MP3_HEADER_LENGTH];
+    size_t ret = fread( buf, 1, MP4SYS_MP3_HEADER_LENGTH, importer->stream );
+    if( ret == 0 )
+    {
+        info->status = MP4SYS_IMPORTER_EOF;
+        return 0;
+    }
+    if( ret == 1 && *buf == 0x00 )
+    {
+        /* NOTE: ugly hack for mp1 stream created with SCMPX. */
+        info->status = MP4SYS_IMPORTER_EOF;
+        return 0;
+    }
+    if( ret != MP4SYS_MP3_HEADER_LENGTH )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+
+    mp4sys_mp3_header_t new_header = {0};
+    if( mp4sys_mp3_parse_header( buf, &new_header ) )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+    memcpy( info->raw_header, buf, MP4SYS_MP3_HEADER_LENGTH );
+
+    /* currently UNsupported "change(s)". */
+    if( header->layer != new_header.layer /* This means change of object_type_indication with Legacy Interface. */
+        || header->sampling_frequency != new_header.sampling_frequency ) /* This may change timescale. */
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return 0;
+    }
+
+    /* currently supported "change(s)". */
+    if( MP4SYS_MODE_IS_2CH( header->mode ) != MP4SYS_MODE_IS_2CH( new_header.mode ) )
+        info->status = MP4SYS_IMPORTER_CHANGE;
+    else
+        info->status = MP4SYS_IMPORTER_OK; /* no change which matters to mp4 muxing was found */
+    info->header = new_header;
+    return 0;
+}
+
+static int mp4sys_mp3_probe( mp4sys_importer_t* importer )
+{
+    uint8_t buf[MP4SYS_MP3_HEADER_LENGTH];
+    if( fread( buf, 1, MP4SYS_MP3_HEADER_LENGTH, importer->stream ) != MP4SYS_MP3_HEADER_LENGTH )
+        return -1;
+
+    mp4sys_mp3_header_t header = {0};
+    if( mp4sys_mp3_parse_header( buf, &header ) )
+        return -1;
+
+    /* now the stream seems valid mp3 */
+
+    lsmash_audio_summary_t* summary = mp4sys_mp3_create_summary( &header, 1 ); /* FIXME: use legacy mode. */
+    if( !summary )
+        return -1;
+
+    /* importer status */
+    mp4sys_mp3_info_t* info = lsmash_malloc_zero( sizeof(mp4sys_mp3_info_t) );
+    if( !info )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    info->status = MP4SYS_IMPORTER_OK;
+    info->header = header;
+    info->samples_in_frame = summary->samples_in_frame;
+    memcpy( info->raw_header, buf, MP4SYS_MP3_HEADER_LENGTH );
+
+    if( lsmash_add_entry( importer->summaries, summary ) )
+    {
+        free( info );
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    importer->info = info;
+
+    return 0;
+}
+
+static uint32_t mp4sys_mp3_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_mp3_info_t *info = (mp4sys_mp3_info_t *)importer->info;
+    if( !info || track_number != 1 || info->status != MP4SYS_IMPORTER_EOF )
+        return 0;
+    return info->samples_in_frame;
+}
+
+const static mp4sys_importer_functions mp4sys_mp3_importer =
+{
+    "MPEG-1/2BC_Audio_Legacy",
+    1,
+    mp4sys_mp3_probe,
+    mp4sys_mp3_get_accessunit,
+    mp4sys_mp3_get_last_delta,
+    mp4sys_mp3_cleanup
+};
+
+/***************************************************************************
+    AMR-NB/WB storage format importer
+    http://www.ietf.org/rfc/rfc3267.txt (Obsoleted)
+    http://www.ietf.org/rfc/rfc4867.txt
+***************************************************************************/
+static void mp4sys_amr_cleanup( mp4sys_importer_t* importer )
+{
+    debug_if( importer && importer->info )
+        free( importer->info );
+}
+
+typedef struct
+{
+    uint8_t  wb;
+    uint32_t samples_in_frame;
+    uint32_t au_number;
+} mp4sys_amr_info_t;
+
+static int mp4sys_amr_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( track_number != 1 )
+        return -1;
+    mp4sys_amr_info_t *info = (mp4sys_amr_info_t *)importer->info;
+
+    uint8_t* buf = buffered_sample->data;
+    if( fread( buf, 1, 1, importer->stream ) == 0 )
+    {
+        /* EOF */
+        buffered_sample->length = 0;
+        return 0;
+    }
+    uint8_t FT = (*buf >> 3) & 0x0F;
+
+    /* AMR-NB has varieties of frame-size table like this. so I'm not sure yet. */
+    const int frame_size[2][16] = {
+        { 13, 14, 16, 18, 20, 21, 27, 32,  5, 5, 5, 5, 0, 0, 0, 1 },
+        { 18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 6, 0, 0, 0, 1, 1 }
+    };
+    int read_size = frame_size[info->wb][FT];
+    if( read_size == 0 || buffered_sample->length < read_size-- )
+        return -1;
+    if( read_size == 0 )
+        buffered_sample->length = 1;
+    else
+    {
+        if( fread( buf+1, 1, read_size, importer->stream ) != read_size )
+            return -1;
+        buffered_sample->length = read_size + 1;
+    }
+    buffered_sample->dts = info->au_number++ * info->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    return 0;
+}
+
+#define MP4SYS_DAMR_LENGTH 17
+
+int mp4sys_amr_create_damr( lsmash_audio_summary_t *summary )
+{
+    lsmash_bs_t* bs = lsmash_bs_create( NULL ); /* no file writing */
+    if( !bs )
+        return -1;
+    lsmash_bs_put_be32( bs, MP4SYS_DAMR_LENGTH );
+    lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_DAMR );
+    /* NOTE: These are specific to each codec vendor, but we're surely not a vendor.
+              Using dummy data. */
+    lsmash_bs_put_be32( bs, 0x20202020 ); /* vendor */
+    lsmash_bs_put_byte( bs, 0 );          /* decoder_version */
+
+    /* NOTE: Using safe value for these settings, maybe sub-optimal. */
+    lsmash_bs_put_be16( bs, 0x83FF );     /* mode_set, represents for possibly existing frame-type (0x83FF == all). */
+    lsmash_bs_put_byte( bs, 1 );          /* mode_change_period */
+    lsmash_bs_put_byte( bs, 1 );          /* frames_per_sample */
+
+    if( summary->exdata )
+        free( summary->exdata );
+    summary->exdata = lsmash_bs_export_data( bs, &summary->exdata_length );
+    lsmash_bs_cleanup( bs );
+    if( !summary->exdata )
+        return -1;
+    summary->exdata_length = MP4SYS_DAMR_LENGTH;
+    return 0;
+}
+
+#define MP4SYS_AMR_STORAGE_MAGIC_LENGTH 6
+#define MP4SYS_AMRWB_EX_MAGIC_LENGTH 3
+
+static int mp4sys_amr_probe( mp4sys_importer_t* importer )
+{
+    uint8_t buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH];
+    uint8_t wb = 0;
+    if( fread( buf, 1, MP4SYS_AMR_STORAGE_MAGIC_LENGTH, importer->stream ) != MP4SYS_AMR_STORAGE_MAGIC_LENGTH )
+        return -1;
+    if( memcmp( buf, "#!AMR", MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1 ) )
+        return -1;
+    if( buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1] != '\n' )
+    {
+        if( buf[MP4SYS_AMR_STORAGE_MAGIC_LENGTH-1] != '-' )
+            return -1;
+        if( fread( buf, 1, MP4SYS_AMRWB_EX_MAGIC_LENGTH, importer->stream ) != MP4SYS_AMRWB_EX_MAGIC_LENGTH )
+            return -1;
+        if( memcmp( buf, "WB\n", MP4SYS_AMRWB_EX_MAGIC_LENGTH ) )
+            return -1;
+        wb = 1;
+    }
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return -1;
+    summary->sample_type            = wb ? ISOM_CODEC_TYPE_SAWB_AUDIO : ISOM_CODEC_TYPE_SAMR_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_NONE; /* AMR is not defined in ISO/IEC 14496-3 */
+    summary->exdata                 = NULL; /* to be set in mp4sys_amrnb_create_damr() */
+    summary->exdata_length          = 0;    /* to be set in mp4sys_amrnb_create_damr() */
+    summary->max_au_length          = wb ? 61 : 32;
+    summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */
+    summary->frequency              = (8000 << wb);
+    summary->channels               = 1;
+    summary->bit_depth              = 16;
+    summary->samples_in_frame       = (160 << wb);
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */
+    mp4sys_amr_info_t *info = malloc( sizeof(mp4sys_amr_info_t) );
+    if( !info )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    info->wb = wb;
+    info->samples_in_frame = summary->samples_in_frame;
+    info->au_number = 0;
+    importer->info = info;
+    if( mp4sys_amr_create_damr( summary ) || lsmash_add_entry( importer->summaries, summary ) )
+    {
+        free( importer->info );
+        importer->info = NULL;
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    return 0;
+}
+
+static uint32_t mp4sys_amr_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_amr_info_t *info = (mp4sys_amr_info_t *)importer->info;
+    if( !info || track_number != 1 )
+        return 0;
+    return info->samples_in_frame;
+}
+
+const static mp4sys_importer_functions mp4sys_amr_importer =
+{
+    "amr",
+    1,
+    mp4sys_amr_probe,
+    mp4sys_amr_get_accessunit,
+    mp4sys_amr_get_last_delta,
+    mp4sys_amr_cleanup
+};
+
+/***************************************************************************
+    AC-3 importer
+***************************************************************************/
+#define AC3_MAX_AU_LENGTH   3840
+#define AC3_MIN_AU_LENGTH   128
+#define AC3_SAMPLE_DURATION 1536    /* 256 (samples per audio block) * 6 (audio blocks) */
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    uint8_t fscod;
+    uint8_t bsid;
+    uint8_t bsmod;
+    uint8_t acmod;
+    uint8_t lfeon;
+    uint8_t frmsizecod;
+    lsmash_bits_t *bits;
+    uint8_t buffer[AC3_MAX_AU_LENGTH];
+    uint8_t *next_dac3;
+    uint32_t au_number;
+} mp4sys_ac3_info_t;
+
+static void mp4sys_remove_ac3_info( mp4sys_ac3_info_t *info )
+{
+    if( !info )
+        return;
+    lsmash_bits_adhoc_cleanup( info->bits );
+    free( info );
+}
+
+static mp4sys_ac3_info_t *mp4sys_create_ac3_info( void )
+{
+    mp4sys_ac3_info_t *info = (mp4sys_ac3_info_t *)lsmash_malloc_zero( sizeof(mp4sys_ac3_info_t) );
+    if( !info )
+        return NULL;
+    info->bits = lsmash_bits_adhoc_create();
+    if( !info->bits )
+    {
+        free( info );
+        return NULL;
+    }
+    return info;
+}
+
+static void mp4sys_ac3_cleanup( mp4sys_importer_t *importer )
+{
+    debug_if( importer && importer->info )
+        mp4sys_remove_ac3_info( importer->info );
+}
+
+static int ac3_check_syncframe_header( mp4sys_ac3_info_t *info )
+{
+    if( info->fscod == 0x3 )
+        return -1;      /* unknown Sample Rate Code */
+    if( info->frmsizecod > 0x25 )
+        return -1;      /* unknown Frame Size Code */
+    if( info->bsid >= 10 )
+        return -1;      /* might be EAC-3 */
+    return 0;
+}
+
+static int ac3_parse_syncframe_header( mp4sys_ac3_info_t *info, uint8_t *data )
+{
+    lsmash_bits_t *bits = info->bits;
+    if( lsmash_bits_import_data( bits, data, AC3_MIN_AU_LENGTH ) )
+        return -1;
+    lsmash_bits_get( bits, 32 );    /* syncword + crc1 */
+    info->fscod      = lsmash_bits_get( bits, 2 );
+    info->frmsizecod = lsmash_bits_get( bits, 6 );
+    info->bsid       = lsmash_bits_get( bits, 5 );
+    info->bsmod      = lsmash_bits_get( bits, 3 );
+    info->acmod      = lsmash_bits_get( bits, 3 );
+    if( (info->acmod & 0x01) && (info->acmod != 0x01) )
+        lsmash_bits_get( bits, 2 );     /* cmixlev */
+    if( info->acmod & 0x04 )
+        lsmash_bits_get( bits, 2 );     /* surmixlev */
+    if( info->acmod == 0x02 )
+        lsmash_bits_get( bits, 2 );     /* dsurmod */
+    info->lfeon = lsmash_bits_get( bits, 1 );
+    lsmash_bits_empty( bits );
+    return ac3_check_syncframe_header( info );
+}
+
+#define AC3_DAC3_BOX_LENGTH 11
+
+static uint8_t *ac3_create_dac3( mp4sys_ac3_info_t *info )
+{
+    lsmash_bits_t *bits = info->bits;
+    lsmash_bits_put( bits, AC3_DAC3_BOX_LENGTH, 32 );
+    lsmash_bits_put( bits, ISOM_BOX_TYPE_DAC3, 32 );
+    lsmash_bits_put( bits, info->fscod, 2 );
+    lsmash_bits_put( bits, info->bsid, 5 );
+    lsmash_bits_put( bits, info->bsmod, 3 );
+    lsmash_bits_put( bits, info->acmod, 3 );
+    lsmash_bits_put( bits, info->lfeon, 1 );
+    lsmash_bits_put( bits, info->frmsizecod >> 1, 5 );
+    lsmash_bits_put( bits, 0, 5 );
+    uint8_t *dac3 = lsmash_bits_export_data( bits, NULL );
+    lsmash_bits_empty( bits );
+    return dac3;
+}
+
+#define IF_AC3_SYNCWORD( x ) if( (x)[0] != 0x0b || (x)[1] != 0x77 )
+
+/* data_length must be size of data that is available. */
+int mp4sys_create_dac3_from_syncframe( lsmash_audio_summary_t *summary, uint8_t *data, uint32_t data_length )
+{
+    if( data_length < AC3_MIN_AU_LENGTH )
+        return -1;
+    IF_AC3_SYNCWORD( data )
+        return -1;
+    mp4sys_ac3_info_t info;
+    info.bits = lsmash_bits_adhoc_create();
+    if( !info.bits )
+        return -1;
+    if( ac3_parse_syncframe_header( &info, data ) )
+    {
+        lsmash_bits_adhoc_cleanup( info.bits );
+        return -1;
+    }
+    uint8_t *dac3 = ac3_create_dac3( &info );
+    lsmash_bits_adhoc_cleanup( info.bits );
+    if( !dac3 )
+        return -1;
+    if( summary->exdata )
+        free( summary->exdata );
+    summary->exdata = dac3;
+    summary->exdata_length = AC3_DAC3_BOX_LENGTH;
+    return 0;
+}
+
+static const uint32_t ac3_sample_rate_table[4] = { 48000, 44100, 32000, 0 };
+
+static const uint32_t ac3_frame_size_table[19][3] =
+{
+    /*  48,  44.1,    32 */
+    {  128,   138,   192 },
+    {  160,   174,   240 },
+    {  192,   208,   288 },
+    {  224,   242,   336 },
+    {  256,   278,   384 },
+    {  320,   348,   480 },
+    {  384,   416,   576 },
+    {  448,   486,   672 },
+    {  512,   556,   768 },
+    {  640,   696,   960 },
+    {  768,   834,  1152 },
+    {  896,   974,  1344 },
+    { 1024,  1114,  1536 },
+    { 1280,  1392,  1920 },
+    { 1536,  1670,  2304 },
+    { 1792,  1950,  2688 },
+    { 2048,  2228,  3072 },
+    { 2304,  2506,  3456 },
+    { 2560,  2786,  3840 }
+};
+
+static const uint32_t ac3_channel_count_table[8] = { 2, 1, 2, 3, 3, 4, 4, 5 };
+
+static const lsmash_channel_layout_tag ac3_channel_layout_table[8][2] =
+{
+    /*        LFE: off                      LFE: on             */
+    { QT_CHANNEL_LAYOUT_UNKNOWN,    QT_CHANNEL_LAYOUT_UNKNOWN    },     /* FIXME: dual mono */
+    { QT_CHANNEL_LAYOUT_MONO,       QT_CHANNEL_LAYOUT_AC3_1_0_1  },
+    { QT_CHANNEL_LAYOUT_STEREO,     QT_CHANNEL_LAYOUT_DVD_4      },
+    { QT_CHANNEL_LAYOUT_AC3_3_0,    QT_CHANNEL_LAYOUT_AC3_3_0_1  },
+    { QT_CHANNEL_LAYOUT_DVD_2,      QT_CHANNEL_LAYOUT_AC3_2_1_1  },
+    { QT_CHANNEL_LAYOUT_AC3_3_1,    QT_CHANNEL_LAYOUT_AC3_3_1_1  },
+    { QT_CHANNEL_LAYOUT_DVD_3,      QT_CHANNEL_LAYOUT_DVD_18     },
+    { QT_CHANNEL_LAYOUT_MPEG_5_0_C, QT_CHANNEL_LAYOUT_MPEG_5_1_C }
+};
+
+static lsmash_audio_summary_t *ac3_create_summary( mp4sys_ac3_info_t *info )
+{
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return NULL;
+    uint8_t *dac3 = ac3_create_dac3( info );
+    if( !dac3 )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return NULL;
+    }
+    summary->exdata                 = dac3;
+    summary->exdata_length          = AC3_DAC3_BOX_LENGTH;
+    summary->sample_type            = ISOM_CODEC_TYPE_AC_3_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_AC_3_AUDIO;        /* forbidden to use for ISO Base Media */
+    summary->max_au_length          = AC3_MAX_AU_LENGTH;
+    summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */
+    summary->frequency              = ac3_sample_rate_table[ info->fscod ];
+    summary->channels               = ac3_channel_count_table[ info->acmod ] + info->lfeon;
+    summary->bit_depth              = 16;       /* no effect */
+    summary->samples_in_frame       = AC3_SAMPLE_DURATION;
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */
+    summary->layout_tag             = ac3_channel_layout_table[ info->acmod ][ info->lfeon ];
+    return summary;
+}
+
+static int mp4sys_ac3_get_accessunit( mp4sys_importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
+    if( !summary )
+        return -1;
+    mp4sys_ac3_info_t *info = (mp4sys_ac3_info_t *)importer->info;
+    mp4sys_importer_status current_status = info->status;
+    if( current_status == MP4SYS_IMPORTER_EOF )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    if( current_status == MP4SYS_IMPORTER_CHANGE )
+    {
+        free( summary->exdata );
+        summary->exdata     = info->next_dac3;
+        summary->frequency  = ac3_sample_rate_table[ info->fscod ];
+        summary->channels   = ac3_channel_count_table[ info->acmod ] + info->lfeon;
+        summary->layout_tag = ac3_channel_layout_table[ info->acmod ][ info->lfeon ];
+    }
+    uint32_t frame_size = ac3_frame_size_table[ info->frmsizecod >> 1 ][ info->fscod ];
+    if( info->fscod == 0x1 && info->frmsizecod & 0x1 )
+        ++frame_size;
+    if( frame_size > AC3_MIN_AU_LENGTH )
+    {
+        uint32_t read_size = frame_size - AC3_MIN_AU_LENGTH;
+        if( fread( info->buffer + AC3_MIN_AU_LENGTH, 1, read_size, importer->stream ) != read_size )
+            return -1;
+    }
+    memcpy( buffered_sample->data, info->buffer, frame_size );
+    buffered_sample->length = frame_size;
+    buffered_sample->dts = info->au_number++ * summary->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    buffered_sample->prop.pre_roll.distance = 1;    /* MDCT */
+    if( fread( info->buffer, 1, AC3_MIN_AU_LENGTH, importer->stream ) != AC3_MIN_AU_LENGTH )
+        info->status = MP4SYS_IMPORTER_EOF;
+    else
+    {
+        /* Parse the next syncframe header. */
+        IF_AC3_SYNCWORD( info->buffer )
+            return -1;
+        ac3_parse_syncframe_header( info, info->buffer );
+        uint8_t *dac3 = ac3_create_dac3( info );
+        if( memcmp( dac3, summary->exdata, AC3_DAC3_BOX_LENGTH ) )
+        {
+            info->status = MP4SYS_IMPORTER_CHANGE;
+            info->next_dac3 = dac3;
+        }
+        else
+        {
+            info->status = MP4SYS_IMPORTER_OK;
+            free( dac3 );
+        }
+    }
+    return current_status;
+}
+
+static int mp4sys_ac3_probe( mp4sys_importer_t* importer )
+{
+    uint8_t buf[AC3_MIN_AU_LENGTH];
+    if( fread( buf, 1, AC3_MIN_AU_LENGTH, importer->stream ) != AC3_MIN_AU_LENGTH )
+        return -1;
+    IF_AC3_SYNCWORD( buf )
+        return -1;
+    mp4sys_ac3_info_t *info = mp4sys_create_ac3_info();
+    if( !info )
+        return -1;
+    if( ac3_parse_syncframe_header( info, buf ) )
+    {
+        mp4sys_remove_ac3_info( info );
+        return -1;
+    }
+    lsmash_audio_summary_t *summary = ac3_create_summary( info );
+    if( !summary )
+    {
+        mp4sys_remove_ac3_info( info );
+        return -1;
+    }
+    info->status = MP4SYS_IMPORTER_OK;
+    info->au_number = 0;
+    memcpy( info->buffer, buf, AC3_MIN_AU_LENGTH );
+    importer->info = info;
+    if( lsmash_add_entry( importer->summaries, summary ) )
+    {
+        mp4sys_remove_ac3_info( importer->info );
+        importer->info = NULL;
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    return 0;
+}
+
+static uint32_t mp4sys_ac3_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_ac3_info_t *info = (mp4sys_ac3_info_t *)importer->info;
+    if( !info || track_number != 1 || info->status != MP4SYS_IMPORTER_EOF )
+        return 0;
+    return AC3_SAMPLE_DURATION;
+}
+
+const static mp4sys_importer_functions mp4sys_ac3_importer =
+{
+    "ac3",
+    1,
+    mp4sys_ac3_probe,
+    mp4sys_ac3_get_accessunit,
+    mp4sys_ac3_get_last_delta,
+    mp4sys_ac3_cleanup
+};
+
+/***************************************************************************
+    Enhanced AC-3 importer
+***************************************************************************/
+#define EAC3_MAX_SYNCFRAME_LENGTH 4096
+#define EAC3_FIRST_FIVE_BYTES     5
+#define EAC3_MIN_SAMPLE_DURATION  256
+
+typedef struct
+{
+    uint8_t fscod;
+    uint8_t fscod2;
+    uint8_t bsid;
+    uint8_t bsmod;
+    uint8_t acmod;
+    uint8_t lfeon;
+    uint8_t num_dep_sub;
+    uint16_t chan_loc;
+} eac3_substream_info_t;
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    uint8_t strmtyp;
+    uint8_t substreamid;
+    uint8_t current_independent_substream_id;
+    eac3_substream_info_t independent_info_0;   /* mirror for creating summary */
+    eac3_substream_info_t independent_info[8];
+    eac3_substream_info_t dependent_info;
+    uint8_t numblkscod;
+    uint8_t number_of_audio_blocks;
+    uint8_t frmsizecod;
+    uint8_t number_of_independent_substreams;
+    lsmash_bits_t *bits;
+    uint8_t buffer[EAC3_MAX_SYNCFRAME_LENGTH];
+    uint8_t *next_dec3;
+    uint32_t next_dec3_length;
+    uint32_t syncframe_count;
+    uint32_t syncframe_count_in_au;
+    uint32_t frame_size;
+    uint8_t *au;
+    uint32_t au_length;
+    uint8_t *incomplete_au;
+    uint32_t incomplete_au_length;
+    uint8_t *au_buffer;
+    uint32_t au_buffer_size;
+    uint32_t au_number;
+} mp4sys_eac3_info_t;
+
+static void mp4sys_remove_eac3_info( mp4sys_eac3_info_t *info )
+{
+    if( !info )
+        return;
+    if( info->au_buffer )
+        free( info->au_buffer );
+    lsmash_bits_adhoc_cleanup( info->bits );
+    free( info );
+}
+
+static mp4sys_eac3_info_t *mp4sys_create_eac3_info( void )
+{
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)lsmash_malloc_zero( sizeof(mp4sys_eac3_info_t) );
+    if( !info )
+        return NULL;
+    info->bits = lsmash_bits_adhoc_create();
+    if( !info->bits )
+    {
+        free( info );
+        return NULL;
+    }
+    return info;
+}
+
+static void mp4sys_eac3_cleanup( mp4sys_importer_t *importer )
+{
+    debug_if( importer && importer->info )
+        mp4sys_remove_eac3_info( importer->info );
+}
+
+static int eac3_check_syncframe_header( mp4sys_eac3_info_t *info )
+{
+    if( info->strmtyp == 0x3 )
+        return -1;      /* unknown Stream type */
+    eac3_substream_info_t *independent_info;
+    if( info->strmtyp != 0x1 )
+        independent_info = &info->independent_info[ info->current_independent_substream_id ];
+    else
+        independent_info = &info->dependent_info;
+    if( independent_info->fscod == 0x3 && independent_info->fscod2 == 0x3 )
+        return -1;      /* unknown Sample Rate Code */
+    if( independent_info->bsid < 10 || independent_info->bsid > 16 )
+        return -1;      /* not EAC-3 */
+    return 0;
+}
+
+static int eac3_parse_syncframe_header( mp4sys_importer_t *importer )
+{
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)importer->info;
+    lsmash_bits_t *bits = info->bits;
+    if( lsmash_bits_import_data( bits, info->buffer, EAC3_FIRST_FIVE_BYTES ) )
+        return -1;
+    lsmash_bits_get( bits, 16 );    /* syncword */
+    info->strmtyp     = lsmash_bits_get( bits, 2 );
+    info->substreamid = lsmash_bits_get( bits, 3 );
+    eac3_substream_info_t *substream_info;
+    if( info->strmtyp != 0x1 )
+    {
+        info->current_independent_substream_id = info->substreamid;
+        substream_info = &info->independent_info[ info->current_independent_substream_id ];
+        if( info->substreamid == 0x0 )
+            info->independent_info_0 = *substream_info;     /* backup */
+        substream_info->chan_loc = 0;
+    }
+    else
+        substream_info = &info->dependent_info;
+    uint16_t frmsiz = lsmash_bits_get( bits, 11 );
+    substream_info->fscod = lsmash_bits_get( bits, 2 );
+    if( substream_info->fscod == 0x3 )
+    {
+        substream_info->fscod2 = lsmash_bits_get( bits, 2 );
+        info->numblkscod = 0x3;
+    }
+    else
+        info->numblkscod = lsmash_bits_get( bits, 2 );
+    substream_info->acmod = lsmash_bits_get( bits, 3 );
+    substream_info->lfeon = lsmash_bits_get( bits, 1 );
+    lsmash_bits_empty( bits );
+    /* Read up to the end of the current syncframe. */
+    info->frame_size = 2 * (frmsiz + 1);
+    uint32_t read_size = info->frame_size - EAC3_FIRST_FIVE_BYTES;
+    if( fread( info->buffer + EAC3_FIRST_FIVE_BYTES, 1, read_size, importer->stream ) != read_size )
+        return -1;
+    if( lsmash_bits_import_data( bits, info->buffer + EAC3_FIRST_FIVE_BYTES, read_size ) )
+        return -1;
+    /* Continue to parse header. */
+    substream_info->bsid = lsmash_bits_get( bits, 5 );
+    lsmash_bits_get( bits, 5 );         /* dialnorm */
+    if( lsmash_bits_get( bits, 1 ) )    /* compre */
+        lsmash_bits_get( bits, 8 );     /* compr */
+    if( substream_info->acmod == 0x0 )
+    {
+        lsmash_bits_get( bits, 5 );         /* dialnorm2 */
+        if( lsmash_bits_get( bits, 1 ) )    /* compre2 */
+            lsmash_bits_get( bits, 8 );     /* compr2 */
+    }
+    if( info->strmtyp == 0x1 && lsmash_bits_get( bits, 1 ) )    /* chanmape */
+    {
+        uint16_t chanmap = lsmash_bits_get( bits, 16 );
+        uint16_t chan_loc = chanmap >> 5;
+        chan_loc = (chan_loc & 0xff ) | ((chan_loc & 0x200) >> 1);
+        info->independent_info[ info->current_independent_substream_id ].chan_loc |= chan_loc;
+    }
+    if( lsmash_bits_get( bits, 1 ) )    /* mixmdate */
+    {
+        if( substream_info->acmod > 0x2 )
+            lsmash_bits_get( bits, 2 );     /* dmixmod */
+        if( ((substream_info->acmod & 0x1) && (substream_info->acmod > 0x2)) || (substream_info->acmod & 0x4) )
+            lsmash_bits_get( bits, 6 );     /* ltrt[c/sur]mixlev + loro[c/sur]mixlev */
+        if( substream_info->lfeon && lsmash_bits_get( bits, 1 ) )   /* lfemixlevcode */
+            lsmash_bits_get( bits, 5 );     /* lfemixlevcod */
+        if( info->strmtyp == 0x0 )
+        {
+            if( lsmash_bits_get( bits, 1 ) )    /* pgmscle*/
+                lsmash_bits_get( bits, 6 );         /* pgmscl */
+            if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) )    /* pgmscle2 */
+                lsmash_bits_get( bits, 6 );         /* pgmscl2 */
+            if( lsmash_bits_get( bits, 1 ) )    /* extpgmscle */
+                lsmash_bits_get( bits, 6 );         /* extpgmscl */
+            uint8_t mixdef = lsmash_bits_get( bits, 2 );
+            if( mixdef == 0x1 )
+                lsmash_bits_get( bits, 5 );     /* premixcmpsel + drcsrc + premixcmpscl */
+            else if( mixdef == 0x2 )
+                lsmash_bits_get( bits, 12 );    /* mixdata */
+            else if( mixdef == 0x3 )
+            {
+                uint8_t mixdeflen = lsmash_bits_get( bits, 5 );
+                lsmash_bits_get( bits, 8 * (mixdeflen + 2) );     /* mixdata */
+            }
+            if( substream_info->acmod < 0x2 )
+            {
+                if( lsmash_bits_get( bits, 1 ) )    /* paninfoe */
+                    lsmash_bits_get( bits, 14 );        /* paninfo */
+                if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) )    /* paninfo2e */
+                    lsmash_bits_get( bits, 14 );        /* paninfo2 */
+            }
+            if( lsmash_bits_get( bits, 1 ) )        /* frmmixcfginfoe */
+            {
+                if( info->numblkscod == 0x0 )
+                    lsmash_bits_get( bits, 5 );         /* blkmixcfginfo[0] */
+                else
+                {
+                    int number_of_blocks_per_syncframe = ((int []){ 1, 2, 3, 6 })[ info->numblkscod ];
+                    for( int blk = 0; blk < number_of_blocks_per_syncframe; blk++ )
+                        if( lsmash_bits_get( bits, 1 ) )    /* blkmixcfginfoe */
+                            lsmash_bits_get( bits, 5 );         /* blkmixcfginfo[blk] */
+                }
+            }
+        }
+    }
+    if( lsmash_bits_get( bits, 1 ) )    /* infomdate */
+    {
+        substream_info->bsmod = lsmash_bits_get( bits, 3 );
+        lsmash_bits_get( bits, 1 );         /* copyrightb */
+        lsmash_bits_get( bits, 1 );         /* origbs */
+        if( substream_info->acmod == 0x2 )
+            lsmash_bits_get( bits, 4 );         /* dsurmod + dheadphonmod */
+        else if( substream_info->acmod >= 0x6 )
+            lsmash_bits_get( bits, 2 );         /* dsurexmod */
+        if( lsmash_bits_get( bits, 1 ) )    /* audprodie */
+            lsmash_bits_get( bits, 8 );         /* mixlevel + roomtyp + adconvtyp */
+        if( substream_info->acmod == 0x0 && lsmash_bits_get( bits, 1 ) )    /* audprodie2 */
+            lsmash_bits_get( bits, 8 );         /* mixlevel2 + roomtyp2 + adconvtyp2 */
+        if( substream_info->fscod < 0x3 )
+            lsmash_bits_get( bits, 1 );     /* sourcefscod */
+    }
+    else
+        substream_info->bsmod = 0;
+    if( info->strmtyp == 0x0 && info->numblkscod != 0x3 )
+        lsmash_bits_get( bits, 1 );     /* convsync */
+    if( info->strmtyp == 0x2 )
+    {
+        int blkid;
+        if( info->numblkscod == 0x3 )
+            blkid = 1;
+        else
+            blkid = lsmash_bits_get( bits, 1 );
+        if( blkid )
+            lsmash_bits_get( bits, 6 );     /* frmsizecod */
+    }
+    if( lsmash_bits_get( bits, 1 ) )    /* addbsie */
+    {
+        uint8_t addbsil = lsmash_bits_get( bits, 6 );
+        lsmash_bits_get( bits, (addbsil + 1) * 8 );     /* addbsi */
+    }
+    lsmash_bits_empty( bits );
+    return eac3_check_syncframe_header( info );
+}
+
+static uint8_t *eac3_create_dec3( mp4sys_eac3_info_t *info, uint32_t *dec3_length )
+{
+    if( info->number_of_independent_substreams > 8 )
+        return NULL;
+    lsmash_bits_t *bits = info->bits;
+    lsmash_bits_put( bits, 0, 32 );     /* box size */
+    lsmash_bits_put( bits, ISOM_BOX_TYPE_DEC3, 32 );
+    lsmash_bits_put( bits, 0, 13 );     /* data_rate will be calculated by isom_update_bitrate_info */
+    lsmash_bits_put( bits, info->number_of_independent_substreams - 1, 3 );     /* num_ind_sub */
+    /* Apparently, the condition of this loop defined in ETSI TS 102 366 V1.2.1 (2008-08) is wrong. */
+    for( int i = 0; i < info->number_of_independent_substreams; i++ )
+    {
+        eac3_substream_info_t *independent_info = i ? &info->independent_info[i] : &info->independent_info_0;
+        lsmash_bits_put( bits, independent_info->fscod, 2 );
+        lsmash_bits_put( bits, independent_info->bsid, 5 );
+        lsmash_bits_put( bits, independent_info->bsmod, 5 );
+        lsmash_bits_put( bits, independent_info->acmod, 3 );
+        lsmash_bits_put( bits, independent_info->lfeon, 1 );
+        lsmash_bits_put( bits, 0, 3 );      /* reserved */
+        lsmash_bits_put( bits, independent_info->num_dep_sub, 4 );
+        if( independent_info->num_dep_sub > 0 )
+            lsmash_bits_put( bits, independent_info->chan_loc, 9 );
+        else
+            lsmash_bits_put( bits, 0, 1 );      /* reserved */
+    }
+    uint8_t *dec3 = lsmash_bits_export_data( bits, dec3_length );
+    lsmash_bits_empty( bits );
+    /* Update box size. */
+    dec3[0] = ((*dec3_length) >> 24) & 0xff;
+    dec3[1] = ((*dec3_length) >> 16) & 0xff;
+    dec3[2] = ((*dec3_length) >>  8) & 0xff;
+    dec3[3] =  (*dec3_length)        & 0xff;
+    return dec3;
+}
+
+#define IF_EAC3_SYNCWORD( x ) IF_AC3_SYNCWORD( x )
+
+static void eac3_update_sample_rate( lsmash_audio_summary_t *summary, mp4sys_eac3_info_t *info )
+{
+    /* Additional independent substreams 1 to 7 must be encoded at the same sample rate as independent substream 0. */
+    summary->frequency = ac3_sample_rate_table[ info->independent_info_0.fscod ];
+    if( summary->frequency == 0 )
+    {
+        static const uint32_t eac3_reduced_sample_rate_table[4] = { 24000, 22050, 16000, 0 };
+        summary->frequency = eac3_reduced_sample_rate_table[ info->independent_info_0.fscod2 ];
+    }
+}
+
+static void eac3_update_channel_layout( lsmash_audio_summary_t *summary, eac3_substream_info_t *independent_info )
+{
+    if( independent_info->chan_loc == 0 )
+    {
+        summary->layout_tag = ac3_channel_layout_table[ independent_info->acmod ][ independent_info->lfeon ];
+        return;
+    }
+    else if( independent_info->acmod != 0x7 )
+    {
+        summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN;
+        return;
+    }
+    /* OK. All L, C, R, Ls and Rs exsist. */
+    if( !independent_info->lfeon )
+    {
+        if( independent_info->chan_loc == 0x2 )
+            summary->layout_tag = QT_CHANNEL_LAYOUT_EAC_7_0_A;
+        else if( independent_info->chan_loc == 0x4 )
+            summary->layout_tag = QT_CHANNEL_LAYOUT_EAC_6_0_A;
+        else
+            summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN;
+        return;
+    }
+    /* Also LFE exsists. */
+    static const struct
+    {
+        uint16_t chan_loc;
+        lsmash_channel_layout_tag tag;
+    } eac3_channel_layout_table[]
+        = {
+            { 0x1,   QT_CHANNEL_LAYOUT_EAC3_7_1_B },
+            { 0x2,   QT_CHANNEL_LAYOUT_EAC3_7_1_A },
+            { 0x4,   QT_CHANNEL_LAYOUT_EAC3_6_1_A },
+            { 0x8,   QT_CHANNEL_LAYOUT_EAC3_6_1_B },
+            { 0x10,  QT_CHANNEL_LAYOUT_EAC3_7_1_C },
+            { 0x10,  QT_CHANNEL_LAYOUT_EAC3_7_1_D },
+            { 0x40,  QT_CHANNEL_LAYOUT_EAC3_7_1_E },
+            { 0x80,  QT_CHANNEL_LAYOUT_EAC3_6_1_C },
+            { 0xc,   QT_CHANNEL_LAYOUT_EAC3_7_1_F },
+            { 0x84,  QT_CHANNEL_LAYOUT_EAC3_7_1_G },
+            { 0x88,  QT_CHANNEL_LAYOUT_EAC3_7_1_H },
+            { 0 }
+          };
+    for( int i = 0; eac3_channel_layout_table[i].chan_loc; i++ )
+        if( independent_info->chan_loc == eac3_channel_layout_table[i].chan_loc )
+        {
+            summary->layout_tag = eac3_channel_layout_table[i].tag;
+            return;
+        }
+    summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN;
+}
+
+static void eac3_update_channel_info( lsmash_audio_summary_t *summary, mp4sys_eac3_info_t *info )
+{
+    summary->channels = 0;
+    for( int i = 0; i < info->number_of_independent_substreams; i++ )
+    {
+        int channel_count = 0;
+        eac3_substream_info_t *independent_info = i ? &info->independent_info[i] : &info->independent_info_0;
+        channel_count = ac3_channel_count_table[ independent_info->acmod ]  /* L/C/R/Ls/Rs combination */
+                      + 2 * !!(independent_info->chan_loc & 0x1)            /* Lc/Rc pair */
+                      + 2 * !!(independent_info->chan_loc & 0x2)            /* Lrs/Rrs pair */
+                      +     !!(independent_info->chan_loc & 0x4)            /* Cs */
+                      +     !!(independent_info->chan_loc & 0x8)            /* Ts */
+                      + 2 * !!(independent_info->chan_loc & 0x10)           /* Lsd/Rsd pair */
+                      + 2 * !!(independent_info->chan_loc & 0x20)           /* Lw/Rw pair */
+                      + 2 * !!(independent_info->chan_loc & 0x40)           /* Lvh/Rvh pair */
+                      +     !!(independent_info->chan_loc & 0x80)           /* Cvh */
+                      +     !!(independent_info->chan_loc & 0x100)          /* LFE2 */
+                      + independent_info->lfeon;                            /* LFE */
+        if( channel_count > summary->channels )
+        {
+            /* Pick the maximum number of channels. */
+            summary->channels = channel_count;
+            eac3_update_channel_layout( summary, independent_info );
+        }
+    }
+}
+
+static lsmash_audio_summary_t *eac3_create_summary( mp4sys_eac3_info_t *info )
+{
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return NULL;
+    summary->exdata = eac3_create_dec3( info, &summary->exdata_length );
+    if( !summary->exdata )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return NULL;
+    }
+    summary->sample_type            = ISOM_CODEC_TYPE_EC_3_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_EC_3_AUDIO;        /* forbidden to use for ISO Base Media */
+    summary->max_au_length          = info->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH;
+    summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_NULL; /* no effect */
+    summary->bit_depth              = 16;       /* no effect */
+    summary->samples_in_frame       = EAC3_MIN_SAMPLE_DURATION * 6;     /* 256 (samples per audio block) * 6 (audio blocks) */
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */
+    eac3_update_sample_rate( summary, info );
+    eac3_update_channel_info( summary, info );
+    return summary;
+}
+
+static int eac3_read_syncframe( mp4sys_importer_t *importer )
+{
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)importer->info;
+    uint32_t read_size = fread( info->buffer, 1, EAC3_FIRST_FIVE_BYTES, importer->stream );
+    if( read_size == 0 )
+        return 1;       /* EOF */
+    else if( read_size != EAC3_FIRST_FIVE_BYTES )
+        return -1;
+    IF_EAC3_SYNCWORD( info->buffer )
+        return -1;
+    if( eac3_parse_syncframe_header( importer ) )
+        return -1;
+    return 0;
+}
+
+static int eac3_get_next_accessunit_internal( mp4sys_importer_t *importer )
+{
+    static const uint8_t audio_block_table[4] = { 1, 2, 3, 6 };
+    int complete_au = 0;
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)importer->info;
+    while( 1 )
+    {
+        int ret = eac3_read_syncframe( importer );
+        if( ret == -1 )
+            return -1;
+        else if( ret == 1 )
+        {
+            /* According to ETSI TS 102 366 V1.2.1 (2008-08),
+             * one access unit consists of 6 audio blocks and begins with independent substream 0.
+             * The specification doesn't mention the case where a enhanced AC-3 stream ends at non-mod6 audio blocks.
+             * At the end of the stream, therefore, we might make an access unit which has less than 6 audio blocks anyway. */
+            info->status = MP4SYS_IMPORTER_EOF;
+            complete_au = 1;
+        }
+        else
+        {
+            int independent = info->strmtyp != 0x1;
+            if( independent && info->substreamid == 0x0 )
+            {
+                if( info->number_of_audio_blocks == 6 )
+                {
+                    /* Encountered the first syncframe of the next access unit. */
+                    info->number_of_audio_blocks = 0;
+                    complete_au = 1;
+                }
+                else if( info->number_of_audio_blocks > 6 )
+                    return -1;
+                info->number_of_independent_substreams = 0;
+                info->number_of_audio_blocks += audio_block_table[ info->numblkscod ];
+            }
+            else if( info->syncframe_count == 0 )
+                /* The first syncframe in an AU must be independent and assigned substream ID 0. */
+                return -1;
+            if( independent )
+                info->independent_info[info->number_of_independent_substreams ++].num_dep_sub = 0;
+            else
+                ++ info->independent_info[info->number_of_independent_substreams - 1].num_dep_sub;
+        }
+        if( complete_au )
+        {
+            memcpy( info->au, info->incomplete_au, info->incomplete_au_length );
+            info->au_length = info->incomplete_au_length;
+            info->incomplete_au_length = 0;
+            info->syncframe_count_in_au = info->syncframe_count;
+            info->syncframe_count = 0;
+            if( info->status == MP4SYS_IMPORTER_EOF )
+                break;
+        }
+        if( info->incomplete_au_length + info->frame_size > info->au_buffer_size )
+        {
+            /* Increase buffer size to store AU. */
+            uint32_t old_au_buffer_size = info->au_buffer_size;
+            uint32_t new_au_buffer_size = info->au_buffer_size + EAC3_MAX_SYNCFRAME_LENGTH;
+            uint8_t *temp = realloc( info->au_buffer, 2 * new_au_buffer_size );
+            if( !temp )
+                return -1;
+            info->au_buffer      = temp;
+            info->au_buffer_size = new_au_buffer_size;
+            info->au             = info->au_buffer;
+            info->incomplete_au  = info->au_buffer + new_au_buffer_size;
+            memmove( info->incomplete_au, info->au_buffer + old_au_buffer_size, info->incomplete_au_length );
+        }
+        memcpy( info->incomplete_au + info->incomplete_au_length, info->buffer, info->frame_size );
+        info->incomplete_au_length += info->frame_size;
+        ++info->syncframe_count;
+        if( complete_au )
+            break;
+    }
+    return 0;
+}
+
+static int mp4sys_eac3_get_accessunit( mp4sys_importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
+    if( !summary )
+        return -1;
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)importer->info;
+    mp4sys_importer_status current_status = info->status;
+    if( current_status == MP4SYS_IMPORTER_EOF && info->au_length == 0 )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    if( current_status == MP4SYS_IMPORTER_CHANGE )
+    {
+        free( summary->exdata );
+        summary->exdata = info->next_dec3;
+        summary->exdata_length = info->next_dec3_length;
+        summary->max_au_length = info->syncframe_count_in_au * EAC3_MAX_SYNCFRAME_LENGTH;
+        eac3_update_sample_rate( summary, info );
+        eac3_update_channel_info( summary, info );
+    }
+    memcpy( buffered_sample->data, info->au, info->au_length );
+    buffered_sample->length = info->au_length;
+    buffered_sample->dts = info->au_number++ * summary->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    buffered_sample->prop.pre_roll.distance = 1;    /* MDCT */
+    if( info->status == MP4SYS_IMPORTER_EOF )
+    {
+        info->au_length = 0;
+        return 0;
+    }
+    uint32_t old_syncframe_count_in_au = info->syncframe_count_in_au;
+    if( eac3_get_next_accessunit_internal( importer ) )
+        return -1;
+    if( info->syncframe_count_in_au )
+    {
+        uint32_t new_length;
+        uint8_t *dec3 = eac3_create_dec3( info, &new_length );
+        if( !dec3 )
+            return -1;
+        if( (info->syncframe_count_in_au > old_syncframe_count_in_au)
+         || (new_length != summary->exdata_length || memcmp( dec3, summary->exdata, summary->exdata_length )) )
+        {
+            info->status = MP4SYS_IMPORTER_CHANGE;
+            info->next_dec3 = dec3;
+            info->next_dec3_length = new_length;
+        }
+        else
+        {
+            info->status = MP4SYS_IMPORTER_OK;
+            free( dec3 );
+        }
+    }
+    return current_status;
+}
+
+static int mp4sys_eac3_probe( mp4sys_importer_t* importer )
+{
+    mp4sys_eac3_info_t *info = mp4sys_create_eac3_info();
+    if( !info )
+        return -1;
+    importer->info = info;
+    if( eac3_get_next_accessunit_internal( importer ) )
+    {
+        mp4sys_remove_eac3_info( importer->info );
+        importer->info = NULL;
+        return -1;
+    }
+    lsmash_audio_summary_t *summary = eac3_create_summary( info );
+    if( !summary )
+    {
+        mp4sys_remove_eac3_info( importer->info );
+        importer->info = NULL;
+        return -1;
+    }
+    if( info->status != MP4SYS_IMPORTER_EOF )
+        info->status = MP4SYS_IMPORTER_OK;
+    info->au_number = 0;
+    if( lsmash_add_entry( importer->summaries, summary ) )
+    {
+        mp4sys_remove_eac3_info( importer->info );
+        importer->info = NULL;
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    return 0;
+}
+
+static uint32_t mp4sys_eac3_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_eac3_info_t *info = (mp4sys_eac3_info_t *)importer->info;
+    if( !info || track_number != 1
+     || info->status != MP4SYS_IMPORTER_EOF || info->au_length != 0 )
+        return 0;
+    return EAC3_MIN_SAMPLE_DURATION * info->number_of_audio_blocks;
+}
+
+const static mp4sys_importer_functions mp4sys_eac3_importer =
+{
+    "eac3",
+    1,
+    mp4sys_eac3_probe,
+    mp4sys_eac3_get_accessunit,
+    mp4sys_eac3_get_last_delta,
+    mp4sys_eac3_cleanup
+};
+
+/***************************************************************************
+    MPEG-4 ALS importer
+***************************************************************************/
+#define ALSSC_TWELVE_LENGTH 22
+
+typedef struct
+{
+    uint32_t size;
+    uint32_t samp_freq;
+    uint32_t samples;
+    uint32_t channels;
+    uint16_t frame_length;
+    uint8_t  resolution;
+    uint8_t  random_access;
+    uint8_t  ra_flag;
+    uint32_t access_unit_size;
+    uint32_t number_of_ra_units;
+    uint32_t *ra_unit_size;
+    uint8_t  *sc_data;
+} als_specific_config_t;
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    als_specific_config_t alssc;
+    uint32_t samples_in_frame;
+    uint32_t au_number;
+} mp4sys_als_info_t;
+
+typedef struct
+{
+    FILE    *stream;
+    uint32_t pos;
+    uint32_t buffer_size;
+    uint8_t *buffer;
+    uint8_t *end;
+} als_stream_manager;
+
+static void mp4sys_remove_als_info( mp4sys_als_info_t *info )
+{
+    if( info->alssc.ra_unit_size )
+        free( info->alssc.ra_unit_size );
+    if( info->alssc.sc_data )
+        free( info->alssc.sc_data );
+    free( info );
+}
+
+static void mp4sys_als_cleanup( mp4sys_importer_t *importer )
+{
+    debug_if( importer && importer->info )
+        mp4sys_remove_als_info( importer->info );
+}
+
+static int als_stream_read( als_stream_manager *manager, uint32_t read_size )
+{
+    if( manager->buffer + manager->buffer_size >= manager->end )
+    {
+        uint8_t *temp = realloc( manager->buffer, manager->buffer_size + read_size );
+        if( !temp )
+            return -1;
+        manager->buffer = temp;
+        manager->buffer_size += read_size;
+    }
+    uint32_t actual_read_size = fread( manager->buffer + manager->pos, 1, read_size, manager->stream );
+    if( actual_read_size == 0 )
+        return -1;
+    manager->end = manager->buffer + manager->pos + actual_read_size;
+    return 0;
+}
+
+static int als_cleanup_stream_manager( als_stream_manager *manager )
+{
+    free( manager->buffer );
+    return -1;
+}
+
+static uint32_t als_get_be32( als_stream_manager *manager )
+{
+    uint32_t value = (manager->buffer[ manager->pos     ] << 24)
+                   | (manager->buffer[ manager->pos + 1 ] << 16)
+                   | (manager->buffer[ manager->pos + 2 ] <<  8)
+                   |  manager->buffer[ manager->pos + 3 ];
+    manager->pos += 4;
+    return value;
+}
+
+static int als_parse_specific_config( mp4sys_importer_t *importer, uint8_t *buf, als_specific_config_t *alssc )
+{
+    alssc->samp_freq     = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+    alssc->samples       = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
+    if( alssc->samples == 0xffffffff )
+        return -1;      /* We don't support this case. */
+    alssc->channels      = (buf[12] << 8) | buf[13];
+    alssc->resolution    = (buf[14] & 0x1c) >> 2;
+    if( alssc->resolution > 3 )
+        return -1;      /* reserved */
+    alssc->frame_length  = (buf[15] << 8) | buf[16];
+    alssc->random_access = buf[17];
+    alssc->ra_flag       = (buf[18] & 0xc0) >> 6;
+    if( alssc->ra_flag == 0 )
+        return -1;      /* We don't support this case. */
+    buf[18] &= 0x3f;    /* Set 0 to ra_flag. We will remove ra_unit_size in each access unit. */
+#if 0
+    if( alssc->samples == 0xffffffff && alssc->ra_flag == 2 )
+        return -1;
+#endif
+    int chan_sort = !!(buf[20] & 0x1);
+    if( alssc->channels == 0 )
+    {
+        if( buf[20] & 0x8 )
+            return -1;      /* If channels = 0 (mono), joint_stereo = 0. */
+        else if( buf[20] & 0x4 )
+            return -1;      /* If channels = 0 (mono), mc_coding = 0. */
+        else if( chan_sort )
+            return -1;      /* If channels = 0 (mono), chan_sort = 0. */
+    }
+    int chan_config      = !!(buf[20] & 0x2);
+    int crc_enabled      = !!(buf[21] & 0x80);
+    int aux_data_enabled = !!(buf[21] & 0x1);
+    uint32_t read_size = 0;
+    if( chan_config )
+        read_size += 2;     /* chan_config_info */
+    if( chan_sort )
+    {
+        uint32_t ChBits;
+        for( ChBits = 1; alssc->channels >> ChBits; ChBits++ );
+        uint32_t chan_pos_length = (alssc->channels + 1) * ChBits;
+        read_size += chan_pos_length / 8 + !!(chan_pos_length % 8);
+    }
+    /* Set up stream manager. */
+    als_stream_manager manager;
+    manager.stream = importer->stream;
+    manager.buffer_size = ALSSC_TWELVE_LENGTH;
+    manager.buffer = malloc( manager.buffer_size );
+    if( !manager.buffer )
+        return -1;
+    manager.pos = ALSSC_TWELVE_LENGTH + read_size;
+    manager.end = manager.buffer + manager.buffer_size;
+    memcpy( manager.buffer, buf, ALSSC_TWELVE_LENGTH );
+    /* Continue to read and parse. */
+    read_size += 8;     /* header_size and trailer_size */
+    if( als_stream_read( &manager, read_size ) )
+        return als_cleanup_stream_manager( &manager );
+    uint32_t header_size  = als_get_be32( &manager );
+    uint32_t trailer_size = als_get_be32( &manager );
+    read_size = header_size * (header_size != 0xffffffff) + trailer_size * (trailer_size != 0xffffffff) + 4 * crc_enabled;
+    if( als_stream_read( &manager, read_size ) )
+        return -1;
+    manager.pos += read_size;   /* Skip orig_header, orig_trailer and crc. */
+    /* Random access unit */
+    uint32_t number_of_frames = (alssc->samples / (alssc->frame_length + 1)) + !!(alssc->samples % (alssc->frame_length + 1));
+    if( alssc->random_access != 0 )
+        alssc->number_of_ra_units = number_of_frames / alssc->random_access + !!(number_of_frames % alssc->random_access);
+    else
+        alssc->number_of_ra_units = 0;
+    if( alssc->ra_flag == 2 && alssc->random_access != 0 )
+    {
+        uint32_t pos = manager.pos;
+        read_size = alssc->number_of_ra_units * 4;
+        if( als_stream_read( &manager, read_size ) )
+            return als_cleanup_stream_manager( &manager );
+        alssc->ra_unit_size = malloc( alssc->number_of_ra_units * sizeof(uint32_t) );
+        if( !alssc->ra_unit_size )
+            return als_cleanup_stream_manager( &manager );
+        for( uint32_t i = 0; i < alssc->number_of_ra_units; i++ )
+            alssc->ra_unit_size[i] = als_get_be32( &manager );
+        manager.pos = pos;      /* Remove ra_unit_size. */
+    }
+    else
+        alssc->ra_unit_size = NULL;
+    /* auxiliary data */
+    if( aux_data_enabled )
+    {
+        if( als_stream_read( &manager, 4 ) )
+            return als_cleanup_stream_manager( &manager );
+        uint32_t aux_size = als_get_be32( &manager );
+        read_size = aux_size * (aux_size != 0xffffffff);
+        if( als_stream_read( &manager, read_size ) )
+            return als_cleanup_stream_manager( &manager );
+        manager.pos += read_size;
+    }
+    /* Copy ALSSpecificConfig. */
+    alssc->size = manager.pos;
+    alssc->sc_data = malloc( alssc->size );
+    if( !alssc->sc_data )
+        return als_cleanup_stream_manager( &manager );
+    memcpy( alssc->sc_data, manager.buffer, alssc->size );
+    als_cleanup_stream_manager( &manager );
+    return 0;
+}
+
+static int mp4sys_als_get_accessunit( mp4sys_importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_get_entry_data( importer->summaries, track_number );
+    if( !summary )
+        return -1;
+    mp4sys_als_info_t *info = (mp4sys_als_info_t *)importer->info;
+    mp4sys_importer_status current_status = info->status;
+    if( current_status == MP4SYS_IMPORTER_EOF )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    als_specific_config_t *alssc = &info->alssc;
+    if( alssc->number_of_ra_units == 0 )
+    {
+        if( fread( buffered_sample->data, 1, alssc->access_unit_size, importer->stream ) != alssc->access_unit_size )
+            return -1;
+        buffered_sample->length = alssc->access_unit_size;
+        buffered_sample->cts = buffered_sample->dts = 0;
+        buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+        info->status = MP4SYS_IMPORTER_EOF;
+        return 0;
+    }
+    uint32_t au_length;
+    if( alssc->ra_flag == 2 )
+    {
+        au_length = alssc->ra_unit_size[info->au_number];
+        if( fread( buffered_sample->data, 1, au_length, importer->stream ) != au_length )
+            return -1;
+    }
+    else /* if( alssc->ra_flag == 1 ) */
+    {
+        uint8_t temp[4];
+        if( fread( temp, 1, 4, importer->stream ) != 4 )
+            return -1;
+        au_length = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];     /* We remove ra_unit_size. */
+        if( fread( buffered_sample->data, 1, au_length, importer->stream ) != au_length )
+            return -1;
+    }
+    buffered_sample->length = au_length;
+    buffered_sample->dts = info->au_number++ * info->samples_in_frame;
+    buffered_sample->cts = buffered_sample->dts;
+    buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    if( info->au_number == alssc->number_of_ra_units )
+        info->status = MP4SYS_IMPORTER_EOF;
+    return 0;
+}
+
+static lsmash_audio_summary_t *als_create_summary( mp4sys_importer_t *importer, als_specific_config_t *alssc )
+{
+    lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_AudioStream );
+    if( !summary )
+        return NULL;
+    summary->exdata = lsmash_memdup( alssc->sc_data, alssc->size );
+    if( !summary->exdata )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return NULL;
+    }
+    summary->exdata_length          = alssc->size;
+    summary->sample_type            = ISOM_CODEC_TYPE_MP4A_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3;
+    summary->aot                    = MP4A_AUDIO_OBJECT_TYPE_ALS;
+    summary->frequency              = alssc->samp_freq;
+    summary->channels               = alssc->channels + 1;
+    summary->bit_depth              = (alssc->resolution + 1) * 8;
+    summary->sbr_mode               = MP4A_AAC_SBR_NOT_SPECIFIED; /* no effect */
+    if( alssc->random_access != 0 )
+    {
+        summary->samples_in_frame = (alssc->frame_length + 1) * alssc->random_access;
+        summary->max_au_length    = summary->channels * (summary->bit_depth / 8) * summary->samples_in_frame;
+    }
+    else
+    {
+        summary->samples_in_frame = 0;      /* hack for mp4sys_als_get_last_delta */
+        uint64_t pos = lsmash_ftell( importer->stream );
+        lsmash_fseek( importer->stream, 0, SEEK_END );
+        summary->max_au_length = alssc->access_unit_size = lsmash_ftell( importer->stream ) - pos;
+        lsmash_fseek( importer->stream, pos, SEEK_SET );
+    }
+    if( lsmash_setup_AudioSpecificConfig( summary ) )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return NULL;
+    }
+    return summary;
+}
+
+static int mp4sys_als_probe( mp4sys_importer_t *importer )
+{
+    uint8_t buf[ALSSC_TWELVE_LENGTH];
+    if( fread( buf, 1, ALSSC_TWELVE_LENGTH, importer->stream ) != ALSSC_TWELVE_LENGTH )
+        return -1;
+    /* Check ALS identifier( = 0x414C5300). */
+    if( buf[0] != 0x41 || buf[1] != 0x4C || buf[2] != 0x53 || buf[3] != 0x00 )
+        return -1;
+    als_specific_config_t alssc;
+    if( als_parse_specific_config( importer, buf, &alssc ) )
+        return -1;
+    lsmash_audio_summary_t *summary = als_create_summary( importer, &alssc );
+    if( !summary )
+        return -1;
+    /* importer status */
+    mp4sys_als_info_t *info = lsmash_malloc_zero( sizeof(mp4sys_als_info_t) );
+    if( !info )
+    {
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    info->status = MP4SYS_IMPORTER_OK;
+    info->alssc = alssc;
+    info->samples_in_frame = summary->samples_in_frame;
+    if( lsmash_add_entry( importer->summaries, summary ) )
+    {
+        free( info );
+        lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+        return -1;
+    }
+    importer->info = info;
+    return 0;
+}
+
+static uint32_t mp4sys_als_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_als_info_t *info = (mp4sys_als_info_t *)importer->info;
+    if( !info || track_number != 1 || info->status != MP4SYS_IMPORTER_EOF )
+        return 0;
+    als_specific_config_t *alssc = &info->alssc;
+    /* If alssc->number_of_ra_units == 0, then the last sample duration is just alssc->samples
+     * since als_create_summary sets 0 to summary->samples_in_frame i.e. info->samples_in_frame. */
+    return alssc->samples - (alssc->number_of_ra_units - 1) * info->samples_in_frame;
+}
+
+const static mp4sys_importer_functions mp4sys_als_importer =
+{
+    "als",
+    1,
+    mp4sys_als_probe,
+    mp4sys_als_get_accessunit,
+    mp4sys_als_get_last_delta,
+    mp4sys_als_cleanup
+};
+
+/***************************************************************************
+    H.264 importer
+***************************************************************************/
+typedef struct
+{
+    uint8_t nal_ref_idc;
+    uint8_t nal_unit_type;
+    uint8_t length;
+} h264_nalu_header_t;
+
+typedef struct
+{
+    uint16_t sar_width;
+    uint16_t sar_height;
+    uint8_t  video_full_range_flag;
+    uint8_t  colour_primaries;
+    uint8_t  transfer_characteristics;
+    uint8_t  matrix_coefficients;
+    uint32_t num_units_in_tick;
+    uint32_t time_scale;
+    uint8_t  fixed_frame_rate_flag;
+} h264_vui_t;
+
+typedef struct
+{
+    uint8_t  present;
+    uint8_t  profile_idc;
+    uint8_t  constraint_set_flags;
+    uint8_t  level_idc;
+    uint8_t  seq_parameter_set_id;
+    uint8_t  chroma_format_idc;
+    uint8_t  separate_colour_plane_flag;
+    uint8_t  ChromaArrayType;
+    uint8_t  bit_depth_luma_minus8;
+    uint8_t  bit_depth_chroma_minus8;
+    uint8_t  pic_order_cnt_type;
+    uint8_t  delta_pic_order_always_zero_flag;
+    uint8_t  num_ref_frames_in_pic_order_cnt_cycle;
+    uint8_t  frame_mbs_only_flag;
+    uint8_t  hrd_present;
+    int32_t  offset_for_non_ref_pic;
+    int32_t  offset_for_top_to_bottom_field;
+    int32_t  offset_for_ref_frame[255];
+    int64_t  ExpectedDeltaPerPicOrderCntCycle;
+    uint32_t max_num_ref_frames;
+    uint32_t log2_max_frame_num;
+    uint32_t MaxFrameNum;
+    uint32_t log2_max_pic_order_cnt_lsb;
+    uint32_t MaxPicOrderCntLsb;
+    uint32_t PicSizeInMapUnits;
+    uint32_t cropped_width;
+    uint32_t cropped_height;
+    h264_vui_t vui;
+} h264_sps_t;
+
+typedef struct
+{
+    uint8_t  present;
+    uint8_t  pic_parameter_set_id;
+    uint8_t  seq_parameter_set_id;
+    uint8_t  entropy_coding_mode_flag;
+    uint8_t  bottom_field_pic_order_in_frame_present_flag;
+    uint8_t  weighted_pred_flag;
+    uint8_t  weighted_bipred_idc;
+    uint8_t  deblocking_filter_control_present_flag;
+    uint8_t  redundant_pic_cnt_present_flag;
+    uint32_t SliceGroupChangeRate;
+} h264_pps_t;
+
+typedef struct
+{
+    uint32_t recovery_frame_cnt;
+    uint8_t  random_accessible;
+} h264_sei_t;
+
+typedef struct
+{
+    uint8_t  present;
+    uint8_t  type;
+    uint8_t  pic_order_cnt_type;
+    uint8_t  nal_ref_idc;
+    uint8_t  IdrPicFlag;
+    uint8_t  pic_parameter_set_id;
+    uint8_t  field_pic_flag;
+    uint8_t  bottom_field_flag;
+    uint8_t  has_mmco5;
+    uint8_t  has_redundancy;
+    uint16_t idr_pic_id;
+    uint32_t frame_num;
+    int32_t  pic_order_cnt_lsb;
+    int32_t  delta_pic_order_cnt_bottom;
+    int32_t  delta_pic_order_cnt[2];
+} h264_slice_info_t;
+
+typedef struct
+{
+    uint8_t  type;
+    uint8_t  idr;
+    uint8_t  random_accessible;
+    uint8_t  non_bipredictive;
+    uint8_t  independent;
+    uint8_t  disposable;        /* 0: nal_ref_idc != 0, 1: otherwise */
+    uint8_t  has_redundancy;
+    uint8_t  incomplete_au_has_primary;
+    uint8_t  pic_parameter_set_id;
+    uint8_t  field_pic_flag;
+    uint8_t  bottom_field_flag;
+    /* POC */
+    uint8_t  has_mmco5;
+    uint8_t  ref_pic_has_mmco5;
+    uint8_t  ref_pic_bottom_field_flag;
+    int32_t  ref_pic_TopFieldOrderCnt;
+    int32_t  ref_pic_PicOrderCntMsb;
+    int32_t  ref_pic_PicOrderCntLsb;
+    int32_t  pic_order_cnt_lsb;
+    int32_t  delta_pic_order_cnt_bottom;
+    int32_t  delta_pic_order_cnt[2];
+    int32_t  PicOrderCnt;
+    /* */
+    uint32_t recovery_frame_cnt;
+    uint32_t frame_num;
+    uint32_t FrameNumOffset;
+    uint8_t *au;
+    uint32_t au_length;
+    uint8_t *incomplete_au;
+    uint32_t incomplete_au_length;
+    uint32_t au_number;
+} h264_picture_info_t;
+
+typedef struct
+{
+    mp4sys_importer_status status;
+    lsmash_video_summary_t *summary;
+    h264_nalu_header_t nalu_header;
+    uint8_t prev_nalu_type;
+    uint8_t composition_reordering_present;
+    uint8_t no_more_read;
+    uint8_t first_summary;
+    h264_sps_t sps;
+    h264_pps_t pps;
+    h264_sei_t sei;
+    isom_avcC_t avcC;
+    h264_slice_info_t slice;
+    h264_picture_info_t picture;
+    lsmash_bits_t *bits;
+    uint32_t buffer_size;
+    uint8_t *buffer;
+    uint8_t *rbsp_buffer;
+    uint8_t *stream_buffer;
+    uint8_t *stream_buffer_pos;
+    uint32_t stream_read_size;
+    uint64_t ebsp_head_pos;
+    uint32_t max_au_length;
+    uint32_t num_undecodable;
+    uint64_t last_intra_cts;
+    lsmash_media_ts_list_t ts_list;
+} mp4sys_h264_info_t;
+
+enum
+{
+    H264_SLICE_TYPE_P    = 0,
+    H264_SLICE_TYPE_B    = 1,
+    H264_SLICE_TYPE_I    = 2,
+    H264_SLICE_TYPE_SP   = 3,
+    H264_SLICE_TYPE_SI   = 4
+} h264_slice_type;
+
+enum
+{
+    H264_PICTURE_TYPE_I           = 0,
+    H264_PICTURE_TYPE_I_P         = 1,
+    H264_PICTURE_TYPE_I_P_B       = 2,
+    H264_PICTURE_TYPE_SI          = 3,
+    H264_PICTURE_TYPE_SI_SP       = 4,
+    H264_PICTURE_TYPE_I_SI        = 5,
+    H264_PICTURE_TYPE_I_SI_P_SP   = 6,
+    H264_PICTURE_TYPE_I_SI_P_SP_B = 7,
+    H264_PICTURE_TYPE_NONE        = 8,
+} h264_picture_type;
+
+#define MP4SYS_H264_DEFAULT_BUFFER_SIZE (1<<16)
+
+static void mp4sys_remove_h264_info( mp4sys_h264_info_t *info )
+{
+    if( !info )
+        return;
+    lsmash_remove_list( info->avcC.sequenceParameterSets,   isom_remove_avcC_ps );
+    lsmash_remove_list( info->avcC.pictureParameterSets,    isom_remove_avcC_ps );
+    lsmash_remove_list( info->avcC.sequenceParameterSetExt, isom_remove_avcC_ps );
+    lsmash_bits_adhoc_cleanup( info->bits );
+    if( info->stream_buffer )
+        free( info->stream_buffer );
+    if( info->picture.au )
+        free( info->picture.au );
+    if( info->picture.incomplete_au )
+        free( info->picture.incomplete_au );
+    if( info->rbsp_buffer )
+        free( info->rbsp_buffer );
+    if( info->ts_list.timestamp )
+        free( info->ts_list.timestamp );
+    free( info );
+}
+
+static mp4sys_h264_info_t *mp4sys_create_h264_info( void )
+{
+    mp4sys_h264_info_t *info = lsmash_malloc_zero( sizeof(mp4sys_h264_info_t) );
+    if( !info )
+        return NULL;
+    info->bits = lsmash_bits_adhoc_create();
+    if( !info->bits )
+    {
+        mp4sys_remove_h264_info( info );
+        return NULL;
+    }
+    isom_avcC_t *avcC = &info->avcC;
+    avcC->type = ISOM_BOX_TYPE_AVCC;
+    avcC->sequenceParameterSets = lsmash_create_entry_list();
+    if( !avcC->sequenceParameterSets )
+    {
+        mp4sys_remove_h264_info( info );
+        return NULL;
+    }
+    avcC->pictureParameterSets = lsmash_create_entry_list();
+    if( !avcC->pictureParameterSets )
+    {
+        mp4sys_remove_h264_info( info );
+        return NULL;
+    }
+    info->buffer = malloc( 4 * MP4SYS_H264_DEFAULT_BUFFER_SIZE );
+    if( !info->buffer )
+    {
+        mp4sys_remove_h264_info( info );
+        return NULL;
+    }
+    info->buffer_size           = MP4SYS_H264_DEFAULT_BUFFER_SIZE;
+    info->stream_buffer         = info->buffer;
+    info->rbsp_buffer           = info->buffer +     info->buffer_size;
+    info->picture.au            = info->buffer + 2 * info->buffer_size;
+    info->picture.incomplete_au = info->buffer + 3 * info->buffer_size;
+    return info;
+}
+
+static void mp4sys_h264_cleanup( mp4sys_importer_t *importer )
+{
+    debug_if( importer && importer->info )
+        mp4sys_remove_h264_info( importer->info );
+}
+
+static inline uint64_t h264_get_codeNum( lsmash_bits_t *bits )
+{
+    uint32_t leadingZeroBits = 0;
+    for( int b = 0; !b; leadingZeroBits++ )
+        b = lsmash_bits_get( bits, 1 );
+    --leadingZeroBits;
+    return ((uint64_t)1 << leadingZeroBits) - 1 + lsmash_bits_get( bits, leadingZeroBits );
+}
+
+static inline uint64_t h264_decode_exp_golomb_ue( uint64_t codeNum )
+{
+    return codeNum;
+}
+
+static inline int64_t h264_decode_exp_golomb_se( uint64_t codeNum )
+{
+    if( codeNum & 1 )
+        return (int64_t)((codeNum >> 1) + 1);
+    return -1 * (int64_t)(codeNum >> 1);
+}
+
+static uint64_t h264_get_exp_golomb_ue( lsmash_bits_t *bits )
+{
+    uint64_t codeNum = h264_get_codeNum( bits );
+    return h264_decode_exp_golomb_ue( codeNum );
+}
+
+static uint64_t h264_get_exp_golomb_se( lsmash_bits_t *bits )
+{
+    uint64_t codeNum = h264_get_codeNum( bits );
+    return h264_decode_exp_golomb_se( codeNum );
+}
+
+/* Convert EBSP (Encapsulated Byte Sequence Packets) to RBSP (Raw Byte Sequence Packets). */
+static void h264_remove_emulation_prevention( uint8_t *src, uint64_t src_length, uint8_t **p_dst )
+{
+    uint8_t *src_end = src + src_length;
+    uint8_t *dst = *p_dst;
+    while( src < src_end )
+        if( ((src + 2) < src_end) && !src[0] && !src[1] && (src[2] == 0x03) )
+        {
+            *dst++ = *src++;
+            *dst++ = *src++;
+            src++;  /* Skip emulation_prevention_three_byte (0x03). */
+        }
+        else
+            *dst++ = *src++;
+    *p_dst = dst;
+}
+
+
+#define IF_INVALID_VALUE( x ) if( x )
+#define IF_EXCEED_INT32( x ) if( (x) < INT32_MIN || (x) > INT32_MAX )
+
+static int h264_check_more_rbsp_data( lsmash_bits_t *bits )
+{
+    lsmash_bs_t *bs = bits->bs;
+    if( bs->pos < bs->store )
+        return 1;       /* rbsp_trailing_bits will be placed at the next or later byte.
+                         * Note: bs->pos points at the next byte if bits->store isn't empty. */
+    if( bits->store == 0 )
+    {
+        /* No rbsp_trailing_bits is present in RBSP data. */
+        bs->error = 1;
+        return 0;
+    }
+    /* Check whether remainder of bits is identical to rbsp_trailing_bits. */
+    uint8_t remainder_bits = bits->cache & ~(~0U << bits->store);
+    uint8_t rbsp_trailing_bits = 1U << (bits->store - 1);
+    return remainder_bits != rbsp_trailing_bits;
+}
+
+static int h264_check_nalu_header( h264_nalu_header_t *nalu_header, uint8_t **p_buf_pos, int use_long_start_code )
+{
+    uint8_t *buf_pos = *p_buf_pos;
+    uint8_t forbidden_zero_bit =                              (*buf_pos >> 7) & 0x01;
+    uint8_t nal_ref_idc        = nalu_header->nal_ref_idc   = (*buf_pos >> 5) & 0x03;
+    uint8_t nal_unit_type      = nalu_header->nal_unit_type =  *buf_pos       & 0x1f;
+    nalu_header->length = 1;
+    *p_buf_pos = buf_pos + nalu_header->length;
+    if( nal_unit_type == 14 || nal_unit_type == 20 )
+        return -1;      /* We don't support yet. */
+    IF_INVALID_VALUE( forbidden_zero_bit )
+        return -1;
+    /* SPS and PPS require long start code (0x00000001).
+     * Also AU delimiter requires it too because this type of NALU shall be the first NALU of any AU if present. */
+    IF_INVALID_VALUE( !use_long_start_code && (nal_unit_type == 7 || nal_unit_type == 8 || nal_unit_type == 9) )
+        return -1;
+    if( nal_ref_idc )
+    {
+        /* nal_ref_idc shall be equal to 0 for all NALUs having nal_unit_type equal to 6, 9, 10, 11, or 12. */
+        IF_INVALID_VALUE( nal_unit_type == 6 || nal_unit_type == 9 || nal_unit_type == 10 || nal_unit_type == 11 || nal_unit_type == 12 )
+            return -1;
+    }
+    else
+        /* nal_ref_idc shall not be equal to 0 for NALUs with nal_unit_type equal to 5. */
+        IF_INVALID_VALUE( nal_unit_type == 5 )
+            return -1;
+    return 0;
+}
+
+static int h264_parse_scaling_list( lsmash_bits_t *bits, int sizeOfScalingList )
+{
+    /* scaling_list( scalingList, sizeOfScalingList, useDefaultScalingMatrixFlag ) */
+    int nextScale = 8;
+    for( int i = 0; i < sizeOfScalingList; i++ )
+    {
+        int64_t delta_scale = h264_get_exp_golomb_se( bits );
+        IF_INVALID_VALUE( delta_scale < -128 || delta_scale > 127 )
+            return -1;
+        nextScale = (nextScale + delta_scale + 256) % 256;
+        if( nextScale == 0 )
+            break;
+    }
+    return 0;
+}
+
+static int h264_parse_hrd_parameters( lsmash_bits_t *bits )
+{
+    /* hrd_parameters() */
+    uint64_t cpb_cnt_minus1 = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( cpb_cnt_minus1 > 31 )
+        return -1;
+    lsmash_bits_get( bits, 4 );     /* bit_rate_scale */
+    lsmash_bits_get( bits, 4 );     /* cpb_size_scale */
+    for( uint64_t SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ )
+    {
+        h264_get_exp_golomb_ue( bits );     /* bit_rate_value_minus1[ SchedSelIdx ] */
+        h264_get_exp_golomb_ue( bits );     /* cpb_size_value_minus1[ SchedSelIdx ] */
+        lsmash_bits_get( bits, 1 );         /* cbr_flag             [ SchedSelIdx ] */
+    }
+    lsmash_bits_get( bits, 5 );     /* initial_cpb_removal_delay_length_minus1 */
+    lsmash_bits_get( bits, 5 );     /* cpb_removal_delay_length_minus1 */
+    lsmash_bits_get( bits, 5 );     /* dpb_output_delay_length_minus1 */
+    lsmash_bits_get( bits, 5 );     /* time_offset_length */
+    return 0;
+}
+
+static int h264_parse_sps_nalu( lsmash_bits_t *bits, h264_sps_t *sps, h264_nalu_header_t *nalu_header,
+                                uint8_t *rbsp_buffer, uint8_t *ebsp, uint64_t ebsp_size )
+{
+    uint8_t *rbsp_start = rbsp_buffer;
+    h264_remove_emulation_prevention( ebsp, ebsp_size, &rbsp_buffer );
+    uint64_t rbsp_length = rbsp_buffer - rbsp_start;
+    if( lsmash_bits_import_data( bits, rbsp_start, rbsp_length ) )
+        return -1;
+    memset( sps, 0, sizeof(h264_sps_t) );
+    /* seq_parameter_set_data() */
+    sps->profile_idc = lsmash_bits_get( bits, 8 );
+    sps->constraint_set_flags = lsmash_bits_get( bits, 8 );
+    sps->level_idc = lsmash_bits_get( bits, 8 );
+    uint64_t seq_parameter_set_id = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( seq_parameter_set_id > 31 )
+        return -1;
+    sps->seq_parameter_set_id = seq_parameter_set_id;
+    if( sps->profile_idc == 100 || sps->profile_idc == 110 || sps->profile_idc == 122
+     || sps->profile_idc == 244 || sps->profile_idc == 44  || sps->profile_idc == 83
+     || sps->profile_idc == 86  || sps->profile_idc == 118 || sps->profile_idc == 128 )
+    {
+        sps->chroma_format_idc = h264_get_exp_golomb_ue( bits );
+        if( sps->chroma_format_idc == 3 )
+            sps->separate_colour_plane_flag = lsmash_bits_get( bits, 1 );
+        uint64_t bit_depth_luma_minus8 = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( bit_depth_luma_minus8 > 6 )
+            return -1;
+        uint64_t bit_depth_chroma_minus8 = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( bit_depth_chroma_minus8 > 6 )
+            return -1;
+        sps->bit_depth_luma_minus8   = bit_depth_luma_minus8;
+        sps->bit_depth_chroma_minus8 = bit_depth_chroma_minus8;
+        lsmash_bits_get( bits, 1 );         /* qpprime_y_zero_transform_bypass_flag */
+        if( lsmash_bits_get( bits, 1 ) )    /* seq_scaling_matrix_present_flag */
+        {
+            int num_loops = sps->chroma_format_idc != 3 ? 8 : 12;
+            for( int i = 0; i < num_loops; i++ )
+                if( lsmash_bits_get( bits, 1 )          /* seq_scaling_list_present_flag[i] */
+                 && h264_parse_scaling_list( bits, i < 6 ? 16 : 64 ) )
+                        return -1;
+        }
+    }
+    else
+    {
+        sps->chroma_format_idc          = 1;
+        sps->separate_colour_plane_flag = 0;
+        sps->bit_depth_luma_minus8      = 0;
+        sps->bit_depth_chroma_minus8    = 0;
+    }
+    sps->ChromaArrayType = sps->separate_colour_plane_flag ? 0 : sps->chroma_format_idc;
+    uint64_t log2_max_frame_num_minus4 = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( log2_max_frame_num_minus4 > 12 )
+        return -1;
+    sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4;
+    sps->MaxFrameNum = 1 << sps->log2_max_frame_num;
+    uint64_t pic_order_cnt_type = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( pic_order_cnt_type > 2 )
+        return -1;
+    sps->pic_order_cnt_type = pic_order_cnt_type;
+    if( sps->pic_order_cnt_type == 0 )
+    {
+        uint64_t log2_max_pic_order_cnt_lsb_minus4 = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( log2_max_pic_order_cnt_lsb_minus4 > 12 )
+            return -1;
+        sps->log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4;
+        sps->MaxPicOrderCntLsb = 1 << sps->log2_max_pic_order_cnt_lsb;
+    }
+    else if( sps->pic_order_cnt_type == 1 )
+    {
+        sps->delta_pic_order_always_zero_flag = lsmash_bits_get( bits, 1 );
+        int64_t max_value =  ((uint64_t)1 << 31) - 1;
+        int64_t min_value = -((uint64_t)1 << 31) + 1;
+        int64_t offset_for_non_ref_pic = h264_get_exp_golomb_se( bits );
+        if( offset_for_non_ref_pic < min_value || offset_for_non_ref_pic > max_value )
+            return -1;
+        sps->offset_for_non_ref_pic = offset_for_non_ref_pic;
+        int64_t offset_for_top_to_bottom_field = h264_get_exp_golomb_se( bits );
+        if( offset_for_top_to_bottom_field < min_value || offset_for_top_to_bottom_field > max_value )
+            return -1;
+        sps->offset_for_top_to_bottom_field = offset_for_top_to_bottom_field;
+        uint64_t num_ref_frames_in_pic_order_cnt_cycle = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( num_ref_frames_in_pic_order_cnt_cycle > 255 )
+            return -1;
+        sps->num_ref_frames_in_pic_order_cnt_cycle = num_ref_frames_in_pic_order_cnt_cycle;
+        sps->ExpectedDeltaPerPicOrderCntCycle = 0;
+        for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
+        {
+            int64_t offset_for_ref_frame = h264_get_exp_golomb_se( bits );
+            if( offset_for_ref_frame < min_value || offset_for_ref_frame > max_value )
+                return -1;
+            sps->offset_for_ref_frame[i] = offset_for_ref_frame;
+            sps->ExpectedDeltaPerPicOrderCntCycle += offset_for_ref_frame;
+        }
+    }
+    sps->max_num_ref_frames = h264_get_exp_golomb_ue( bits );
+    lsmash_bits_get( bits, 1 );         /* gaps_in_frame_num_value_allowed_flag */
+    uint64_t pic_width_in_mbs_minus1        = h264_get_exp_golomb_ue( bits );
+    uint64_t pic_height_in_map_units_minus1 = h264_get_exp_golomb_ue( bits );
+    sps->frame_mbs_only_flag = lsmash_bits_get( bits, 1 );
+    if( !sps->frame_mbs_only_flag )
+        lsmash_bits_get( bits, 1 );     /* mb_adaptive_frame_field_flag */
+    lsmash_bits_get( bits, 1 );         /* direct_8x8_inference_flag */
+    uint64_t PicWidthInMbs       = pic_width_in_mbs_minus1        + 1;
+    uint64_t PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1;
+    sps->PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits;
+    sps->cropped_width  = PicWidthInMbs * 16;
+    sps->cropped_height = (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits * 16;
+    if( lsmash_bits_get( bits, 1 ) )    /* frame_cropping_flag */
+    {
+        uint8_t CropUnitX;
+        uint8_t CropUnitY;
+        if( sps->ChromaArrayType == 0 )
+        {
+            CropUnitX = 1;
+            CropUnitY = 2 - sps->frame_mbs_only_flag;
+        }
+        else
+        {
+            static const int SubWidthC [] = { 0, 2, 2, 1 };
+            static const int SubHeightC[] = { 0, 2, 1, 1 };
+            CropUnitX = SubWidthC [ sps->chroma_format_idc ];
+            CropUnitY = SubHeightC[ sps->chroma_format_idc ] * (2 - sps->frame_mbs_only_flag);
+        }
+        uint64_t frame_crop_left_offset   = h264_get_exp_golomb_ue( bits );
+        uint64_t frame_crop_right_offset  = h264_get_exp_golomb_ue( bits );
+        uint64_t frame_crop_top_offset    = h264_get_exp_golomb_ue( bits );
+        uint64_t frame_crop_bottom_offset = h264_get_exp_golomb_ue( bits );
+        sps->cropped_width  -= (frame_crop_left_offset + frame_crop_right_offset)  * CropUnitX;
+        sps->cropped_height -= (frame_crop_top_offset  + frame_crop_bottom_offset) * CropUnitY;
+    }
+    if( lsmash_bits_get( bits, 1 ) )    /* vui_parameters_present_flag */
+    {
+        /* vui_parameters() */
+        if( lsmash_bits_get( bits, 1 ) )        /* aspect_ratio_info_present_flag */
+        {
+            uint8_t aspect_ratio_idc = lsmash_bits_get( bits, 8 );
+            if( aspect_ratio_idc == 255 )
+            {
+                /* Extended_SAR */
+                sps->vui.sar_width  = lsmash_bits_get( bits, 16 );
+                sps->vui.sar_height = lsmash_bits_get( bits, 16 );
+            }
+            else
+            {
+                static const struct
+                {
+                    uint16_t sar_width;
+                    uint16_t sar_height;
+                } pre_defined_sar[]
+                    = {
+                        {  0,  0 }, {  1,  1 }, { 12, 11 }, {  10, 11 }, { 16, 11 },
+                        { 40, 33 }, { 24, 11 }, { 20, 11 }, {  32, 11 }, { 80, 33 },
+                        { 18, 11 }, { 15, 11 }, { 64, 33 }, { 160, 99 }, {  4,  3 },
+                        {  3,  2 }, {  2,  1 }
+                      };
+                if( aspect_ratio_idc < (sizeof(pre_defined_sar) / sizeof(pre_defined_sar[0])) )
+                {
+                    sps->vui.sar_width  = pre_defined_sar[ aspect_ratio_idc ].sar_width;
+                    sps->vui.sar_height = pre_defined_sar[ aspect_ratio_idc ].sar_height;
+                }
+                else
+                {
+                    /* Behavior when unknown aspect_ratio_idc is detected is not specified in the specification. */
+                    sps->vui.sar_width  = 0;
+                    sps->vui.sar_height = 0;
+                }
+            }
+        }
+        if( lsmash_bits_get( bits, 1 ) )        /* overscan_info_present_flag */
+            lsmash_bits_get( bits, 1 );         /* overscan_appropriate_flag */
+        if( lsmash_bits_get( bits, 1 ) )        /* video_signal_type_present_flag */
+        {
+            lsmash_bits_get( bits, 3 );         /* video_format */
+            sps->vui.video_full_range_flag = lsmash_bits_get( bits, 1 );
+            if( lsmash_bits_get( bits, 1 ) )    /* colour_description_present_flag */
+            {
+                sps->vui.colour_primaries         = lsmash_bits_get( bits, 8 );
+                sps->vui.transfer_characteristics = lsmash_bits_get( bits, 8 );
+                sps->vui.matrix_coefficients      = lsmash_bits_get( bits, 8 );
+            }
+        }
+        if( lsmash_bits_get( bits, 1 ) )        /* chroma_loc_info_present_flag */
+        {
+            h264_get_exp_golomb_ue( bits );     /* chroma_sample_loc_type_top_field */
+            h264_get_exp_golomb_ue( bits );     /* chroma_sample_loc_type_bottom_field */
+        }
+        if( lsmash_bits_get( bits, 1 ) )        /* timing_info_present_flag */
+        {
+            sps->vui.num_units_in_tick     = lsmash_bits_get( bits, 32 );
+            sps->vui.time_scale            = lsmash_bits_get( bits, 32 );
+            sps->vui.fixed_frame_rate_flag = lsmash_bits_get( bits, 1 );
+        }
+        int nal_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 );
+        if( nal_hrd_parameters_present_flag
+         && h264_parse_hrd_parameters( bits ) )
+            return -1;
+        int vcl_hrd_parameters_present_flag = lsmash_bits_get( bits, 1 );
+        if( vcl_hrd_parameters_present_flag
+         && h264_parse_hrd_parameters( bits ) )
+            return -1;
+        if( nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag )
+        {
+            sps->hrd_present = 1;
+            lsmash_bits_get( bits, 1 );         /* low_delay_hrd_flag */
+        }
+        lsmash_bits_get( bits, 1 );             /* pic_struct_present_flag */
+        if( lsmash_bits_get( bits, 1 ) )        /* bitstream_restriction_flag */
+        {
+            lsmash_bits_get( bits, 1 );         /* motion_vectors_over_pic_boundaries_flag */
+            h264_get_exp_golomb_ue( bits );     /* max_bytes_per_pic_denom */
+            h264_get_exp_golomb_ue( bits );     /* max_bits_per_mb_denom */
+            h264_get_exp_golomb_ue( bits );     /* log2_max_mv_length_horizontal */
+            h264_get_exp_golomb_ue( bits );     /* log2_max_mv_length_vertical */
+            h264_get_exp_golomb_ue( bits );     /* num_reorder_frames */
+            h264_get_exp_golomb_ue( bits );     /* max_dec_frame_buffering */
+        }
+    }
+    else
+    {
+        sps->vui.video_full_range_flag = 0;
+        sps->vui.num_units_in_tick     = 1;
+        sps->vui.time_scale            = 50;
+        sps->vui.fixed_frame_rate_flag = 0;
+    }
+    /* rbsp_trailing_bits() */
+    IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) )     /* rbsp_stop_one_bit */
+        return -1;
+    lsmash_bits_empty( bits );
+    return bits->bs->error ? -1 : 0;
+}
+
+static int h264_parse_pps_nalu( lsmash_bits_t *bits, h264_sps_t *sps, h264_pps_t *pps, h264_nalu_header_t *nalu_header,
+                                uint8_t *rbsp_buffer, uint8_t *ebsp, uint64_t ebsp_size )
+{
+    if( !sps )
+        return -1;
+    uint8_t *rbsp_start = rbsp_buffer;
+    h264_remove_emulation_prevention( ebsp, ebsp_size, &rbsp_buffer );
+    uint64_t rbsp_length = rbsp_buffer - rbsp_start;
+    if( lsmash_bits_import_data( bits, rbsp_start, rbsp_length ) )
+        return -1;
+    memset( pps, 0, sizeof(h264_pps_t) );
+    /* pic_parameter_set_rbsp */
+    uint64_t pic_parameter_set_id = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( pic_parameter_set_id > 255 )
+        return -1;
+    pps->pic_parameter_set_id = pic_parameter_set_id;
+    uint64_t seq_parameter_set_id = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( seq_parameter_set_id > 31 )
+        return -1;
+    pps->seq_parameter_set_id = seq_parameter_set_id;
+    pps->entropy_coding_mode_flag = lsmash_bits_get( bits, 1 );
+    pps->bottom_field_pic_order_in_frame_present_flag = lsmash_bits_get( bits, 1 );
+    uint64_t num_slice_groups_minus1 = h264_get_exp_golomb_ue( bits );
+    if( num_slice_groups_minus1 )        /* num_slice_groups_minus1 */
+    {
+        uint64_t slice_group_map_type = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( slice_group_map_type > 6 )
+            return -1;
+        if( slice_group_map_type == 0 )
+            for( uint64_t iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ )
+                h264_get_exp_golomb_ue( bits );     /* run_length_minus1[ iGroup ] */
+        else if( slice_group_map_type == 2 )
+            for( uint64_t iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ )
+            {
+                h264_get_exp_golomb_ue( bits );     /* top_left    [ iGroup ] */
+                h264_get_exp_golomb_ue( bits );     /* bottom_right[ iGroup ] */
+            }
+        else if( slice_group_map_type == 3
+              || slice_group_map_type == 4
+              || slice_group_map_type == 5 )
+        {
+            lsmash_bits_get( bits, 1 );         /* slice_group_change_direction_flag */
+            uint64_t slice_group_change_rate_minus1 = h264_get_exp_golomb_ue( bits );
+            IF_INVALID_VALUE( slice_group_change_rate_minus1 > (sps->PicSizeInMapUnits - 1) )
+                return -1;
+            pps->SliceGroupChangeRate = slice_group_change_rate_minus1 + 1;
+        }
+        else if( slice_group_map_type == 6 )
+        {
+            uint64_t pic_size_in_map_units_minus1 = h264_get_exp_golomb_ue( bits );
+            /* slice_group_id_length = ceil( log2( num_slice_groups_minus1 + 1 ) ); */
+            uint64_t slice_group_id_length;
+            for( slice_group_id_length = 1; num_slice_groups_minus1 >> slice_group_id_length; slice_group_id_length++ );
+            for( uint64_t i = 0; i <= pic_size_in_map_units_minus1; i++ )
+                /* slice_group_id */
+                IF_INVALID_VALUE( lsmash_bits_get( bits, slice_group_id_length ) > num_slice_groups_minus1 )
+                    return -1;
+        }
+    }
+    h264_get_exp_golomb_ue( bits );     /* num_ref_idx_l0_default_active_minus1 */
+    h264_get_exp_golomb_ue( bits );     /* num_ref_idx_l1_default_active_minus1 */
+    pps->weighted_pred_flag  = lsmash_bits_get( bits, 1 );
+    pps->weighted_bipred_idc = lsmash_bits_get( bits, 2 );
+    h264_get_exp_golomb_se( bits );     /* pic_init_qp_minus26 */
+    h264_get_exp_golomb_se( bits );     /* pic_init_qs_minus26 */
+    h264_get_exp_golomb_se( bits );     /* chroma_qp_index_offset */
+    pps->deblocking_filter_control_present_flag = lsmash_bits_get( bits, 1 );
+    lsmash_bits_get( bits, 1 );         /* constrained_intra_pred_flag */
+    pps->redundant_pic_cnt_present_flag = lsmash_bits_get( bits, 1 );
+    if( h264_check_more_rbsp_data( bits ) )
+    {
+        int transform_8x8_mode_flag = lsmash_bits_get( bits, 1 );
+        if( lsmash_bits_get( bits, 1 ) )        /* pic_scaling_matrix_present_flag */
+        {
+            int num_loops = 6 + (sps->chroma_format_idc != 3 ? 2 : 6) * transform_8x8_mode_flag;
+            for( int i = 0; i < num_loops; i++ )
+                if( lsmash_bits_get( bits, 1 )          /* pic_scaling_list_present_flag[i] */
+                 && h264_parse_scaling_list( bits, i < 6 ? 16 : 64 ) )
+                        return -1;
+        }
+        h264_get_exp_golomb_se( bits );         /* second_chroma_qp_index_offset */
+    }
+    /* rbsp_trailing_bits() */
+    IF_INVALID_VALUE( !lsmash_bits_get( bits, 1 ) )     /* rbsp_stop_one_bit */
+        return -1;
+    lsmash_bits_empty( bits );
+    return bits->bs->error ? -1 : 0;
+}
+
+static int h264_parse_sei_nalu( lsmash_bits_t *bits, h264_sei_t *sei, h264_nalu_header_t *nalu_header,
+                                uint8_t *rbsp_buffer, uint8_t *ebsp, uint64_t ebsp_size )
+{
+    uint8_t *rbsp_start = rbsp_buffer;
+    h264_remove_emulation_prevention( ebsp, ebsp_size, &rbsp_buffer );
+    uint64_t rbsp_length = rbsp_buffer - rbsp_start;
+    if( lsmash_bits_import_data( bits, rbsp_start, rbsp_length ) )
+        return -1;
+    uint64_t rbsp_pos = 0;
+    do
+    {
+        /* sei_message() */
+        uint32_t payloadType = 0;
+        for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) )
+        {
+            /* 0xff     : ff_byte
+             * otherwise: last_payload_type_byte */
+            payloadType += temp;
+            ++rbsp_pos;
+            if( temp != 0xff )
+                break;
+        }
+        uint32_t payloadSize = 0;
+        for( uint8_t temp = lsmash_bits_get( bits, 8 ); ; temp = lsmash_bits_get( bits, 8 ) )
+        {
+            /* 0xff     : ff_byte
+             * otherwise: last_payload_size_byte */
+            payloadSize += temp;
+            ++rbsp_pos;
+            if( temp != 0xff )
+                break;
+        }
+        if( payloadType == 3 )
+        {
+            /* filler_payload
+             * AVC file format is forbidden to contain this. */
+            return -1;
+        }
+        else if( payloadType == 6 )
+        {
+            /* recovery_point */
+            sei->recovery_frame_cnt = h264_get_exp_golomb_ue( bits );
+            sei->random_accessible = 1;
+            lsmash_bits_get( bits, 1 );     /* exact_match_flag */
+            lsmash_bits_get( bits, 1 );     /* broken_link_flag */
+            lsmash_bits_get( bits, 2 );     /* changing_slice_group_idc */
+        }
+        else
+            lsmash_bits_get( bits, payloadSize * 8 );
+        lsmash_bits_get_align( bits );
+        rbsp_pos += payloadSize;
+    } while( *(rbsp_start + rbsp_pos) != 0x80 );        /* All SEI messages are byte aligned at their end.
+                                                         * Therefore, 0x80 shall be rbsp_trailing_bits(). */
+    lsmash_bits_empty( bits );
+    return bits->bs->error ? -1 : 0;
+}
+
+static int h264_parse_slice_header( lsmash_bits_t *bits, h264_sps_t *sps, h264_pps_t *pps,
+                                    h264_slice_info_t *slice, h264_nalu_header_t *nalu_header,
+                                    uint8_t *rbsp_buffer, uint8_t *ebsp, uint64_t ebsp_size )
+{
+    if( !sps || !pps )
+        return -1;      /* This would occur when the stream starts from non-IDR picture. */
+    uint8_t *rbsp_start = rbsp_buffer;
+    h264_remove_emulation_prevention( ebsp, ebsp_size, &rbsp_buffer );
+    uint64_t rbsp_length = rbsp_buffer - rbsp_start;
+    if( lsmash_bits_import_data( bits, rbsp_start, rbsp_length ) )
+        return -1;
+    memset( slice, 0, sizeof(h264_slice_info_t) );
+    slice->pic_order_cnt_type = sps->pic_order_cnt_type;
+    slice->nal_ref_idc = nalu_header->nal_ref_idc;
+    slice->IdrPicFlag = (nalu_header->nal_unit_type == 5);
+    /* slice_header() */
+    h264_get_exp_golomb_ue( bits );     /* first_mb_in_slice */
+    uint8_t slice_type = slice->type = h264_get_exp_golomb_ue( bits );
+    IF_INVALID_VALUE( (uint64_t)slice->type > 9 )
+        return -1;
+    if( slice_type > 4 )
+        slice_type = slice->type -= 5;
+    IF_INVALID_VALUE( (slice->IdrPicFlag || sps->max_num_ref_frames == 0) && slice_type != 2 && slice_type != 4 )
+        return -1;
+    slice->pic_parameter_set_id = h264_get_exp_golomb_ue( bits );
+    if( sps->separate_colour_plane_flag )
+        lsmash_bits_get( bits, 2 );     /* colour_plane_id */
+    uint64_t frame_num = lsmash_bits_get( bits, sps->log2_max_frame_num );
+    IF_INVALID_VALUE( frame_num >= (1 << sps->log2_max_frame_num) || (slice->IdrPicFlag && frame_num) )
+        return -1;
+    slice->frame_num = frame_num;
+    if( !sps->frame_mbs_only_flag )
+    {
+        slice->field_pic_flag = lsmash_bits_get( bits, 1 );
+        if( slice->field_pic_flag )
+            slice->bottom_field_flag = lsmash_bits_get( bits, 1 );
+    }
+    if( slice->IdrPicFlag )
+    {
+        uint64_t idr_pic_id = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( idr_pic_id > 65535 )
+            return -1;
+        slice->idr_pic_id = idr_pic_id;
+    }
+    if( sps->pic_order_cnt_type == 0 )
+    {
+        uint64_t pic_order_cnt_lsb = lsmash_bits_get( bits, sps->log2_max_pic_order_cnt_lsb );
+        IF_INVALID_VALUE( pic_order_cnt_lsb >= sps->MaxPicOrderCntLsb )
+            return -1;
+        slice->pic_order_cnt_lsb = pic_order_cnt_lsb;
+        if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag )
+            slice->delta_pic_order_cnt_bottom = h264_get_exp_golomb_se( bits );
+    }
+    else if( sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag )
+    {
+        slice->delta_pic_order_cnt[0] = h264_get_exp_golomb_se( bits );
+        if( pps->bottom_field_pic_order_in_frame_present_flag && !slice->field_pic_flag )
+            slice->delta_pic_order_cnt[1] = h264_get_exp_golomb_se( bits );
+    }
+    if( pps->redundant_pic_cnt_present_flag )
+    {
+        uint64_t redundant_pic_cnt = h264_get_exp_golomb_ue( bits );
+        IF_INVALID_VALUE( redundant_pic_cnt > 127 )
+            return -1;
+        slice->has_redundancy = !!redundant_pic_cnt;
+    }
+    if( slice_type == H264_SLICE_TYPE_B )
+        lsmash_bits_get( bits, 1 );
+    uint64_t num_ref_idx_l0_active_minus1 = 0;
+    uint64_t num_ref_idx_l1_active_minus1 = 0;
+    if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_B )
+    {
+        if( lsmash_bits_get( bits, 1 ) )            /* num_ref_idx_active_override_flag */
+        {
+            num_ref_idx_l0_active_minus1 = h264_get_exp_golomb_ue( bits );
+            IF_INVALID_VALUE( num_ref_idx_l0_active_minus1 > 31 )
+                return -1;
+            if( slice_type == H264_SLICE_TYPE_B )
+            {
+                num_ref_idx_l1_active_minus1 = h264_get_exp_golomb_ue( bits );
+                IF_INVALID_VALUE( num_ref_idx_l1_active_minus1 > 31 )
+                    return -1;
+            }
+        }
+    }
+    if( nalu_header->nal_unit_type == 20 )
+    {
+        return -1;      /* No support of MVC yet */
+#if 0
+        /* ref_pic_list_mvc_modification() */
+        if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP )
+        {
+            if( lsmash_bits_get( bits, 1 ) )        /* (S)P: ref_pic_list_modification_flag_l0
+                                                     *    B: ref_pic_list_modification_flag_l1 */
+            {
+                uint64_t modification_of_pic_nums_idc;
+                do
+                {
+                    modification_of_pic_nums_idc = h264_get_exp_golomb_ue( bits );
+#if 0
+                    if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 )
+                        h264_get_exp_golomb_ue( bits );     /* abs_diff_pic_num_minus1 */
+                    else if( modification_of_pic_nums_idc == 2 )
+                        h264_get_exp_golomb_ue( bits );     /* long_term_pic_num */
+                    else if( modification_of_pic_nums_idc == 4 || modification_of_pic_nums_idc == 5 )
+                        h264_get_exp_golomb_ue( bits );     /* abs_diff_view_idx_minus1 */
+#else
+                    if( modification_of_pic_nums_idc != 3 )
+                        h264_get_exp_golomb_ue( bits );     /* abs_diff_pic_num_minus1, long_term_pic_num or abs_diff_view_idx_minus1 */
+#endif
+                } while( modification_of_pic_nums_idc != 3 );
+        }
+#endif
+    }
+    else
+    {
+        /* ref_pic_list_modification() */
+        if( slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_B || slice_type == H264_SLICE_TYPE_SP )
+        {
+            if( lsmash_bits_get( bits, 1 ) )        /* (S)P: ref_pic_list_modification_flag_l0
+                                                     *    B: ref_pic_list_modification_flag_l1 */
+            {
+                uint64_t modification_of_pic_nums_idc;
+                do
+                {
+                    modification_of_pic_nums_idc = h264_get_exp_golomb_ue( bits );
+#if 0
+                    if( modification_of_pic_nums_idc == 0 || modification_of_pic_nums_idc == 1 )
+                        h264_get_exp_golomb_ue( bits );     /* abs_diff_pic_num_minus1 */
+                    else if( modification_of_pic_nums_idc == 2 )
+                        h264_get_exp_golomb_ue( bits );     /* long_term_pic_num */
+#else
+                    if( modification_of_pic_nums_idc != 3 )
+                        h264_get_exp_golomb_ue( bits );     /* abs_diff_pic_num_minus1 or long_term_pic_num */
+#endif
+                } while( modification_of_pic_nums_idc != 3 );
+            }
+        }
+    }
+    if( (pps->weighted_pred_flag && (slice_type == H264_SLICE_TYPE_P || slice_type == H264_SLICE_TYPE_SP))
+     || (pps->weighted_bipred_idc == 1 && slice_type == H264_SLICE_TYPE_B) )
+    {
+        /* pred_weight_table() */
+        h264_get_exp_golomb_ue( bits );         /* luma_log2_weight_denom */
+        if( sps->ChromaArrayType )
+            h264_get_exp_golomb_ue( bits );     /* chroma_log2_weight_denom */
+        for( uint8_t i = 0; i <= num_ref_idx_l0_active_minus1; i++ )
+        {
+            if( lsmash_bits_get( bits, 1 ) )    /* luma_weight_l0_flag */
+            {
+                h264_get_exp_golomb_se( bits );     /* luma_weight_l0[i] */
+                h264_get_exp_golomb_se( bits );     /* luma_offset_l0[i] */
+            }
+            if( sps->ChromaArrayType
+             && lsmash_bits_get( bits, 1 )      /* chroma_weight_l0_flag */ )
+                for( int j = 0; j < 2; j++ )
+                {
+                    h264_get_exp_golomb_se( bits );     /* chroma_weight_l0[i][j]*/
+                    h264_get_exp_golomb_se( bits );     /* chroma_offset_l0[i][j] */
+                }
+        }
+        if( slice_type == H264_SLICE_TYPE_B )
+            for( uint8_t i = 0; i <= num_ref_idx_l1_active_minus1; i++ )
+            {
+                if( lsmash_bits_get( bits, 1 ) )    /* luma_weight_l1_flag */
+                {
+                    h264_get_exp_golomb_se( bits );     /* luma_weight_l1[i] */
+                    h264_get_exp_golomb_se( bits );     /* luma_offset_l1[i] */
+                }
+                if( sps->ChromaArrayType
+                 && lsmash_bits_get( bits, 1 )      /* chroma_weight_l1_flag */ )
+                    for( int j = 0; j < 2; j++ )
+                    {
+                        h264_get_exp_golomb_se( bits );     /* chroma_weight_l1[i][j]*/
+                        h264_get_exp_golomb_se( bits );     /* chroma_offset_l1[i][j] */
+                    }
+            }
+    }
+    if( !nalu_header->nal_ref_idc )
+    {
+        /* dec_ref_pic_marking() */
+        if( slice->IdrPicFlag )
+        {
+            lsmash_bits_get( bits, 1 );     /* no_output_of_prior_pics_flag */
+            lsmash_bits_get( bits, 1 );     /* long_term_reference_flag */
+        }
+        else if( lsmash_bits_get( bits, 1 ) )       /* adaptive_ref_pic_marking_mode_flag */
+        {
+            uint64_t memory_management_control_operation;
+            do
+            {
+                memory_management_control_operation = h264_get_exp_golomb_ue( bits );
+                if( memory_management_control_operation )
+                {
+                    if( memory_management_control_operation == 5 )
+                        slice->has_mmco5 = 1;
+                    h264_get_exp_golomb_ue( bits );
+                }
+            } while( memory_management_control_operation );
+        }
+    }
+#if 0   /* We needn't read more. */
+    if( pps->entropy_coding_mode_flag && slice_type != H264_SLICE_TYPE_I && slice_type != H264_SLICE_TYPE_SI )
+        h264_get_exp_golomb_ue( bits );     /* cabac_init_idc */
+    h264_get_exp_golomb_se( bits );         /* slice_qp_delta */
+    if( slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_SI )
+    {
+        if( slice_type == H264_SLICE_TYPE_SP )
+            lsmash_bits_get( bits, 1 );     /* sp_for_switch_flag */
+        h264_get_exp_golomb_se( bits );     /* slice_qs_delta */
+    }
+    if( pps->deblocking_filter_control_present_flag
+     && h264_get_exp_golomb_ue( bits ) != 1 /* disable_deblocking_filter_idc */ )
+    {
+        int64_t slice_alpha_c0_offset_div2 = h264_get_exp_golomb_se( bits );
+        IF_INVALID_VALUE( slice_alpha_c0_offset_div2 < -6 || slice_alpha_c0_offset_div2 > 6 )
+            return -1;
+        int64_t slice_beta_offset_div2     = h264_get_exp_golomb_se( bits );
+        IF_INVALID_VALUE( slice_beta_offset_div2     < -6 || slice_beta_offset_div2     > 6 )
+            return -1;
+    }
+    if( pps->num_slice_groups_minus1
+    && (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5) )
+    {
+        uint64_t slice_group_change_cycle_length = ceil( log( sps->PicSizeInMapUnits / pps->SliceGroupChangeRate + 1 ) / 0.693147180559945 );
+        uint64_t slice_group_change_cycle = lsmash_bits_get( bits, slice_group_change_cycle_length );
+        IF_INVALID_VALUE( slice_group_change_cycle > (uint64_t)ceil( sps->PicSizeInMapUnits / pps->SliceGroupChangeRate ) )
+            return -1;
+    }
+#endif
+    lsmash_bits_empty( bits );
+    return bits->bs->error ? -1 : 0;
+}
+
+static int h264_calculate_poc( h264_sps_t *sps, h264_picture_info_t *picture, h264_picture_info_t *prev_picture )
+{
+    int64_t TopFieldOrderCnt    = 0;
+    int64_t BottomFieldOrderCnt = 0;
+    if( sps->pic_order_cnt_type == 0 )
+    {
+        int32_t prevPicOrderCntMsb;
+        int32_t prevPicOrderCntLsb;
+        if( picture->idr )
+        {
+            prevPicOrderCntMsb = 0;
+            prevPicOrderCntLsb = 0;
+        }
+        else if( prev_picture->ref_pic_has_mmco5 )
+        {
+            prevPicOrderCntMsb = 0;
+            prevPicOrderCntLsb = prev_picture->ref_pic_bottom_field_flag ? 0 : prev_picture->ref_pic_TopFieldOrderCnt;
+        }
+        else
+        {
+            prevPicOrderCntMsb = prev_picture->ref_pic_PicOrderCntMsb;
+            prevPicOrderCntLsb = prev_picture->ref_pic_PicOrderCntLsb;
+        }
+        int64_t PicOrderCntMsb;
+        int32_t pic_order_cnt_lsb = picture->pic_order_cnt_lsb;
+        uint64_t MaxPicOrderCntLsb = sps->MaxPicOrderCntLsb;
+        if( (pic_order_cnt_lsb < prevPicOrderCntLsb)
+         && ((prevPicOrderCntLsb - pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2)) )
+            PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb;
+        else if( (pic_order_cnt_lsb > prevPicOrderCntLsb)
+         && ((pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2)) )
+            PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb;
+        else
+            PicOrderCntMsb = prevPicOrderCntMsb;
+        IF_EXCEED_INT32( PicOrderCntMsb )
+            return -1;
+        if( !picture->field_pic_flag )
+        {
+            TopFieldOrderCnt    = PicOrderCntMsb + pic_order_cnt_lsb;
+            BottomFieldOrderCnt = TopFieldOrderCnt + picture->delta_pic_order_cnt_bottom;
+        }
+        else if( picture->bottom_field_flag )
+            BottomFieldOrderCnt = PicOrderCntMsb + pic_order_cnt_lsb;
+        else
+            TopFieldOrderCnt    = PicOrderCntMsb + pic_order_cnt_lsb;
+        IF_EXCEED_INT32( TopFieldOrderCnt )
+            return -1;
+        IF_EXCEED_INT32( BottomFieldOrderCnt )
+            return -1;
+#if 0
+        fprintf( stderr, "PictureOrderCount\n" );
+        fprintf( stderr, "    prevPicOrderCntMsb: %"PRId32"\n", prevPicOrderCntMsb );
+        fprintf( stderr, "    prevPicOrderCntLsb: %"PRId32"\n", prevPicOrderCntLsb );
+        fprintf( stderr, "    PicOrderCntMsb: %"PRId64"\n", PicOrderCntMsb );
+        fprintf( stderr, "    pic_order_cnt_lsb: %"PRId32"\n", pic_order_cnt_lsb );
+        fprintf( stderr, "    MaxPicOrderCntLsb: %"PRIu64"\n", MaxPicOrderCntLsb );
+        fprintf( stderr, "    TopFieldOrderCnt: %"PRId64"\n", TopFieldOrderCnt );
+        fprintf( stderr, "    BottomFieldOrderCnt: %"PRId64"\n", BottomFieldOrderCnt );
+#endif
+        if( !picture->disposable )
+        {
+            picture->ref_pic_has_mmco5         = picture->has_mmco5;
+            picture->ref_pic_bottom_field_flag = picture->bottom_field_flag;
+            picture->ref_pic_TopFieldOrderCnt  = TopFieldOrderCnt;
+            picture->ref_pic_PicOrderCntMsb    = PicOrderCntMsb;
+            picture->ref_pic_PicOrderCntLsb    = pic_order_cnt_lsb;
+        }
+    }
+    else if( sps->pic_order_cnt_type == 1 )
+    {
+        uint32_t frame_num = picture->frame_num;
+        uint32_t prevFrameNum = prev_picture->frame_num;
+        uint32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset;
+        uint64_t FrameNumOffset = picture->idr ? 0 : prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0);
+        IF_INVALID_VALUE( FrameNumOffset > INT32_MAX )
+            return -1;
+        uint64_t absFrameNum;
+        int64_t expectedPicOrderCnt;
+        if( sps->num_ref_frames_in_pic_order_cnt_cycle )
+        {
+            absFrameNum  = FrameNumOffset + frame_num;
+            absFrameNum -= picture->disposable && absFrameNum > 0;
+            if( absFrameNum )
+            {
+                uint64_t picOrderCntCycleCnt       = (absFrameNum - 1) / sps->num_ref_frames_in_pic_order_cnt_cycle;
+                uint8_t frameNumInPicOrderCntCycle = (absFrameNum - 1) % sps->num_ref_frames_in_pic_order_cnt_cycle;
+                expectedPicOrderCnt = picOrderCntCycleCnt * sps->ExpectedDeltaPerPicOrderCntCycle;
+                for( uint8_t i = 0; i <= frameNumInPicOrderCntCycle; i++ )
+                    expectedPicOrderCnt += sps->offset_for_ref_frame[i];
+            }
+            else
+                expectedPicOrderCnt = 0;
+        }
+        else
+        {
+            absFrameNum = 0;
+            expectedPicOrderCnt = 0;
+        }
+        if( picture->disposable )
+            expectedPicOrderCnt += sps->offset_for_non_ref_pic;
+        if( !picture->field_pic_flag )
+        {
+            TopFieldOrderCnt    = expectedPicOrderCnt + picture->delta_pic_order_cnt[0];
+            BottomFieldOrderCnt = TopFieldOrderCnt    + sps->offset_for_top_to_bottom_field + picture->delta_pic_order_cnt[1];
+        }
+        else if( picture->bottom_field_flag )
+            BottomFieldOrderCnt = expectedPicOrderCnt + sps->offset_for_top_to_bottom_field + picture->delta_pic_order_cnt[0];
+        else
+            TopFieldOrderCnt    = expectedPicOrderCnt + picture->delta_pic_order_cnt[0];
+        IF_EXCEED_INT32( TopFieldOrderCnt )
+            return -1;
+        IF_EXCEED_INT32( BottomFieldOrderCnt )
+            return -1;
+    }
+    else if( sps->pic_order_cnt_type == 2 )
+    {
+        uint32_t frame_num = picture->frame_num;
+        uint32_t prevFrameNum = prev_picture->frame_num;
+        int32_t prevFrameNumOffset = prev_picture->has_mmco5 ? 0 : prev_picture->FrameNumOffset;
+        int64_t FrameNumOffset;
+        int64_t tempPicOrderCnt;
+        if( picture->idr )
+        {
+            FrameNumOffset  = 0;
+            tempPicOrderCnt = 0;
+        }
+        else
+        {
+            FrameNumOffset  = prevFrameNumOffset + (prevFrameNum > frame_num ? sps->MaxFrameNum : 0);
+            tempPicOrderCnt = 2 * (FrameNumOffset + frame_num) - picture->disposable;
+        }
+        IF_EXCEED_INT32( FrameNumOffset )
+            return -1;
+        if( !picture->field_pic_flag )
+        {
+            TopFieldOrderCnt    = tempPicOrderCnt;
+            BottomFieldOrderCnt = tempPicOrderCnt;
+        }
+        else if( picture->bottom_field_flag )
+            BottomFieldOrderCnt = tempPicOrderCnt;
+        else
+            TopFieldOrderCnt    = tempPicOrderCnt;
+        IF_EXCEED_INT32( TopFieldOrderCnt )
+            return -1;
+        IF_EXCEED_INT32( BottomFieldOrderCnt )
+            return -1;
+        picture->FrameNumOffset = FrameNumOffset;
+    }
+    if( !picture->field_pic_flag )
+        picture->PicOrderCnt = LSMASH_MIN( TopFieldOrderCnt, BottomFieldOrderCnt );
+    else
+        picture->PicOrderCnt = picture->bottom_field_flag ? BottomFieldOrderCnt : TopFieldOrderCnt;
+#if 0
+    fprintf( stderr, "    POC: %"PRId32"\n", picture->PicOrderCnt );
+#endif
+    return 0;
+}
+
+static inline void h264_compare_parameter_set( lsmash_entry_list_t *parameter_sets, uint8_t *ps_nalu, uint16_t ps_nalu_length, int *same_ps )
+{
+    if( !parameter_sets->head )
+        return;
+    isom_avcC_ps_entry_t *ps = (isom_avcC_ps_entry_t *)parameter_sets->head->data;
+    if( ps && (ps->parameterSetLength == ps_nalu_length) )
+        *same_ps = !memcmp( ps->parameterSetNALUnit, ps_nalu, ps_nalu_length );
+}
+
+static lsmash_video_summary_t *h264_create_summary( mp4sys_h264_info_t *info, uint8_t *rbsp_buffer,
+                                                    h264_nalu_header_t *sps_nalu_header,
+                                                    uint8_t *sps_nalu, uint16_t sps_nalu_length,
+                                                    h264_nalu_header_t *pps_nalu_header,
+                                                    uint8_t *pps_nalu, uint16_t pps_nalu_length,
+                                                    int probe )
+{
+    assert( info );
+    isom_avcC_t *avcC = &info->avcC;
+    int same_sps = 0;
+    int same_pps = 0;
+    if( sps_nalu )
+    {
+        h264_compare_parameter_set( avcC->sequenceParameterSets, sps_nalu, sps_nalu_length, &same_sps );
+        if( !same_sps )
+        {
+            if( h264_parse_sps_nalu( info->bits, &info->sps, sps_nalu_header, rbsp_buffer,
+                                     sps_nalu + sps_nalu_header->length, sps_nalu_length - sps_nalu_header->length ) )
+                return NULL;
+            if( !probe || !info->sps.present )
+            {
+                h264_sps_t *sps = &info->sps;
+                avcC->configurationVersion       = 1;
+                avcC->AVCProfileIndication       = sps->profile_idc;
+                avcC->profile_compatibility      = sps->constraint_set_flags;
+                avcC->AVCLevelIndication         = sps->level_idc;
+                avcC->lengthSizeMinusOne         = 3;       /* We always use 4 bytes length. */
+                avcC->numOfSequenceParameterSets = 1;
+                avcC->chroma_format              = sps->chroma_format_idc;
+                avcC->bit_depth_luma_minus8      = sps->bit_depth_luma_minus8;
+                avcC->bit_depth_chroma_minus8    = sps->bit_depth_chroma_minus8;
+                lsmash_remove_entries( avcC->sequenceParameterSets, isom_remove_avcC_ps );
+                isom_avcC_ps_entry_t *ps = malloc( sizeof(isom_avcC_ps_entry_t) );
+                if( !ps )
+                    return NULL;
+                ps->parameterSetNALUnit = lsmash_memdup( sps_nalu, sps_nalu_length );
+                if( !ps->parameterSetNALUnit )
+                {
+                    free( ps );
+                    return NULL;
+                }
+                ps->parameterSetLength = sps_nalu_length;
+                if( lsmash_add_entry( avcC->sequenceParameterSets, ps ) )
+                {
+                    free( ps->parameterSetNALUnit );
+                    free( ps );
+                    return NULL;
+                }
+                info->sps.present = 1;
+            }
+        }
+    }
+    if( pps_nalu )
+    {
+        h264_compare_parameter_set( avcC->pictureParameterSets, pps_nalu, pps_nalu_length, &same_pps );
+        if( !same_pps )
+        {
+            if( h264_parse_pps_nalu( info->bits, &info->sps, &info->pps, pps_nalu_header, rbsp_buffer,
+                                     pps_nalu + pps_nalu_header->length, pps_nalu_length - pps_nalu_header->length ) )
+                return NULL;
+            if( !probe || !info->pps.present )
+            {
+                avcC->numOfPictureParameterSets = 1;
+                lsmash_remove_entries( avcC->pictureParameterSets, isom_remove_avcC_ps );
+                isom_avcC_ps_entry_t *ps = malloc( sizeof(isom_avcC_ps_entry_t) );
+                if( !ps )
+                    return NULL;
+                ps->parameterSetNALUnit = lsmash_memdup( pps_nalu, pps_nalu_length );
+                if( !ps->parameterSetNALUnit )
+                {
+                    free( ps );
+                    return NULL;
+                }
+                ps->parameterSetLength = pps_nalu_length;
+                if( lsmash_add_entry( avcC->pictureParameterSets, ps ) )
+                {
+                    free( ps->parameterSetNALUnit );
+                    free( ps );
+                    return NULL;
+                }
+                info->pps.present = 1;
+            }
+        }
+    }
+    /* Create summary when SPS, PPS and no summary are present even if probe is true.
+     * Skip to create a new summary when detecting the first summary if probe is true, i.e. we hold the first summary. */
+    if( !info->sps.present || !info->pps.present || (probe && info->summary) )
+        return info->summary;
+    if( !probe && ((!sps_nalu && pps_nalu && same_pps) || (!pps_nalu && sps_nalu && same_sps)) )
+        return info->summary;
+    h264_sps_t *sps = &info->sps;
+    h264_pps_t *pps = &info->pps;
+    if( sps->seq_parameter_set_id != pps->seq_parameter_set_id )
+        return info->summary;       /* No support yet */
+    lsmash_video_summary_t *summary;
+    if( info->summary )
+    {
+        summary = info->summary;
+        if( summary->exdata )
+            free( summary->exdata );
+        summary->exdata = NULL;
+        info->first_summary = 0;
+    }
+    else
+    {
+        summary = (lsmash_video_summary_t *)lsmash_create_summary( MP4SYS_STREAM_TYPE_VisualStream );
+        if( !summary )
+            return NULL;
+        info->first_summary = 1;
+    }
+    /* Update summary here.
+     * max_au_length is set at the last of mp4sys_h264_probe function. */
+    summary->sample_type            = ISOM_CODEC_TYPE_AVC1_VIDEO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_Visual_H264_ISO_14496_10;
+    summary->timescale              = sps->vui.time_scale;
+    summary->timebase               = sps->vui.num_units_in_tick;
+    summary->full_range             = sps->vui.video_full_range_flag;
+    summary->vfr                    = !sps->vui.fixed_frame_rate_flag;
+    summary->width                  = sps->cropped_width;
+    summary->height                 = sps->cropped_height;
+    summary->par_h                  = sps->vui.sar_width;
+    summary->par_v                  = sps->vui.sar_height;
+    summary->primaries              = sps->vui.colour_primaries;
+    summary->transfer               = sps->vui.transfer_characteristics;
+    summary->matrix                 = sps->vui.matrix_coefficients;
+    /* Export 'avcC' box into exdata. */
+    lsmash_bs_t *bs = lsmash_bs_create( NULL );
+    if( !bs )
+        goto fail;
+    lsmash_bs_put_be32( bs, avcC->size );
+    lsmash_bs_put_be32( bs, avcC->type );
+    lsmash_bs_put_byte( bs, avcC->configurationVersion );
+    lsmash_bs_put_byte( bs, avcC->AVCProfileIndication );
+    lsmash_bs_put_byte( bs, avcC->profile_compatibility );
+    lsmash_bs_put_byte( bs, avcC->AVCLevelIndication );
+    lsmash_bs_put_byte( bs, avcC->lengthSizeMinusOne | 0xfc );
+    lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSets | 0xe0 );
+    isom_avcC_ps_entry_t *ps = (isom_avcC_ps_entry_t *)avcC->sequenceParameterSets->head->data;
+    if( !ps )
+        goto fail;
+    lsmash_bs_put_be16( bs, ps->parameterSetLength );
+    lsmash_bs_put_bytes( bs, ps->parameterSetNALUnit, ps->parameterSetLength );
+    lsmash_bs_put_byte( bs, avcC->numOfPictureParameterSets );
+    ps = (isom_avcC_ps_entry_t *)avcC->pictureParameterSets->head->data;
+    if( !ps )
+        goto fail;
+    lsmash_bs_put_be16( bs, ps->parameterSetLength );
+    lsmash_bs_put_bytes( bs, ps->parameterSetNALUnit, ps->parameterSetLength );
+    if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
+    {
+        lsmash_bs_put_byte( bs, avcC->chroma_format | 0xfc );
+        lsmash_bs_put_byte( bs, avcC->bit_depth_luma_minus8 | 0xf8 );
+        lsmash_bs_put_byte( bs, avcC->bit_depth_chroma_minus8 | 0xf8 );
+        lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSetExt );
+        /* No SequenceParameterSetExt */
+    }
+    summary->exdata = lsmash_bs_export_data( bs, &summary->exdata_length );
+    lsmash_bs_cleanup( bs );
+    /* Update box size. */
+    uint8_t *exdata = (uint8_t *)summary->exdata;
+    exdata[0] = (summary->exdata_length >> 24) & 0xff;
+    exdata[1] = (summary->exdata_length >> 16) & 0xff;
+    exdata[2] = (summary->exdata_length >>  8) & 0xff;
+    exdata[3] =  summary->exdata_length        & 0xff;
+    return summary;
+fail:
+    lsmash_remove_list( avcC->sequenceParameterSets,   isom_remove_avcC_ps );
+    lsmash_remove_list( avcC->pictureParameterSets,    isom_remove_avcC_ps );
+    lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps );
+    lsmash_cleanup_summary( (lsmash_summary_t *)summary );
+    lsmash_bs_cleanup( bs );
+    return NULL;
+}
+
+static inline int h264_update_picture_type( h264_picture_info_t *picture, h264_slice_info_t *slice )
+{
+    if( picture->type == H264_PICTURE_TYPE_I_P )
+    {
+        if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_P_B;
+        else if( slice->type == H264_SLICE_TYPE_SI || slice->type == H264_SLICE_TYPE_SP )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_I_P_B )
+    {
+        if( slice->type != H264_SLICE_TYPE_P && slice->type != H264_SLICE_TYPE_B && slice->type != H264_SLICE_TYPE_I )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_I )
+    {
+        if( slice->type == H264_SLICE_TYPE_P )
+            picture->type = H264_PICTURE_TYPE_I_P;
+        else if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_P_B;
+        else if( slice->type == H264_SLICE_TYPE_SI )
+            picture->type = H264_PICTURE_TYPE_I_SI;
+        else if( slice->type == H264_SLICE_TYPE_SP )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_SI_SP )
+    {
+        if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_I )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP;
+        else if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_SI )
+    {
+        if( slice->type == H264_SLICE_TYPE_P )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP;
+        else if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B;
+        else if( slice->type != H264_SLICE_TYPE_I )
+            picture->type = H264_PICTURE_TYPE_I_SI;
+        else if( slice->type == H264_SLICE_TYPE_SP )
+            picture->type = H264_PICTURE_TYPE_SI_SP;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_I_SI )
+    {
+        if( slice->type == H264_SLICE_TYPE_P || slice->type == H264_SLICE_TYPE_SP )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP;
+        else if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_I_SI_P_SP )
+    {
+        if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_SI_P_SP_B;
+    }
+    else if( picture->type == H264_PICTURE_TYPE_NONE )
+    {
+        if( slice->type == H264_SLICE_TYPE_P )
+            picture->type = H264_PICTURE_TYPE_I_P;
+        else if( slice->type == H264_SLICE_TYPE_B )
+            picture->type = H264_PICTURE_TYPE_I_P_B;
+        else if( slice->type == H264_SLICE_TYPE_I )
+            picture->type = H264_PICTURE_TYPE_I;
+        else if( slice->type == H264_SLICE_TYPE_SI )
+            picture->type = H264_PICTURE_TYPE_SI;
+        else if( slice->type == H264_SLICE_TYPE_SP )
+            picture->type = H264_PICTURE_TYPE_SI_SP;
+    }
+#if 0
+    fprintf( stderr, "Picture type = %s\n", picture->type == H264_PICTURE_TYPE_I_P        ? "P"
+                                          : picture->type == H264_PICTURE_TYPE_I_P_B      ? "B"
+                                          : picture->type == H264_PICTURE_TYPE_I          ? "I"
+                                          : picture->type == H264_PICTURE_TYPE_SI         ? "SI"
+                                          : picture->type == H264_PICTURE_TYPE_I_SI       ? "SI"
+                                          :                                                 "SP" );
+#endif
+    return 0;
+}
+
+static inline int h264_update_picture_info( h264_picture_info_t *picture, h264_slice_info_t *slice, h264_sei_t *sei )
+{
+    picture->frame_num                  = slice->frame_num;
+    picture->pic_order_cnt_lsb          = slice->pic_order_cnt_lsb;
+    picture->delta_pic_order_cnt_bottom = slice->delta_pic_order_cnt_bottom;
+    picture->delta_pic_order_cnt[0]     = slice->delta_pic_order_cnt[0];
+    picture->delta_pic_order_cnt[1]     = slice->delta_pic_order_cnt[1];
+    picture->field_pic_flag             = slice->field_pic_flag;
+    picture->bottom_field_flag          = slice->bottom_field_flag;
+    picture->idr                        = slice->IdrPicFlag;
+    picture->pic_parameter_set_id       = slice->pic_parameter_set_id;
+    picture->disposable                 = (slice->nal_ref_idc == 0);
+    picture->random_accessible         |= (picture->idr || sei->random_accessible);
+    picture->has_mmco5                 |= slice->has_mmco5;
+    picture->has_redundancy            |= slice->has_redundancy;
+    picture->recovery_frame_cnt         = sei->recovery_frame_cnt;
+    if( h264_update_picture_type( picture, slice ) )
+        return -1;
+    picture->independent      = picture->type == H264_PICTURE_TYPE_I || picture->type == H264_PICTURE_TYPE_I_SI;
+    picture->non_bipredictive = picture->type != H264_PICTURE_TYPE_I_P_B && picture->type != H264_PICTURE_TYPE_I_SI_P_SP_B;
+    sei->random_accessible  = 0;
+    sei->recovery_frame_cnt = 0;
+    return 0;
+}
+
+static inline int h264_find_au_delimit( h264_slice_info_t *slice, h264_slice_info_t *prev_slice )
+{
+    if( slice->frame_num                    != prev_slice->frame_num
+     || ((slice->pic_order_cnt_type == 0    && prev_slice->pic_order_cnt_type == 0)
+      && (slice->pic_order_cnt_lsb          != prev_slice->pic_order_cnt_lsb
+      ||  slice->delta_pic_order_cnt_bottom != prev_slice->delta_pic_order_cnt_bottom))
+     || ((slice->pic_order_cnt_type == 1    && prev_slice->pic_order_cnt_type == 1)
+      && (slice->delta_pic_order_cnt[0]     != prev_slice->delta_pic_order_cnt[0]
+      ||  slice->delta_pic_order_cnt[1]     != prev_slice->delta_pic_order_cnt[1]))
+     || slice->field_pic_flag               != prev_slice->field_pic_flag
+     || slice->bottom_field_flag            != prev_slice->bottom_field_flag
+     || slice->IdrPicFlag                   != prev_slice->IdrPicFlag
+     || slice->pic_parameter_set_id         != prev_slice->pic_parameter_set_id
+     || ((slice->nal_ref_idc == 0           || prev_slice->nal_ref_idc == 0)
+      && (slice->nal_ref_idc                != prev_slice->nal_ref_idc))
+     || (slice->IdrPicFlag == 1             && prev_slice->IdrPicFlag == 1
+      && slice->idr_pic_id                  != prev_slice->idr_pic_id) )
+        return 1;
+    return 0;
+}
+
+static int h264_supplement_buffer( mp4sys_h264_info_t *info, uint32_t size )
+{
+    assert( size > info->buffer_size );
+    uint8_t *temp = realloc( info->buffer, 4 * size );
+    if( !temp )
+        return -1;
+    uint32_t old_buffer_size = info->buffer_size;
+    info->buffer                = temp;
+    info->buffer_size           = size;
+    info->stream_buffer         = info->buffer;
+    info->rbsp_buffer           = info->buffer +     info->buffer_size;
+    info->picture.au            = info->buffer + 2 * info->buffer_size;
+    info->picture.incomplete_au = info->buffer + 3 * info->buffer_size;
+    memmove( info->picture.incomplete_au, info->buffer + 3 * old_buffer_size, old_buffer_size );
+    memmove( info->picture.au,            info->buffer + 2 * old_buffer_size, old_buffer_size );
+    memmove( info->rbsp_buffer,           info->buffer +     old_buffer_size, old_buffer_size );
+    return 0;
+}
+
+static inline void h264_complete_au( h264_picture_info_t *picture, int probe )
+{
+    if( !probe )
+        memcpy( picture->au, picture->incomplete_au, picture->incomplete_au_length );
+    picture->au_length = picture->incomplete_au_length;
+    picture->incomplete_au_length = 0;
+    picture->incomplete_au_has_primary = 0;
+}
+
+#define H264_SHORT_START_CODE_LENGTH 3
+#define H264_LONG_START_CODE_LENGTH  4
+#define CHECK_NEXT_SHORT_START_CODE( x ) (!(x)[0] && !(x)[1] && ((x)[2] == 0x01))
+#define CHECK_NEXT_LONG_START_CODE( x ) (!(x)[0] && !(x)[1] && !(x)[2] && ((x)[3] == 0x01))
+#define IF_BUFFER_SHORTAGE( anticipation_bytes, buffer_size ) \
+    if( ((buf_pos + anticipation_bytes) >= buf_end) && !no_more_read ) \
+    { \
+        uint32_t read_size = fread( buf + anticipation_bytes, 1, buffer_size - anticipation_bytes, importer->stream ); \
+        buf_pos = buf; \
+        valid_length = anticipation_bytes + read_size;\
+        buf_end = buf + valid_length; \
+        no_more_read = read_size == 0 ? feof( importer->stream ) : 0; \
+    }
+
+/* If probe equals 0, don't get the actual data (EBPS) of an access unit and only parse NALU.
+ * Currently, you can get AU of AVC video elemental stream only, not AVC parameter set elemental stream defined in 14496-15. */
+static int h264_get_access_unit_internal( mp4sys_importer_t *importer, mp4sys_h264_info_t *info, uint32_t track_number, int probe )
+{
+    h264_slice_info_t *slice           = &info->slice;
+    h264_picture_info_t *picture       = &info->picture;
+    lsmash_video_summary_t *summary    = info->summary;
+    h264_nalu_header_t nalu_header     = info->nalu_header;
+    uint8_t prev_nalu_type             = info->prev_nalu_type;
+    uint8_t no_more_read               = info->no_more_read;
+    uint8_t *buf                       = info->stream_buffer;
+    uint8_t *buf_pos                   = info->stream_buffer_pos;
+    uint8_t *buf_end                   = info->stream_buffer + info->stream_read_size;
+    uint64_t ebsp_head_pos             = info->ebsp_head_pos;
+    uint8_t  incomplete_au_has_primary = picture->incomplete_au_has_primary;
+    uint32_t valid_length = buf_end - buf;
+    uint64_t consecutive_zero_byte_count = 0;
+    uint64_t ebsp_length = 0;
+    int      no_more_buf = 0;
+    int      complete_au = 0;
+    int      success = 0;
+    picture->au_length         = 0;
+    picture->type              = H264_PICTURE_TYPE_NONE;
+    picture->random_accessible = 0;
+    picture->has_mmco5         = 0;
+    picture->has_redundancy    = 0;
+    while( 1 )
+    {
+        IF_BUFFER_SHORTAGE( 2, info->buffer_size );
+        no_more_buf = buf_pos >= buf_end;
+        int no_more = no_more_read && no_more_buf;
+        if( (((buf_pos + 2) < buf_end) && CHECK_NEXT_SHORT_START_CODE( buf_pos )) || no_more )
+        {
+            uint64_t next_nalu_head_pos = ebsp_head_pos + ebsp_length + !no_more * H264_SHORT_START_CODE_LENGTH;
+            uint8_t *backup_pos = buf_pos;      /* Remember position of start code of the next NALU. */
+            uint8_t nalu_type = nalu_header.nal_unit_type;
+            int is_vcl_nalu = nalu_type >= 1 && nalu_type <= 5;
+            int read_back = 0;
+            int last_nalu = 0;
+            if( no_more && ebsp_length == 0 )
+            {
+                /* For the last NALU. */
+                ebsp_length = picture->incomplete_au_length - nalu_header.length - 4;
+                consecutive_zero_byte_count = 0;
+                last_nalu = 1;
+            }
+#if 0
+            if( probe )
+            {
+                fprintf( stderr, "NALU type: %"PRIu8"\n", nalu_type );
+                fprintf( stderr, "    NALU header position: %"PRIx64"\n", ebsp_head_pos - nalu_header.length );
+                fprintf( stderr, "    EBSP position: %"PRIx64"\n", ebsp_head_pos );
+                fprintf( stderr, "    EBSP length: %"PRIx64" (%"PRIu64")\n", ebsp_length - consecutive_zero_byte_count,
+                                                                             ebsp_length - consecutive_zero_byte_count );
+                fprintf( stderr, "    consecutive_zero_byte_count: %"PRIx64"\n", consecutive_zero_byte_count );
+                fprintf( stderr, "    Next NALU header position: %"PRIx64"\n", next_nalu_head_pos );
+            }
+#endif
+            if( nalu_type == 12 )
+            {
+                /* We don't support streams with both filler and HRD yet.
+                 * Otherwise, just skip filler because elemental streams defined in 14496-15 is forbidden to use filler. */
+                if( info->sps.hrd_present )
+                    return -1;
+            }
+            else if( (nalu_type >= 1 && nalu_type <= 13) || nalu_type == 19 )
+            {
+                /* Get the EBSP of the current NALU here.
+                 * AVC elemental stream defined in 14496-15 can recognizes from 0 to 13, and 19 of nal_unit_type.
+                 * We don't support SVC and MVC elemental stream defined in 14496-15 yet. */
+                ebsp_length -= consecutive_zero_byte_count;     /* Any EBSP doesn't have zero bytes at the end. */
+                uint64_t nalu_length = nalu_header.length + ebsp_length;
+                uint32_t buf_distance = buf_pos - buf;
+                if( info->buffer_size < nalu_length )
+                {
+                    if( h264_supplement_buffer( info, 2 * nalu_length ) )
+                        break;
+                    buf        = info->stream_buffer;
+                    buf_pos    = buf + buf_distance;
+                    buf_end    = buf + valid_length;
+                    backup_pos = buf_pos;
+                }
+                /* Move to the first byte of the current NALU. */
+                read_back = buf_distance < (nalu_length + consecutive_zero_byte_count);
+                if( read_back )
+                {
+                    lsmash_fseek( importer->stream, ebsp_head_pos - nalu_header.length, SEEK_SET );
+                    int read_fail = fread( buf, 1, nalu_length, importer->stream ) != nalu_length;
+                    valid_length = nalu_length;
+                    buf_pos = buf;
+                    buf_end = buf + valid_length;
+                    if( read_fail )
+                        break;
+#if 0
+                    if( probe )
+                        fprintf( stderr, "    ----Read Back\n" );
+#endif
+                }
+                else
+                    buf_pos -= nalu_length + consecutive_zero_byte_count;
+                if( is_vcl_nalu )
+                {
+                    /* VCL NALU (slice) */
+                    h264_slice_info_t prev_slice = *slice;
+                    memset( slice, 0, sizeof(h264_slice_info_t) );
+                    if( h264_parse_slice_header( info->bits, &info->sps, &info->pps, slice, &nalu_header, info->rbsp_buffer, buf_pos + nalu_header.length, ebsp_length ) )
+                        break;
+                    if( prev_slice.present || last_nalu )
+                    {
+                        /* Check whether the AU that contains the previous VCL NALU completed or not. */
+                        if( (incomplete_au_has_primary && h264_find_au_delimit( slice, &prev_slice )) || last_nalu )
+                        {
+                            /* The current NALU is the first VCL NALU of the primary coded picture of an new AU.
+                             * Therefore, the previous slice belongs to the AU you want at this time. */
+                            if( h264_update_picture_info( picture, &prev_slice, &info->sei ) )
+                                break;
+                            complete_au = 1;
+                        }
+                        else
+                        {
+                            picture->random_accessible |= (picture->idr || info->sei.random_accessible);
+                            picture->has_mmco5         |= prev_slice.has_mmco5;
+                            picture->has_redundancy    |= prev_slice.has_redundancy;
+                            if( h264_update_picture_type( picture, &prev_slice ) )
+                                break;
+                            picture->independent      = picture->type == H264_PICTURE_TYPE_I || picture->type == H264_PICTURE_TYPE_I_SI;
+                            picture->non_bipredictive = picture->type != H264_PICTURE_TYPE_I_P_B && picture->type != H264_PICTURE_TYPE_I_SI_P_SP_B;
+                            info->sei.random_accessible  = 0;
+                            info->sei.recovery_frame_cnt = 0;
+                        }
+                        prev_slice.present = 0;     /* Discard the previous slice info. */
+                    }
+                    slice->present = 1;
+                }
+                else if( ((nalu_type >= 6 && nalu_type <= 9) || (nalu_type >= 14 && nalu_type <= 18))
+                 && ((prev_nalu_type >= 1 && prev_nalu_type <= 5) || prev_nalu_type == 12 || prev_nalu_type == 19) )
+                {
+                    /* The last slice belongs to the AU you want at this time. */
+                    if( h264_update_picture_info( picture, slice, &info->sei ) )
+                        break;
+                    slice->present = 0;     /* Discard the current slice info. */
+                    complete_au = 1;
+                }
+                else if( no_more )
+                    complete_au = 1;
+                if( nalu_type == 6 )
+                {
+                    if( h264_parse_sei_nalu( info->bits, &info->sei, &nalu_header, info->rbsp_buffer, buf_pos + nalu_header.length, ebsp_length ) )
+                        break;
+                }
+                else if( nalu_type == 7 )
+                    summary = h264_create_summary( info, info->rbsp_buffer, &nalu_header, buf_pos, nalu_length, NULL, NULL, 0, probe );
+                else if( nalu_type == 8 )
+                    summary = h264_create_summary( info, info->rbsp_buffer, NULL, NULL, 0, &nalu_header, buf_pos, nalu_length, probe );
+                else if( nalu_type == 13 )
+                    return -1;      /* We don't support sequence parameter set extension yet. */
+                if( complete_au && incomplete_au_has_primary )
+                {
+                    h264_complete_au( picture, probe );
+                    if( last_nalu )
+                    {
+                        success = 1;
+                        break;
+                    }
+                }
+                if( is_vcl_nalu || nalu_type == 6
+                 || (nalu_type >= 9 && nalu_type <= 11) || nalu_type == 19 )
+                {
+                    /* Append this NALU into access unit. */
+                    uint64_t needed_au_length = picture->incomplete_au_length + 4 + nalu_length;
+                    if( info->buffer_size < needed_au_length )
+                    {
+                        uint32_t next_start_code_distance = backup_pos - buf;
+                        buf_distance = buf_pos - buf;
+                        if( h264_supplement_buffer( info, 2 * needed_au_length ) )
+                            break;
+                        buf        = info->stream_buffer;
+                        buf_pos    = buf + buf_distance;
+                        buf_end    = buf + valid_length;
+                        backup_pos = buf + next_start_code_distance;
+                    }
+                    if( !probe )
+                    {
+                        uint8_t four_bytes_nalu_length[4];
+                        four_bytes_nalu_length[0] = (nalu_length >> 24) & 0xff;
+                        four_bytes_nalu_length[1] = (nalu_length >> 16) & 0xff;
+                        four_bytes_nalu_length[2] = (nalu_length >>  8) & 0xff;
+                        four_bytes_nalu_length[3] =  nalu_length        & 0xff;
+                        memcpy( picture->incomplete_au + picture->incomplete_au_length, four_bytes_nalu_length, 4 );
+                        memcpy( picture->incomplete_au + picture->incomplete_au_length + 4, buf_pos, nalu_length );
+                    }
+                    picture->incomplete_au_length = needed_au_length;
+                    if( is_vcl_nalu )
+                        incomplete_au_has_primary |= !slice->has_redundancy;
+                }
+            }
+            /* Move to the first byte of the next EBSP. */
+            if( read_back )
+            {
+                lsmash_fseek( importer->stream, next_nalu_head_pos, SEEK_SET );
+                valid_length = fread( buf, 1, info->buffer_size, importer->stream );
+                buf_pos = buf;
+                buf_end = buf + valid_length;
+            }
+            else
+                buf_pos = backup_pos + H264_SHORT_START_CODE_LENGTH;
+            prev_nalu_type = nalu_type;
+            IF_BUFFER_SHORTAGE( 0, info->buffer_size );
+            no_more_buf = buf_pos >= buf_end;
+            ebsp_length = 0;
+            no_more = no_more_read && no_more_buf;
+            if( !no_more )
+            {
+                /* Check the next NALU header. */
+                if( h264_check_nalu_header( &nalu_header, &buf_pos, !!consecutive_zero_byte_count ) )
+                    break;
+                ebsp_head_pos = next_nalu_head_pos + nalu_header.length;
+            }
+            if( complete_au || no_more )
+            {
+                /* If there is no data in the stream, and flushed chunk of NALUs, flush it as complete AU here. */
+                if( no_more && picture->incomplete_au_length && picture->au_length == 0 )
+                {
+                    if( h264_update_picture_info( picture, slice, &info->sei ) )
+                        break;
+                    slice->present = 0;     /* redundant */
+                    h264_complete_au( picture, probe );
+                }
+                success = 1;
+                break;
+            }
+            consecutive_zero_byte_count = 0;
+            continue;       /* Avoid increment of ebsp_length. */
+        }
+        else if( !no_more )
+        {
+            if( *buf_pos++ )
+                consecutive_zero_byte_count = 0;
+            else
+                ++consecutive_zero_byte_count;
+        }
+        ++ebsp_length;
+    }
+    info->status                       = no_more_read && no_more_buf && picture->incomplete_au_length == 0 ? MP4SYS_IMPORTER_EOF : MP4SYS_IMPORTER_OK;
+    info->summary                      = summary;
+    info->nalu_header                  = nalu_header;
+    info->prev_nalu_type               = prev_nalu_type;
+    info->no_more_read                 = no_more_read;
+    info->stream_buffer_pos            = buf_pos;
+    info->stream_read_size             = buf_end - buf;
+    info->ebsp_head_pos                = ebsp_head_pos;
+    picture->incomplete_au_has_primary = incomplete_au_has_primary;
+    picture->au_number                += 1;
+    return (!success || picture->au_length == 0) ? -1 : 0;
+}
+
+static int mp4sys_h264_get_accessunit( mp4sys_importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    debug_if( !importer || !importer->info || !buffered_sample->data || !buffered_sample->length )
+        return -1;
+    if( !importer->info || track_number != 1 )
+        return -1;
+    mp4sys_h264_info_t *info = (mp4sys_h264_info_t *)importer->info;
+    mp4sys_importer_status current_status = info->status;
+    if( current_status == MP4SYS_IMPORTER_ERROR || buffered_sample->length < info->max_au_length )
+        return -1;
+    if( current_status == MP4SYS_IMPORTER_EOF )
+    {
+        buffered_sample->length = 0;
+        return 0;
+    }
+    if( info->summary && !info->first_summary )
+    {
+        current_status = MP4SYS_IMPORTER_CHANGE;
+        /* Summaries may be not active immediately because we can't get any corresponding AU at once.
+         * The first summary will be an only exception of this. */
+        lsmash_entry_t* entry = lsmash_get_entry( importer->summaries, track_number );
+        if( !entry || !entry->data )
+            return -1;
+        lsmash_cleanup_summary( entry->data );
+        entry->data = info->summary;
+        info->summary = NULL;
+    }
+    if( h264_get_access_unit_internal( importer, info, track_number, 0 ) )
+    {
+        info->status = MP4SYS_IMPORTER_ERROR;
+        return -1;
+    }
+    h264_sps_t *sps = &info->sps;
+    h264_picture_info_t *picture = &info->picture;
+    buffered_sample->dts = info->ts_list.timestamp[picture->au_number - 1].dts;
+    buffered_sample->cts = info->ts_list.timestamp[picture->au_number - 1].cts;
+    if( picture->au_number < info->num_undecodable )
+        buffered_sample->prop.leading = ISOM_SAMPLE_IS_UNDECODABLE_LEADING;
+    else
+        buffered_sample->prop.leading = picture->non_bipredictive || buffered_sample->cts >= info->last_intra_cts
+                                      ? ISOM_SAMPLE_IS_NOT_LEADING : ISOM_SAMPLE_IS_UNDECODABLE_LEADING;
+    if( picture->independent )
+        info->last_intra_cts = buffered_sample->cts;
+    if( info->composition_reordering_present && !picture->disposable && !picture->idr )
+        buffered_sample->prop.allow_earlier = QT_SAMPLE_EARLIER_PTS_ALLOWED;
+    buffered_sample->prop.independent = picture->independent    ? ISOM_SAMPLE_IS_INDEPENDENT : ISOM_SAMPLE_IS_NOT_INDEPENDENT;
+    buffered_sample->prop.disposable  = picture->disposable     ? ISOM_SAMPLE_IS_DISPOSABLE  : ISOM_SAMPLE_IS_NOT_DISPOSABLE;
+    buffered_sample->prop.redundant   = picture->has_redundancy ? ISOM_SAMPLE_HAS_REDUNDANCY : ISOM_SAMPLE_HAS_NO_REDUNDANCY;
+    buffered_sample->prop.post_roll.identifier = picture->frame_num;
+    if( picture->random_accessible )
+    {
+        if( picture->idr )
+            buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+        else if( picture->recovery_frame_cnt )
+        {
+            buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY;
+            buffered_sample->prop.post_roll.complete = (picture->frame_num + picture->recovery_frame_cnt) % sps->MaxFrameNum;
+        }
+        else
+            buffered_sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP;
+    }
+    buffered_sample->length = picture->au_length;
+    memcpy( buffered_sample->data, picture->au, picture->au_length );
+    /* Return 1 if a new summary is detected. */
+    return current_status == MP4SYS_IMPORTER_CHANGE;
+}
+
+static int mp4sys_h264_probe( mp4sys_importer_t *importer )
+{
+    /* Find the first start code. */
+    uint8_t buf[MP4SYS_H264_DEFAULT_BUFFER_SIZE];
+    int found_start_code = 0;
+    uint32_t valid_length = fread( buf, 1, MP4SYS_H264_DEFAULT_BUFFER_SIZE, importer->stream );
+    uint8_t *buf_pos = buf;
+    uint8_t *buf_end = buf + valid_length;
+    int no_more_read = buf >= buf_end ? feof( importer->stream ) : 0;
+    while( 1 )
+    {
+        /* Invalid if encountered any value of non-zero before the first start code. */
+        IF_INVALID_VALUE( *buf_pos )
+            return -1;
+        /* The first NALU of an AU in decoding order shall have long start code (0x00000001). */
+        if( CHECK_NEXT_LONG_START_CODE( buf_pos ) )
+        {
+            found_start_code = 1;
+            break;
+        }
+        if( (buf_pos + H264_LONG_START_CODE_LENGTH) == buf_end )
+            break;
+        ++buf_pos;
+    }
+    /* If the first trial of finding long start code failed, we assume this stream is not byte stream format of H.264. */
+    if( !found_start_code )
+        return -1;
+    buf_pos += H264_LONG_START_CODE_LENGTH;
+    IF_BUFFER_SHORTAGE( 0, MP4SYS_H264_DEFAULT_BUFFER_SIZE );
+    h264_nalu_header_t first_nalu_header;
+    if( h264_check_nalu_header( &first_nalu_header, &buf_pos, 1 ) )
+        return -1;
+    uint64_t first_ebsp_head_pos = buf_pos - buf;       /* EBSP doesn't include NALU header. */
+    /* Check existence of a complete AU. */
+    mp4sys_h264_info_t *info = mp4sys_create_h264_info();
+    if( !info )
+        return -1;
+    info->status        = no_more_read ? MP4SYS_IMPORTER_EOF : MP4SYS_IMPORTER_OK;
+    info->nalu_header   = first_nalu_header;
+    info->ebsp_head_pos = first_ebsp_head_pos;
+    memcpy( info->stream_buffer, buf, valid_length );
+    info->stream_buffer_pos = &info->stream_buffer[buf_pos - buf];
+    info->stream_read_size  = valid_length;
+    /* Parse all NALU in the stream for preparation of calculating timestamps. */
+    uint32_t poc_alloc = (1 << 12) * sizeof(uint64_t);
+    uint64_t *poc = malloc( poc_alloc );
+    if( !poc )
+        goto fail;
+    uint32_t num_access_units = 0;
+    fprintf( stderr, "Analyzing stream as H.264\r" );
+    while( info->status != MP4SYS_IMPORTER_EOF )
+    {
+#if 0
+        fprintf( stderr, "Analyzing stream as H.264: %"PRIu32"\n", num_access_units + 1 );
+#endif
+        h264_picture_info_t prev_picture = info->picture;
+        if( h264_get_access_unit_internal( importer, info, 0, 1 ) )
+            goto fail;
+        if( h264_calculate_poc( &info->sps, &info->picture, &prev_picture ) )
+            goto fail;
+        if( poc_alloc <= num_access_units * sizeof(uint64_t) )
+        {
+            uint32_t alloc = 2 * num_access_units * sizeof(uint64_t);
+            uint64_t *temp = realloc( poc, alloc );
+            if( !temp )
+            {
+                if( poc )
+                    free( poc );
+                goto fail;
+            }
+            poc = temp;
+            poc_alloc = alloc;
+        }
+        poc[num_access_units++] = info->picture.PicOrderCnt;
+        info->max_au_length = LSMASH_MAX( info->picture.au_length, info->max_au_length );
+    }
+    fprintf( stderr, "                                                                               \r" );
+    if( !info->summary || lsmash_add_entry( importer->summaries, info->summary ) )
+        goto fail;
+    lsmash_media_ts_t *timestamp = malloc( num_access_units * sizeof(lsmash_media_ts_t) );
+    if( !timestamp )
+    {
+        free( poc );
+        goto fail;
+    }
+    /* Make zero-origin. */
+    int32_t min_poc = poc[0];
+    for( uint32_t i = 1; i < num_access_units; i++ )
+        min_poc = LSMASH_MIN( (int32_t)poc[i], min_poc );
+    if( min_poc )
+        for( uint32_t i = 0; i < num_access_units; i++ )
+            poc[i] -= min_poc;
+    /* Deduplicate POCs. */
+    uint64_t poc_offset = 0;
+    uint64_t poc_max = 0;
+    for( uint32_t i = 1; i < num_access_units; i++ )
+    {
+        if( poc[i] == 0 )
+        {
+            poc_offset += poc_max + 1;
+            poc_max = 0;
+        }
+        else
+            poc_max = LSMASH_MAX( poc[i], poc_max );
+        poc[i] += poc_offset;
+    }
+    /* Count leading samples that are undecodable. */
+    for( uint32_t i = 0; i < num_access_units; i++ )
+    {
+        if( poc[i] == 0 )
+            break;
+        ++info->num_undecodable;
+    }
+    /* Get max composition delay. */
+    uint32_t composition_delay = 0;
+    uint32_t max_composition_delay = 0;
+    for( uint32_t i = 1; i < num_access_units; i++ )
+        if( poc[i] < poc[i - 1] )
+        {
+            ++composition_delay;
+            max_composition_delay = LSMASH_MAX( max_composition_delay, composition_delay );
+        }
+        else
+            composition_delay = 0;
+    /* Generate timestamps. */
+    if( max_composition_delay )
+    {
+        for( uint32_t i = 0; i < num_access_units; i++ )
+        {
+            timestamp[i].cts = poc[i];
+            timestamp[i].dts = i;
+        }
+        qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_cts );
+        for( uint32_t i = 0; i < num_access_units; i++ )
+            timestamp[i].cts = i + max_composition_delay;
+        qsort( timestamp, num_access_units, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))lsmash_compare_dts );
+    }
+    else
+        for( uint32_t i = 0; i < num_access_units; i++ )
+            timestamp[i].cts = timestamp[i].dts = i;
+    free( poc );
+#if 0
+    for( uint32_t i = 0; i < num_access_units; i++ )
+        fprintf( stderr, "Timestamp[%"PRIu32"]: DTS=%"PRIu64", CTS=%"PRIu64"\n", i, timestamp[i].dts, timestamp[i].cts );
+#endif
+    info->ts_list.sample_count           = num_access_units;
+    info->ts_list.timestamp              = timestamp;
+    info->composition_reordering_present = !!max_composition_delay;
+    /* Go back to EBSP of the first NALU. */
+    lsmash_fseek( importer->stream, first_ebsp_head_pos, SEEK_SET );
+    valid_length = fread( info->stream_buffer, 1, info->buffer_size, importer->stream );
+    info->status                 = MP4SYS_IMPORTER_OK;
+    info->nalu_header            = first_nalu_header;
+    info->prev_nalu_type         = 0;
+    info->no_more_read           = 0;
+    info->first_summary          = 0;
+    info->summary->max_au_length = info->max_au_length;
+    info->summary                = NULL;
+    info->stream_buffer_pos      = info->stream_buffer;
+    info->stream_read_size       = valid_length;
+    info->ebsp_head_pos          = first_ebsp_head_pos;
+    uint8_t *temp_au             = info->picture.au;
+    uint8_t *temp_incomplete_au  = info->picture.incomplete_au;
+    memset( &info->picture, 0, sizeof(h264_picture_info_t) );
+    info->picture.au            = temp_au;
+    info->picture.incomplete_au = temp_incomplete_au;
+    memset( &info->slice, 0, sizeof(h264_slice_info_t) );
+    memset( &info->sps, 0, sizeof(h264_sps_t) );
+    memset( &info->pps, 0, sizeof(h264_pps_t) );
+    lsmash_remove_entries( info->avcC.sequenceParameterSets,   isom_remove_avcC_ps );
+    lsmash_remove_entries( info->avcC.pictureParameterSets,    isom_remove_avcC_ps );
+    lsmash_remove_entries( info->avcC.sequenceParameterSetExt, isom_remove_avcC_ps );
+    importer->info = info;
+    return 0;
+fail:
+    mp4sys_remove_h264_info( info );
+    lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary );
+    return -1;
+}
+
+static uint32_t mp4sys_h264_get_last_delta( mp4sys_importer_t* importer, uint32_t track_number )
+{
+    debug_if( !importer || !importer->info )
+        return 0;
+    mp4sys_h264_info_t *info = (mp4sys_h264_info_t *)importer->info;
+    if( !info || track_number != 1 || info->status != MP4SYS_IMPORTER_EOF )
+        return 0;
+    return info->ts_list.sample_count > 1
+         ? 1
+         : UINT32_MAX;    /* arbitrary */
+}
+
+const static mp4sys_importer_functions mp4sys_h264_importer = {
+    "H.264",
+    1,
+    mp4sys_h264_probe,
+    mp4sys_h264_get_accessunit,
+    mp4sys_h264_get_last_delta,
+    mp4sys_h264_cleanup
+};
+
+/***************************************************************************
+    importer public interfaces
+***************************************************************************/
+
+
+/******** importer listing table ********/
+const static mp4sys_importer_functions* mp4sys_importer_tbl[] = {
+    &mp4sys_adts_importer,
+    &mp4sys_mp3_importer,
+    &mp4sys_amr_importer,
+    &mp4sys_ac3_importer,
+    &mp4sys_eac3_importer,
+    &mp4sys_als_importer,
+    &mp4sys_h264_importer,
+    NULL,
+};
+
+/******** importer public functions ********/
+
+void mp4sys_importer_close( mp4sys_importer_t* importer )
+{
+    if( !importer )
+        return;
+    if( !importer->is_stdin && importer->stream )
+        fclose( importer->stream );
+    if( importer->funcs.cleanup )
+        importer->funcs.cleanup( importer );
+    lsmash_remove_list( importer->summaries, lsmash_cleanup_summary );
+    free( importer );
+}
+
+mp4sys_importer_t *mp4sys_importer_open( const char *identifier, const char *format )
+{
+    if( identifier == NULL )
+        return NULL;
+
+    int auto_detect = ( format == NULL || !strcmp( format, "auto" ) );
+    mp4sys_importer_t *importer = (mp4sys_importer_t *)lsmash_malloc_zero( sizeof(mp4sys_importer_t) );
+    if( !importer )
+        return NULL;
+
+    if( !strcmp( identifier, "-" ) )
+    {
+        /* special treatment for stdin */
+        if( auto_detect )
+        {
+            free( importer );
+            return NULL;
+        }
+        importer->stream = stdin;
+        importer->is_stdin = 1;
+    }
+    else if( (importer->stream = fopen( identifier, "rb" )) == NULL )
+    {
+        mp4sys_importer_close( importer );
+        return NULL;
+    }
+    importer->summaries = lsmash_create_entry_list();
+    if( !importer->summaries )
+    {
+        mp4sys_importer_close( importer );
+        return NULL;
+    }
+    /* find importer */
+    const mp4sys_importer_functions *funcs;
+    if( auto_detect )
+    {
+        /* just rely on detector. */
+        for( int i = 0; (funcs = mp4sys_importer_tbl[i]) != NULL; i++ )
+        {
+            if( !funcs->detectable )
+                continue;
+            if( !funcs->probe( importer ) || lsmash_fseek( importer->stream, 0, SEEK_SET ) )
+                break;
+        }
+    }
+    else
+    {
+        /* needs name matching. */
+        for( int i = 0; (funcs = mp4sys_importer_tbl[i]) != NULL; i++ )
+        {
+            if( strcmp( funcs->name, format ) )
+                continue;
+            if( funcs->probe( importer ) )
+                funcs = NULL;
+            break;
+        }
+    }
+    if( !funcs )
+    {
+        mp4sys_importer_close( importer );
+        return NULL;
+    }
+    importer->funcs = *funcs;
+    return importer;
+}
+
+/* 0 if success, positive if changed, negative if failed */
+int mp4sys_importer_get_access_unit( mp4sys_importer_t* importer, uint32_t track_number, lsmash_sample_t *buffered_sample )
+{
+    if( !importer || !importer->funcs.get_accessunit || !buffered_sample->data || buffered_sample->length == 0 )
+        return -1;
+    return importer->funcs.get_accessunit( importer, track_number, buffered_sample );
+}
+
+/* Return 0 if failed, otherwise succeeded. */
+uint32_t mp4sys_importer_get_last_delta( mp4sys_importer_t *importer, uint32_t track_number )
+{
+    if( !importer || !importer->funcs.get_last_delta )
+        return -1;
+    return importer->funcs.get_last_delta( importer, track_number );
+}
+
+uint32_t mp4sys_importer_get_track_count( mp4sys_importer_t *importer )
+{
+    if( !importer || !importer->summaries )
+        return 0;
+    return importer->summaries->entry_count;
+}
+
+lsmash_summary_t *mp4sys_duplicate_summary( mp4sys_importer_t *importer, uint32_t track_number )
+{
+    if( !importer )
+        return NULL;
+    lsmash_summary_t *src_summary = lsmash_get_entry_data( importer->summaries, track_number );
+    if( !src_summary )
+        return NULL;
+    lsmash_summary_t *summary = lsmash_create_summary( src_summary->stream_type );
+    if( !summary )
+        return NULL;
+    switch( src_summary->stream_type )
+    {
+        case MP4SYS_STREAM_TYPE_VisualStream :
+            memcpy( summary, src_summary, sizeof(lsmash_video_summary_t) );
+            break;
+        case MP4SYS_STREAM_TYPE_AudioStream :
+            memcpy( summary, src_summary, sizeof(lsmash_audio_summary_t) );
+            break;
+        default :
+            lsmash_cleanup_summary( summary );
+            return NULL;
+    }
+    summary->exdata = NULL;
+    summary->exdata_length = 0;
+    if( lsmash_summary_add_exdata( summary, src_summary->exdata, src_summary->exdata_length ) )
+    {
+        lsmash_cleanup_summary( summary );
+        return NULL;
+    }
+    return summary;
+}
diff --git a/output/mp4/importer.h b/output/mp4/importer.h
new file mode 100644
index 0000000..3762512
--- /dev/null
+++ b/output/mp4/importer.h
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * importer.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ * Contributors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef LSMASH_IMPORTER_H
+#define LSMASH_IMPORTER_H
+
+/***************************************************************************
+    importer
+***************************************************************************/
+
+#ifndef LSMASH_IMPORTER_INTERNAL
+
+typedef void mp4sys_importer_t;
+
+/* importing functions */
+mp4sys_importer_t *mp4sys_importer_open( const char *identifier, const char *format );
+void mp4sys_importer_close( mp4sys_importer_t *importer );
+int mp4sys_importer_get_access_unit( mp4sys_importer_t *importer, uint32_t track_number, lsmash_sample_t *buffered_sample );
+uint32_t mp4sys_importer_get_last_delta( mp4sys_importer_t *importer, uint32_t track_number );
+uint32_t mp4sys_importer_get_track_count( mp4sys_importer_t *importer );
+lsmash_summary_t *mp4sys_duplicate_summary( mp4sys_importer_t *importer, uint32_t track_number );
+
+int mp4sys_amr_create_damr( lsmash_audio_summary_t *summary );
+int mp4sys_create_dac3_from_syncframe( lsmash_audio_summary_t *summary, uint8_t *data, uint32_t data_length );
+
+#endif /* #ifndef LSMASH_IMPORTER_INTERNAL */
+
+#endif /* #ifndef LSMASH_IMPORTER_H */
diff --git a/output/mp4/internal.h b/output/mp4/internal.h
new file mode 100644
index 0000000..b697f5d
--- /dev/null
+++ b/output/mp4/internal.h
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * internal.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors:  Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include "common/osdep.h" /* must be placed before stdio.h */
+#include <stdio.h>
+#include <assert.h>
+
+#ifndef lsmash_fseek
+#define lsmash_fseek fseek
+#define lsmash_ftell ftell
+#endif
+
+#include "lsmash.h"
+
+#endif
diff --git a/output/mp4/isom.c b/output/mp4/isom.c
new file mode 100644
index 0000000..86441aa
--- /dev/null
+++ b/output/mp4/isom.c
@@ -0,0 +1,10206 @@
+/*****************************************************************************
+ * isom.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ * Contributors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <ctype.h> /* for chapter handling */
+
+#include "box.h"
+#include "mp4a.h"
+#include "mp4sys.h"
+#ifdef LSMASH_DEMUXER_ENABLED
+#include "read.h"
+#include "print.h"
+#include "timeline.h"
+#endif
+
+
+/*---- ----*/
+/* Return 1 if the box is fullbox, Otherwise return 0. */
+int isom_is_fullbox( void *box )
+{
+    isom_box_t *current = (isom_box_t *)box;
+    uint32_t type = current->type;
+    static const uint32_t fullbox_table[] = {
+        ISOM_BOX_TYPE_MVHD,
+        ISOM_BOX_TYPE_IODS,
+        ISOM_BOX_TYPE_ESDS,
+        ISOM_BOX_TYPE_TKHD,
+        QT_BOX_TYPE_CLEF,
+        QT_BOX_TYPE_PROF,
+        QT_BOX_TYPE_ENOF,
+        ISOM_BOX_TYPE_ELST,
+        ISOM_BOX_TYPE_MDHD,
+        ISOM_BOX_TYPE_HDLR,
+        ISOM_BOX_TYPE_VMHD,
+        ISOM_BOX_TYPE_SMHD,
+        ISOM_BOX_TYPE_HMHD,
+        ISOM_BOX_TYPE_NMHD,
+        QT_BOX_TYPE_GMIN,
+        ISOM_BOX_TYPE_DREF,
+        ISOM_BOX_TYPE_URL ,
+        ISOM_BOX_TYPE_STSD,
+        ISOM_BOX_TYPE_STSL,
+        QT_BOX_TYPE_CHAN,
+        ISOM_BOX_TYPE_STTS,
+        ISOM_BOX_TYPE_CTTS,
+        ISOM_BOX_TYPE_CSLG,
+        ISOM_BOX_TYPE_STSS,
+        QT_BOX_TYPE_STPS,
+        ISOM_BOX_TYPE_SDTP,
+        ISOM_BOX_TYPE_STSC,
+        ISOM_BOX_TYPE_STSZ,
+        ISOM_BOX_TYPE_STCO,
+        ISOM_BOX_TYPE_CO64,
+        ISOM_BOX_TYPE_SGPD,
+        ISOM_BOX_TYPE_SBGP,
+        ISOM_BOX_TYPE_CHPL,
+        ISOM_BOX_TYPE_META,
+        QT_BOX_TYPE_KEYS,
+        ISOM_BOX_TYPE_MEAN,
+        ISOM_BOX_TYPE_NAME,
+        ISOM_BOX_TYPE_MEHD,
+        ISOM_BOX_TYPE_TREX,
+        ISOM_BOX_TYPE_MFHD,
+        ISOM_BOX_TYPE_TFHD,
+        ISOM_BOX_TYPE_TRUN,
+        ISOM_BOX_TYPE_TFRA,
+        ISOM_BOX_TYPE_MFRO,
+        0
+    };
+    for( int i = 0; fullbox_table[i]; i++ )
+        if( type == fullbox_table[i] )
+            return 1;
+    return (type == ISOM_BOX_TYPE_CPRT) && current->parent && (current->parent->type == ISOM_BOX_TYPE_UDTA);
+}
+
+/* Return 1 if the sample type is LPCM audio, Otherwise return 0. */
+int isom_is_lpcm_audio( uint32_t type )
+{
+    return type == QT_CODEC_TYPE_23NI_AUDIO
+        || type == QT_CODEC_TYPE_NONE_AUDIO
+        || type == QT_CODEC_TYPE_LPCM_AUDIO
+        || type == QT_CODEC_TYPE_RAW_AUDIO
+        || type == QT_CODEC_TYPE_SOWT_AUDIO
+        || type == QT_CODEC_TYPE_TWOS_AUDIO
+        || type == QT_CODEC_TYPE_FL32_AUDIO
+        || type == QT_CODEC_TYPE_FL64_AUDIO
+        || type == QT_CODEC_TYPE_IN24_AUDIO
+        || type == QT_CODEC_TYPE_IN32_AUDIO
+        || type == QT_CODEC_TYPE_NOT_SPECIFIED;
+}
+
+char *isom_4cc2str( uint32_t fourcc )
+{
+    static char str[5];
+    str[0] = (fourcc >> 24) & 0xff;
+    str[1] = (fourcc >> 16) & 0xff;
+    str[2] = (fourcc >>  8) & 0xff;
+    str[3] =  fourcc        & 0xff;
+    str[4] = 0;
+    return str;
+}
+
+static inline void isom_init_basebox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
+{
+    box->root     = parent->root;
+    box->parent   = parent;
+    box->size     = 0;
+    box->type     = type;
+    box->usertype = NULL;
+}
+
+static inline void isom_init_fullbox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
+{
+    box->root     = parent->root;
+    box->parent   = parent;
+    box->size     = 0;
+    box->type     = type;
+    box->usertype = NULL;
+    box->version  = 0;
+    box->flags    = 0;
+}
+
+void isom_init_box_common( void *box, void *parent, uint32_t type )
+{
+    assert( parent && ((isom_box_t *)parent)->root );
+    if( ((isom_box_t *)parent)->type == ISOM_BOX_TYPE_STSD )
+    {
+        isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
+        return;
+    }
+    if( isom_is_fullbox( box ) )
+        isom_init_fullbox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
+    else
+        isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
+}
+
+static void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box )
+{
+    if( box->size > UINT32_MAX )
+    {
+        lsmash_bs_put_be32( bs, 1 );
+        lsmash_bs_put_be32( bs, box->type );
+        lsmash_bs_put_be64( bs, box->size );    /* largesize */
+    }
+    else
+    {
+        lsmash_bs_put_be32( bs, (uint32_t)box->size );
+        lsmash_bs_put_be32( bs, box->type );
+    }
+    if( box->type == ISOM_BOX_TYPE_UUID )
+        lsmash_bs_put_bytes( bs, box->usertype, 16 );
+}
+
+static void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box )
+{
+    isom_bs_put_basebox_common( bs, box );
+    lsmash_bs_put_byte( bs, box->version );
+    lsmash_bs_put_be24( bs, box->flags );
+}
+
+static void isom_bs_put_box_common( lsmash_bs_t *bs, void *box )
+{
+    if( !box )
+    {
+        bs->error = 1;
+        return;
+    }
+    isom_box_t *parent = ((isom_box_t *)box)->parent;
+    if( parent && parent->type == ISOM_BOX_TYPE_STSD )
+    {
+        isom_bs_put_basebox_common( bs, (isom_box_t *)box );
+        return;
+    }
+    if( isom_is_fullbox( box ) )
+        isom_bs_put_fullbox_common( bs, (isom_box_t *)box );
+    else
+        isom_bs_put_basebox_common( bs, (isom_box_t *)box );
+}
+
+isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID )
+{
+    if( !track_ID || !root || !root->moov || !root->moov->trak_list )
+        return NULL;
+    for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak || !trak->tkhd )
+            return NULL;
+        if( trak->tkhd->track_ID == track_ID )
+            return trak;
+    }
+    return NULL;
+}
+
+static isom_trex_entry_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
+{
+    if( !track_ID || !mvex || !mvex->trex_list )
+        return NULL;
+    for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
+    {
+        isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
+        if( !trex )
+            return NULL;
+        if( trex->track_ID == track_ID )
+            return trex;
+    }
+    return NULL;
+}
+
+static isom_traf_entry_t *isom_get_traf( isom_moof_entry_t *moof, uint32_t track_ID )
+{
+    if( !track_ID || !moof || !moof->traf_list )
+        return NULL;
+    for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+    {
+        isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
+        if( !traf || !traf->tfhd )
+            return NULL;
+        if( traf->tfhd->track_ID == track_ID )
+            return traf;
+    }
+    return NULL;
+}
+
+static isom_tfra_entry_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
+{
+    if( !track_ID || !mfra || !mfra->tfra_list )
+        return NULL;
+    for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
+    {
+        isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
+        if( !tfra )
+            return NULL;
+        if( tfra->track_ID == track_ID )
+            return tfra;
+    }
+    return NULL;
+}
+
+static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
+{
+    isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) );
+    if( !data )
+        return -1;
+    data->segment_duration = segment_duration;
+    data->media_time = media_time;
+    data->media_rate = media_rate;
+    if( lsmash_add_entry( elst->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
+        elst->version = 1;
+    return 0;
+}
+
+static isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type, uint32_t ref_count, uint32_t *track_ID )
+{
+    if( !tref || !tref->ref_list )
+        return NULL;
+    isom_tref_type_t *ref = malloc( sizeof(isom_tref_type_t) );
+    if( !ref )
+        return NULL;
+    isom_init_box_common( ref, tref, type );
+    ref->ref_count = ref_count;
+    ref->track_ID = track_ID;
+    if( lsmash_add_entry( tref->ref_list, ref ) )
+    {
+        free( ref );
+        return NULL;
+    }
+    return ref;
+}
+
+static int isom_add_dref_entry( isom_dref_t *dref, uint32_t flags, char *name, char *location )
+{
+    if( !dref || !dref->list )
+        return -1;
+    isom_dref_entry_t *data = lsmash_malloc_zero( sizeof(isom_dref_entry_t) );
+    if( !data )
+        return -1;
+    isom_init_box_common( data, dref, name ? ISOM_BOX_TYPE_URN : ISOM_BOX_TYPE_URL );
+    data->flags = flags;
+    if( location )
+    {
+        data->location_length = strlen( location ) + 1;
+        data->location = lsmash_memdup( location, data->location_length );
+        if( !data->location )
+        {
+            free( data );
+            return -1;
+        }
+    }
+    if( name )
+    {
+        data->name_length = strlen( name ) + 1;
+        data->name = lsmash_memdup( name, data->name_length );
+        if( !data->name )
+        {
+            if( data->location )
+                free( data->location );
+            free( data );
+            return -1;
+        }
+    }
+    if( lsmash_add_entry( dref->list, data ) )
+    {
+        if( data->location )
+            free( data->location );
+        if( data->name )
+            free( data->name );
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
+{
+    isom_avcC_ps_entry_t *entry = malloc( sizeof(isom_avcC_ps_entry_t) );
+    if( !entry )
+        return NULL;
+    entry->parameterSetNALUnit = lsmash_memdup( ps, ps_size );
+    if( !entry->parameterSetNALUnit )
+    {
+        free( entry );
+        return NULL;
+    }
+    entry->parameterSetLength = ps_size;
+    return entry;
+}
+
+void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps )
+{
+    if( !ps )
+        return;
+    if( ps->parameterSetNALUnit )
+        free( ps->parameterSetNALUnit );
+    free( ps );
+}
+
+int lsmash_add_sps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *sps, uint32_t sps_size )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return -1;
+    isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
+    if( !data || !data->avcC )
+        return -1;
+    isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
+    isom_avcC_ps_entry_t *ps = isom_create_ps_entry( sps, sps_size );
+    if( !ps )
+        return -1;
+    if( lsmash_add_entry( avcC->sequenceParameterSets, ps ) )
+    {
+        isom_remove_avcC_ps( ps );
+        return -1;
+    }
+    avcC->numOfSequenceParameterSets = avcC->sequenceParameterSets->entry_count;
+    return 0;
+}
+
+int lsmash_add_pps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *pps, uint32_t pps_size )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return -1;
+    isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
+    if( !data || !data->avcC )
+        return -1;
+    isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
+    isom_avcC_ps_entry_t *ps = isom_create_ps_entry( pps, pps_size );
+    if( !ps )
+        return -1;
+    if( lsmash_add_entry( avcC->pictureParameterSets, ps ) )
+    {
+        isom_remove_avcC_ps( ps );
+        return -1;
+    }
+    avcC->numOfPictureParameterSets = avcC->pictureParameterSets->entry_count;
+    return 0;
+}
+
+int lsmash_add_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return -1;
+    isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
+    if( !data || !data->avcC )
+        return -1;
+    isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
+    isom_avcC_ps_entry_t *ps = isom_create_ps_entry( spsext, spsext_size );
+    if( !ps )
+        return -1;
+    if( lsmash_add_entry( avcC->sequenceParameterSetExt, ps ) )
+    {
+        isom_remove_avcC_ps( ps );
+        return -1;
+    }
+    avcC->numOfSequenceParameterSetExt = avcC->sequenceParameterSetExt->entry_count;
+    return 0;
+}
+
+int isom_add_avcC( isom_visual_entry_t *visual )
+{
+    if( !visual )
+        return -1;
+    isom_create_box( avcC, visual, ISOM_BOX_TYPE_AVCC );
+    avcC->sequenceParameterSets = lsmash_create_entry_list();
+    if( !avcC->sequenceParameterSets )
+    {
+        free( avcC );
+        return -1;
+    }
+    avcC->pictureParameterSets = lsmash_create_entry_list();
+    if( !avcC->pictureParameterSets )
+    {
+        isom_remove_avcC( avcC );
+        return -1;
+    }
+    avcC->sequenceParameterSetExt = lsmash_create_entry_list();
+    if( !avcC->sequenceParameterSetExt )
+    {
+        isom_remove_avcC( avcC );
+        return -1;
+    }
+    visual->avcC = avcC;
+    return 0;
+}
+
+int isom_add_clap( isom_visual_entry_t *visual )
+{
+    if( !visual || visual->clap )
+        return -1;
+    isom_create_box( clap, visual, ISOM_BOX_TYPE_CLAP );
+    clap->cleanApertureWidthN = 1;
+    clap->cleanApertureWidthD = 1;
+    clap->cleanApertureHeightN = 1;
+    clap->cleanApertureHeightD = 1;
+    clap->horizOffN = 0;
+    clap->horizOffD = 1;
+    clap->vertOffN = 0;
+    clap->vertOffD = 1;
+    visual->clap = clap;
+    return 0;
+}
+
+int isom_add_pasp( isom_visual_entry_t *visual )
+{
+    if( !visual || visual->pasp )
+        return -1;
+    isom_create_box( pasp, visual, ISOM_BOX_TYPE_PASP );
+    pasp->hSpacing = 1;
+    pasp->vSpacing = 1;
+    visual->pasp = pasp;
+    return 0;
+}
+
+int isom_add_colr( isom_visual_entry_t *visual )
+{
+    if( !visual || visual->colr )
+        return -1;
+    isom_create_box( colr, visual, QT_BOX_TYPE_COLR );
+    isom_color_parameter_t *param = (isom_color_parameter_t *)(&isom_color_parameter_tbl[0]);
+    colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
+    colr->primaries_index = param->primaries;
+    colr->transfer_function_index = param->transfer;
+    colr->matrix_index = param->matrix;
+    visual->colr = colr;
+    return 0;
+}
+
+int isom_add_stsl( isom_visual_entry_t *visual )
+{
+    if( !visual || visual->stsl )
+        return -1;
+    isom_create_box( stsl, visual, ISOM_BOX_TYPE_STSL );
+    stsl->scale_method = ISOM_SCALING_METHOD_HIDDEN;
+    visual->stsl = stsl;
+    return 0;
+}
+
+static void isom_remove_esds( isom_esds_t *esds );
+static void isom_remove_visual_extensions( isom_visual_entry_t *visual );
+
+static int isom_add_visual_extensions( isom_visual_entry_t *visual, lsmash_video_summary_t *summary )
+{
+    /* Check if set up Track Aperture Modes. */
+    isom_trak_entry_t *trak = (isom_trak_entry_t *)visual->parent->parent->parent->parent->parent;
+    int qt_compatible = trak->root->qt_compatible;
+    isom_tapt_t *tapt = trak->tapt;
+    int set_aperture_modes = qt_compatible                      /* Track Aperture Modes is only available under QuickTime file format. */
+        && !summary->scaling_method                             /* Sample scaling method might conflict with this feature. */
+        && tapt && tapt->clef && tapt->prof && tapt->enof       /* Check if required boxes exist. */
+        && !((isom_stsd_t *)visual->parent)->list->entry_count; /* Multiple sample description might conflict with this, so in that case, disable this feature.
+                                                                 * Note: this sample description isn't added yet here. */
+    if( !set_aperture_modes )
+        isom_remove_tapt( trak->tapt );
+    /* Set up Clean Aperture. */
+    if( set_aperture_modes || summary->crop_top || summary->crop_left || summary->crop_bottom || summary->crop_right )
+    {
+        if( isom_add_clap( visual ) )
+        {
+            isom_remove_visual_extensions( visual );
+            return -1;
+        }
+        isom_clap_t *clap = visual->clap;
+        clap->cleanApertureWidthN = summary->width - (summary->crop_left + summary->crop_right);
+        clap->cleanApertureHeightN = summary->height - (summary->crop_top + summary->crop_bottom);
+        clap->horizOffN = (int64_t)summary->crop_left - summary->crop_right;
+        clap->vertOffN = (int64_t)summary->crop_top - summary->crop_bottom;
+        if( !(clap->horizOffN & 0x1) )
+        {
+            clap->horizOffN /= 2;
+            clap->horizOffD = 1;
+        }
+        else
+            clap->horizOffD = 2;
+        if( !(clap->vertOffN & 0x1) )
+        {
+            clap->vertOffN /= 2;
+            clap->vertOffD = 1;
+        }
+        else
+            clap->vertOffD = 2;
+    }
+    /* Set up Pixel Aspect Ratio. */
+    if( set_aperture_modes || (summary->par_h && summary->par_v) )
+    {
+        if( isom_add_pasp( visual ) )
+        {
+            isom_remove_visual_extensions( visual );
+            return -1;
+        }
+        isom_pasp_t *pasp = visual->pasp;
+        pasp->hSpacing = summary->par_h;
+        pasp->vSpacing = summary->par_v;
+    }
+    /* Set up Color Parameter. */
+    if( qt_compatible && (summary->primaries || summary->transfer || summary->matrix) )
+    {
+        if( isom_add_colr( visual ) )
+        {
+            isom_remove_visual_extensions( visual );
+            return -1;
+        }
+        isom_colr_t *colr = visual->colr;
+        uint16_t primaries = summary->primaries;
+        uint16_t transfer = summary->transfer;
+        uint16_t matrix = summary->matrix;
+        /* Set 'nclc' to parameter type, we don't support 'prof'. */
+        colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
+        /* primaries */
+        if( primaries >= QT_COLOR_PARAMETER_END )
+            return -1;
+        else if( primaries > UINT16_MAX )
+            colr->primaries_index = isom_color_parameter_tbl[primaries - UINT16_MAX_PLUS_ONE].primaries;
+        else
+            colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) ? primaries : 2;
+        /* transfer */
+        if( transfer >= QT_COLOR_PARAMETER_END )
+            return -1;
+        else if( transfer > UINT16_MAX )
+            colr->transfer_function_index = isom_color_parameter_tbl[transfer - UINT16_MAX_PLUS_ONE].transfer;
+        else
+            colr->transfer_function_index = (transfer == 1 || transfer == 7) ? transfer : 2;
+        /* matrix */
+        if( matrix >= QT_COLOR_PARAMETER_END )
+            return -1;
+        else if( matrix > UINT16_MAX )
+            colr->matrix_index = isom_color_parameter_tbl[matrix - UINT16_MAX_PLUS_ONE].matrix;
+        else
+            colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7 ) ? matrix : 2;
+    }
+    /* Set up Sample Scaling. */
+    if( !qt_compatible && summary->scaling_method )
+    {
+        if( isom_add_stsl( visual ) )
+        {
+            isom_remove_visual_extensions( visual );
+            return -1;
+        }
+        isom_stsl_t *stsl = visual->stsl;
+        stsl->constraint_flag = 1;
+        stsl->scale_method = summary->scaling_method;
+    }
+    /* Set up AVC Decoder Configuration. */
+    static const uint32_t avc_type[] =
+        {
+            ISOM_CODEC_TYPE_AVC1_VIDEO,
+            ISOM_CODEC_TYPE_AVC2_VIDEO,
+            ISOM_CODEC_TYPE_AVCP_VIDEO
+        };
+    for( int i = 0; i < sizeof(avc_type)/sizeof(avc_type[0]); i++ )
+        if( visual->type == avc_type[i] )
+        {
+            if( summary->exdata_length >= 15 )
+            {
+                /* Chech if avcC is constructed as exdata. */
+                uint8_t *exdata = (uint8_t *)summary->exdata;
+                uint32_t length = (exdata[0] << 24) | (exdata[1] << 16) | (exdata[2] << 8) | exdata[3];
+                if( length == summary->exdata_length
+                 && LSMASH_4CC( exdata[4], exdata[5], exdata[6], exdata[7] ) == ISOM_BOX_TYPE_AVCC )
+                {
+                    visual->exdata = lsmash_memdup( summary->exdata, summary->exdata_length );
+                    if( !visual->exdata )
+                        return -1;
+                    visual->exdata_length = summary->exdata_length;
+                    break;
+                }
+            }
+            if( isom_add_avcC( visual ) )
+                return -1;
+            break;
+        }
+    /* Set up Track Apeture Modes. */
+    if( set_aperture_modes )
+    {
+        uint32_t width  = visual->width  << 16;
+        uint32_t height = visual->height << 16;
+        double clap_width  = ((double)visual->clap->cleanApertureWidthN  / visual->clap->cleanApertureWidthD)  * (1<<16);
+        double clap_height = ((double)visual->clap->cleanApertureHeightN / visual->clap->cleanApertureHeightD) * (1<<16);
+        double par = (double)visual->pasp->hSpacing / visual->pasp->vSpacing;
+        if( par >= 1.0 )
+        {
+            tapt->clef->width  = clap_width * par;
+            tapt->clef->height = clap_height;
+            tapt->prof->width  = width * par;
+            tapt->prof->height = height;
+        }
+        else
+        {
+            tapt->clef->width  = clap_width;
+            tapt->clef->height = clap_height / par;
+            tapt->prof->width  = width;
+            tapt->prof->height = height / par;
+        }
+        tapt->enof->width  = width;
+        tapt->enof->height = height;
+    }
+    return 0;
+}
+
+static int isom_add_visual_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_video_summary_t *summary )
+{
+    if( !stsd || !stsd->list || !summary )
+        return -1;
+    lsmash_entry_list_t *list = stsd->list;
+    isom_visual_entry_t *visual = lsmash_malloc_zero( sizeof(isom_visual_entry_t) );
+    if( !visual )
+        return -1;
+    isom_init_box_common( visual, stsd, sample_type );
+    visual->data_reference_index = 1;
+    visual->width = (uint16_t)summary->width;
+    visual->height = (uint16_t)summary->height;
+    visual->horizresolution = visual->vertresolution = 0x00480000;
+    visual->frame_count = 1;
+    switch( sample_type )
+    {
+        case ISOM_CODEC_TYPE_AVC1_VIDEO :
+        case ISOM_CODEC_TYPE_AVC2_VIDEO :
+            strcpy( visual->compressorname, "\012AVC Coding" );
+            break;
+        case ISOM_CODEC_TYPE_AVCP_VIDEO :
+            strcpy( visual->compressorname, "\016AVC Parameters" );
+            break;
+        default :
+            break;
+    }
+    visual->depth = 0x0018;
+    visual->color_table_ID = -1;
+    if( isom_add_visual_extensions( visual, summary )
+     || lsmash_add_entry( list, visual ) )
+    {
+        isom_remove_visual_extensions( visual );
+        free( visual );
+        return -1;
+    }
+    return 0;
+}
+
+#if 0
+static int isom_add_mp4s_entry( isom_stsd_t *stsd )
+{
+    if( !stsd || !stsd->list )
+        return -1;
+    isom_mp4s_entry_t *mp4s = lsmash_malloc_zero( sizeof(isom_mp4s_entry_t) );
+    if( !mp4s )
+        return -1;
+    isom_init_box_common( mp4s, stsd, ISOM_CODEC_TYPE_MP4S_SYSTEM );
+    mp4s->data_reference_index = 1;
+    if( lsmash_add_entry( stsd->list, mp4s ) )
+    {
+        free( mp4s );
+        return -1;
+    }
+    return 0;
+}
+#endif
+
+int isom_add_wave( isom_audio_entry_t *audio )
+{
+    if( !audio || audio->wave )
+        return -1;
+    isom_create_box( wave, audio, QT_BOX_TYPE_WAVE );
+    audio->wave = wave;
+    return 0;
+}
+
+int isom_add_frma( isom_wave_t *wave )
+{
+    if( !wave || wave->frma )
+        return -1;
+    isom_create_box( frma, wave, QT_BOX_TYPE_FRMA );
+    wave->frma = frma;
+    return 0;
+}
+
+int isom_add_enda( isom_wave_t *wave )
+{
+    if( !wave || wave->enda )
+        return -1;
+    isom_create_box( enda, wave, QT_BOX_TYPE_ENDA );
+    wave->enda = enda;
+    return 0;
+}
+
+int isom_add_mp4a( isom_wave_t *wave )
+{
+    if( !wave || wave->mp4a )
+        return -1;
+    isom_create_box( mp4a, wave, QT_BOX_TYPE_MP4A );
+    wave->mp4a = mp4a;
+    return 0;
+}
+
+int isom_add_terminator( isom_wave_t *wave )
+{
+    if( !wave || wave->terminator )
+        return -1;
+    isom_create_box( terminator, wave, QT_BOX_TYPE_TERMINATOR );
+    wave->terminator = terminator;
+    return 0;
+}
+
+int isom_add_chan( isom_audio_entry_t *audio )
+{
+    if( !audio || audio->chan )
+        return -1;
+    isom_create_box( chan, audio, QT_BOX_TYPE_CHAN );
+    chan->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN;
+    audio->chan = chan;
+    return 0;
+}
+
+static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio )
+{
+    lsmash_audio_summary_t *summary = &audio->summary;
+    if( isom_add_wave( audio )
+     || isom_add_frma( audio->wave )
+     || isom_add_mp4a( audio->wave )
+     || isom_add_terminator( audio->wave ) )
+        return -1;
+    audio->data_reference_index = 1;
+    audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1;
+    audio->channelcount = audio->version == 2 ? 3 : LSMASH_MIN( summary->channels, 2 );
+    audio->samplesize = 16;
+    audio->compression_ID = QT_COMPRESSION_ID_VARIABLE_COMPRESSION;
+    audio->packet_size = 0;
+    if( audio->version == 1 )
+    {
+        audio->samplerate = summary->frequency << 16;
+        audio->samplesPerPacket = summary->samples_in_frame;
+        audio->bytesPerPacket = 1;      /* Apparently, this field is set to 1. */
+        audio->bytesPerFrame = audio->bytesPerPacket * summary->channels;
+        audio->bytesPerSample = 1 + (summary->bit_depth != 8);
+    }
+    else    /* audio->version == 2 */
+    {
+        audio->samplerate = 0x00010000;
+        audio->sizeOfStructOnly = 72;
+        audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
+        audio->numAudioChannels = summary->channels;
+        audio->always7F000000 = 0x7F000000;
+        audio->constBitsPerChannel = 0;         /* compressed audio */
+        audio->formatSpecificFlags = 0;
+        audio->constBytesPerAudioPacket = 0;    /* variable */
+        audio->constLPCMFramesPerAudioPacket = summary->samples_in_frame;
+    }
+    audio->wave->frma->data_format = audio->type;
+    /* create ES Descriptor */
+    isom_esds_t *esds = lsmash_malloc_zero( sizeof(isom_esds_t) );
+    if( !esds )
+        return -1;
+    isom_init_box_common( esds, audio->wave, ISOM_BOX_TYPE_ESDS );
+    mp4sys_ES_Descriptor_params_t esd_param;
+    memset( &esd_param, 0, sizeof(mp4sys_ES_Descriptor_params_t) );
+    esd_param.objectTypeIndication = summary->object_type_indication;
+    esd_param.streamType = summary->stream_type;
+    esd_param.dsi_payload = summary->exdata;
+    esd_param.dsi_payload_length = summary->exdata_length;
+    esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
+    if( !esds->ES )
+        return -1;
+    audio->wave->esds = esds;
+    return 0;
+}
+
+static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio )
+{
+    lsmash_audio_summary_t *summary = &audio->summary;
+    if( summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream )
+        return -1;
+    switch( summary->object_type_indication )
+    {
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3:
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile:
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile:
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile:
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3:      /* Legacy Interface */
+        case MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3:      /* Legacy Interface */
+            break;
+        default:
+            return -1;
+    }
+    isom_create_box( esds, audio, ISOM_BOX_TYPE_ESDS );
+    mp4sys_ES_Descriptor_params_t esd_param;
+    esd_param.ES_ID = 0;              /* This is esds internal, so 0 is allowed. */
+    esd_param.objectTypeIndication = summary->object_type_indication;
+    esd_param.streamType = summary->stream_type;
+    esd_param.bufferSizeDB = 0;       /* NOTE: ISO/IEC 14496-3 does not mention this, so we use 0. */
+    esd_param.maxBitrate = 0;         /* This will be updated later if needed. or... I think this can be arbitrary value. */
+    esd_param.avgBitrate = 0;         /* FIXME: 0 if VBR. */
+    esd_param.dsi_payload = summary->exdata;
+    esd_param.dsi_payload_length = summary->exdata_length;
+    esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
+    if( !esds->ES )
+        return -1;
+    audio->data_reference_index = 1;
+    /* WARNING: This field cannot retain frequency above 65535Hz.
+       This is not "FIXME", I just honestly implemented what the spec says.
+       BTW, who ever expects sampling frequency takes fixed-point decimal??? */
+    audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
+    /* In pure mp4 file, these "template" fields shall be default values according to the spec.
+       But not pure - hybrid with other spec - mp4 file can take other values.
+       Which is to say, these template values shall be ignored in terms of mp4, except some object_type_indications.
+       see 14496-14, "Template fields used". */
+    audio->channelcount = 2;
+    audio->samplesize = 16;
+    audio->esds = esds;
+    return 0;
+}
+
+static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio )
+{
+    uint32_t sample_type = audio->type;
+    lsmash_audio_summary_t *summary = &audio->summary;
+    /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */
+    if( (sample_type == QT_CODEC_TYPE_RAW_AUDIO && (summary->bit_depth != 8 || summary->sample_format))
+     || (sample_type == QT_CODEC_TYPE_FL32_AUDIO && (summary->bit_depth != 32 || !summary->sample_format))
+     || (sample_type == QT_CODEC_TYPE_FL64_AUDIO && (summary->bit_depth != 64 || !summary->sample_format))
+     || (sample_type == QT_CODEC_TYPE_IN24_AUDIO && (summary->bit_depth != 24 || summary->sample_format))
+     || (sample_type == QT_CODEC_TYPE_IN32_AUDIO && (summary->bit_depth != 32 || summary->sample_format))
+     || (sample_type == QT_CODEC_TYPE_23NI_AUDIO && (summary->bit_depth != 32 || summary->sample_format || !summary->endianness))
+     || (sample_type == QT_CODEC_TYPE_SOWT_AUDIO && (summary->bit_depth != 16 || summary->sample_format || !summary->endianness))
+     || (sample_type == QT_CODEC_TYPE_TWOS_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
+     || (sample_type == QT_CODEC_TYPE_NONE_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
+     || (sample_type == QT_CODEC_TYPE_NOT_SPECIFIED && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
+     || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->bit_depth % 8) )
+    {
+        audio->type = QT_CODEC_TYPE_LPCM_AUDIO;
+        audio->version = 2;
+    }
+    else if( sample_type == QT_CODEC_TYPE_LPCM_AUDIO )
+        audio->version = 2;
+    else if( summary->bit_depth > 16
+     || (sample_type != QT_CODEC_TYPE_RAW_AUDIO && sample_type != QT_CODEC_TYPE_TWOS_AUDIO
+     && sample_type != QT_CODEC_TYPE_NONE_AUDIO && sample_type != QT_CODEC_TYPE_NOT_SPECIFIED) )
+        audio->version = 1;
+    audio->data_reference_index = 1;
+    /* Set up constBytesPerAudioPacket field.
+     * We use constBytesPerAudioPacket as the actual size of audio frame even when version is not 2. */
+    audio->constBytesPerAudioPacket = (summary->bit_depth * summary->channels) / 8;
+    /* Set up other fields in this description by its version. */
+    if( audio->version == 2 )
+    {
+        audio->channelcount = 3;
+        audio->samplesize = 16;
+        audio->compression_ID = -2;
+        audio->samplerate = 0x00010000;
+        audio->sizeOfStructOnly = 72;
+        audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
+        audio->numAudioChannels = summary->channels;
+        audio->always7F000000 = 0x7F000000;
+        audio->constBitsPerChannel = summary->bit_depth;
+        audio->constLPCMFramesPerAudioPacket = 1;
+        if( summary->sample_format )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_FLOAT;
+        if( sample_type == QT_CODEC_TYPE_TWOS_AUDIO || !summary->endianness )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN;
+        if( !summary->sample_format && summary->signedness )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER;
+        if( summary->packed )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_PACKED;
+        if( !summary->packed && summary->alignment )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH;
+        if( !summary->interleaved )
+            audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED;
+    }
+    else if( audio->version == 1 )
+    {
+        audio->channelcount = summary->channels;
+        audio->samplesize = 16;
+        /* Audio formats other than 'raw ' and 'twos' are treated as compressed audio. */
+        if( sample_type == QT_CODEC_TYPE_RAW_AUDIO || sample_type == QT_CODEC_TYPE_TWOS_AUDIO )
+            audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
+        else
+            audio->compression_ID = QT_COMPRESSION_ID_FIXED_COMPRESSION;
+        audio->samplerate = summary->frequency << 16;
+        audio->samplesPerPacket = 1;
+        audio->bytesPerPacket = summary->bit_depth / 8;
+        audio->bytesPerFrame = audio->bytesPerPacket * summary->channels;   /* sample_size field in stsz box is NOT used. */
+        audio->bytesPerSample = 1 + (summary->bit_depth != 8);
+        if( sample_type == QT_CODEC_TYPE_FL32_AUDIO || sample_type == QT_CODEC_TYPE_FL64_AUDIO
+         || sample_type == QT_CODEC_TYPE_IN24_AUDIO || sample_type == QT_CODEC_TYPE_IN32_AUDIO )
+        {
+            if( isom_add_wave( audio )
+             || isom_add_frma( audio->wave )
+             || isom_add_enda( audio->wave )
+             || isom_add_terminator( audio->wave ) )
+                return -1;
+            audio->wave->frma->data_format = sample_type;
+            audio->wave->enda->littleEndian = summary->endianness;
+        }
+    }
+    else    /* audio->version == 0 */
+    {
+        audio->channelcount = summary->channels;
+        audio->samplesize = summary->bit_depth;
+        audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
+        audio->samplerate = summary->frequency << 16;
+    }
+    return 0;
+}
+
+static int isom_set_extra_description( isom_audio_entry_t *audio )
+{
+    lsmash_audio_summary_t *summary = &audio->summary;
+    audio->data_reference_index = 1;
+    audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
+    audio->channelcount = 2;
+    audio->samplesize = 16;
+    if( summary->exdata )
+    {
+        audio->exdata = lsmash_memdup( summary->exdata, summary->exdata_length );
+        if( !audio->exdata )
+            return -1;
+        audio->exdata_length = summary->exdata_length;
+    }
+    else
+    {
+        /* No CODEC Specific Info */
+        switch( audio->type )
+        {
+            case ISOM_CODEC_TYPE_AC_3_AUDIO :
+            case ISOM_CODEC_TYPE_ALAC_AUDIO :
+            case ISOM_CODEC_TYPE_EC_3_AUDIO :
+            case ISOM_CODEC_TYPE_SAMR_AUDIO :
+            case ISOM_CODEC_TYPE_SAWB_AUDIO :
+                return -1;
+            default :
+                break;
+        }
+        audio->exdata = NULL;
+        audio->exdata_length = 0;
+    }
+    return 0;
+}
+
+static int isom_add_audio_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_audio_summary_t *summary )
+{
+    if( !stsd || !stsd->list || !summary )
+        return -1;
+    isom_audio_entry_t *audio = lsmash_malloc_zero( sizeof(isom_audio_entry_t) );
+    if( !audio )
+        return -1;
+    isom_init_box_common( audio, stsd, sample_type );
+    memcpy( &audio->summary, summary, sizeof(lsmash_audio_summary_t) );
+    int ret = 0;
+    lsmash_root_t *root = stsd->root;
+    if( sample_type == ISOM_CODEC_TYPE_MP4A_AUDIO )
+    {
+        if( root->ftyp && root->ftyp->major_brand == ISOM_BRAND_TYPE_QT )
+            ret = isom_set_qtff_mp4a_description( audio );
+        else
+            ret = isom_set_isom_mp4a_description( audio );
+    }
+    else if( isom_is_lpcm_audio( sample_type ) )
+        ret = isom_set_qtff_lpcm_description( audio );
+    else
+        ret = isom_set_extra_description( audio );
+    if( ret )
+        goto fail;
+    if( root->qt_compatible )
+    {
+        lsmash_channel_layout_tag layout_tag = summary->layout_tag;
+        lsmash_channel_bitmap bitmap = summary->bitmap;
+        if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS    /* We don't support the feature of Channel Descriptions. */
+         || (layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!bitmap || bitmap > QT_CHANNEL_BIT_FULL)) )
+        {
+            layout_tag = summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN | summary->channels;
+            bitmap = summary->bitmap = 0;
+        }
+        /* Don't create Channel Compositor Box if the channel layout is unknown. */
+        if( (layout_tag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 )
+        {
+            if( isom_add_chan( audio ) )
+                goto fail;
+            audio->chan->channelLayoutTag = layout_tag;
+            audio->chan->channelBitmap = bitmap;
+        }
+    }
+    if( lsmash_add_entry( stsd->list, audio ) )
+        goto fail;
+    return 0;
+fail:
+    isom_remove_esds( audio->esds );
+    isom_remove_wave( audio->wave );
+    isom_remove_chan( audio->chan );
+    if( audio->exdata )
+        free( audio->exdata );
+    free( audio );
+    return -1;
+}
+
+static int isom_add_text_entry( isom_stsd_t *stsd )
+{
+    if( !stsd || !stsd->list )
+        return -1;
+    isom_text_entry_t *text = lsmash_malloc_zero( sizeof(isom_text_entry_t) );
+    if( !text )
+        return -1;
+    isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT );
+    text->data_reference_index = 1;
+    if( lsmash_add_entry( stsd->list, text ) )
+    {
+        free( text );
+        return -1;
+    }
+    return 0;
+}
+
+int isom_add_ftab( isom_tx3g_entry_t *tx3g )
+{
+    if( !tx3g )
+        return -1;
+    isom_ftab_t *ftab = lsmash_malloc_zero( sizeof(isom_ftab_t) );
+    if( !ftab )
+        return -1;
+    isom_init_box_common( ftab, tx3g, ISOM_BOX_TYPE_FTAB );
+    ftab->list = lsmash_create_entry_list();
+    if( !ftab->list )
+    {
+        free( ftab );
+        return -1;
+    }
+    tx3g->ftab = ftab;
+    return 0;
+}
+
+static int isom_add_tx3g_entry( isom_stsd_t *stsd )
+{
+    if( !stsd || !stsd->list )
+        return -1;
+    isom_tx3g_entry_t *tx3g = lsmash_malloc_zero( sizeof(isom_tx3g_entry_t) );
+    if( !tx3g )
+        return -1;
+    isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT );
+    tx3g->data_reference_index = 1;
+    if( isom_add_ftab( tx3g ) ||
+        lsmash_add_entry( stsd->list, tx3g ) )
+    {
+        free( tx3g );
+        return -1;
+    }
+    return 0;
+}
+
+/* This function returns 0 if failed, sample_entry_number if succeeded. */
+int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_type, void *summary )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->root || !trak->root->ftyp || !trak->mdia || !trak->mdia->minf
+     || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return 0;
+    isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
+    lsmash_entry_list_t *list = stsd->list;
+    int ret = -1;
+    switch( sample_type )
+    {
+        case ISOM_CODEC_TYPE_AVC1_VIDEO :
+#if 0
+        case ISOM_CODEC_TYPE_AVC2_VIDEO :
+        case ISOM_CODEC_TYPE_AVCP_VIDEO :
+        case ISOM_CODEC_TYPE_SVC1_VIDEO :
+        case ISOM_CODEC_TYPE_MVC1_VIDEO :
+        case ISOM_CODEC_TYPE_MVC2_VIDEO :
+        case ISOM_CODEC_TYPE_MP4V_VIDEO :
+        case ISOM_CODEC_TYPE_DRAC_VIDEO :
+        case ISOM_CODEC_TYPE_ENCV_VIDEO :
+        case ISOM_CODEC_TYPE_MJP2_VIDEO :
+        case ISOM_CODEC_TYPE_S263_VIDEO :
+        case ISOM_CODEC_TYPE_VC_1_VIDEO :
+#endif
+            ret = isom_add_visual_entry( stsd, sample_type, (lsmash_video_summary_t *)summary );
+            break;
+#if 0
+        case ISOM_CODEC_TYPE_MP4S_SYSTEM :
+            ret = isom_add_mp4s_entry( stsd );
+            break;
+#endif
+        case ISOM_CODEC_TYPE_MP4A_AUDIO :
+        case ISOM_CODEC_TYPE_AC_3_AUDIO :
+        case ISOM_CODEC_TYPE_ALAC_AUDIO :
+        case ISOM_CODEC_TYPE_EC_3_AUDIO :
+        case ISOM_CODEC_TYPE_SAMR_AUDIO :
+        case ISOM_CODEC_TYPE_SAWB_AUDIO :
+        case QT_CODEC_TYPE_23NI_AUDIO :
+        case QT_CODEC_TYPE_NONE_AUDIO :
+        case QT_CODEC_TYPE_LPCM_AUDIO :
+        case QT_CODEC_TYPE_RAW_AUDIO :
+        case QT_CODEC_TYPE_SOWT_AUDIO :
+        case QT_CODEC_TYPE_TWOS_AUDIO :
+        case QT_CODEC_TYPE_FL32_AUDIO :
+        case QT_CODEC_TYPE_FL64_AUDIO :
+        case QT_CODEC_TYPE_IN24_AUDIO :
+        case QT_CODEC_TYPE_IN32_AUDIO :
+        case QT_CODEC_TYPE_NOT_SPECIFIED :
+#if 0
+        case ISOM_CODEC_TYPE_DRA1_AUDIO :
+        case ISOM_CODEC_TYPE_DTSC_AUDIO :
+        case ISOM_CODEC_TYPE_DTSH_AUDIO :
+        case ISOM_CODEC_TYPE_DTSL_AUDIO :
+        case ISOM_CODEC_TYPE_ENCA_AUDIO :
+        case ISOM_CODEC_TYPE_G719_AUDIO :
+        case ISOM_CODEC_TYPE_G726_AUDIO :
+        case ISOM_CODEC_TYPE_M4AE_AUDIO :
+        case ISOM_CODEC_TYPE_MLPA_AUDIO :
+        case ISOM_CODEC_TYPE_RAW_AUDIO  :
+        case ISOM_CODEC_TYPE_SAWP_AUDIO :
+        case ISOM_CODEC_TYPE_SEVC_AUDIO :
+        case ISOM_CODEC_TYPE_SQCP_AUDIO :
+        case ISOM_CODEC_TYPE_SSMV_AUDIO :
+        case ISOM_CODEC_TYPE_TWOS_AUDIO :
+#endif
+            ret = isom_add_audio_entry( stsd, sample_type, (lsmash_audio_summary_t *)summary );
+            break;
+        case ISOM_CODEC_TYPE_TX3G_TEXT :
+            ret = isom_add_tx3g_entry( stsd );
+            break;
+        case QT_CODEC_TYPE_TEXT_TEXT :
+            ret = isom_add_text_entry( stsd );
+            break;
+        default :
+            return 0;
+    }
+    return ret ? 0 : list->entry_count;
+}
+
+static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
+{
+    if( !stbl || !stbl->stts || !stbl->stts->list )
+        return -1;
+    isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) );
+    if( !data )
+        return -1;
+    data->sample_count = 1;
+    data->sample_delta = sample_delta;
+    if( lsmash_add_entry( stbl->stts->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
+{
+    if( !stbl || !stbl->ctts || !stbl->ctts->list )
+        return -1;
+    isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) );
+    if( !data )
+        return -1;
+    data->sample_count = 1;
+    data->sample_offset = sample_offset;
+    if( lsmash_add_entry( stbl->ctts->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
+{
+    if( !stbl || !stbl->stsc || !stbl->stsc->list )
+        return -1;
+    isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) );
+    if( !data )
+        return -1;
+    data->first_chunk = first_chunk;
+    data->samples_per_chunk = samples_per_chunk;
+    data->sample_description_index = sample_description_index;
+    if( lsmash_add_entry( stbl->stsc->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
+{
+    if( !stbl || !stbl->stsz )
+        return -1;
+    isom_stsz_t *stsz = stbl->stsz;
+    /* retrieve initial sample_size */
+    if( !stsz->sample_count )
+        stsz->sample_size = entry_size;
+    /* if it seems constant access_unit size at present, update sample_count only */
+    if( !stsz->list && stsz->sample_size == entry_size )
+    {
+        ++ stsz->sample_count;
+        return 0;
+    }
+    /* found sample_size varies, create sample_size list */
+    if( !stsz->list )
+    {
+        stsz->list = lsmash_create_entry_list();
+        if( !stsz->list )
+            return -1;
+        for( uint32_t i = 0; i < stsz->sample_count; i++ )
+        {
+            isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
+            if( !data )
+                return -1;
+            data->entry_size = stsz->sample_size;
+            if( lsmash_add_entry( stsz->list, data ) )
+            {
+                free( data );
+                return -1;
+            }
+        }
+        stsz->sample_size = 0;
+    }
+    isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
+    if( !data )
+        return -1;
+    data->entry_size = entry_size;
+    if( lsmash_add_entry( stsz->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    ++ stsz->sample_count;
+    return 0;
+}
+
+static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
+{
+    if( !stbl || !stbl->stss || !stbl->stss->list )
+        return -1;
+    isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) );
+    if( !data )
+        return -1;
+    data->sample_number = sample_number;
+    if( lsmash_add_entry( stbl->stss->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
+{
+    if( !stbl || !stbl->stps || !stbl->stps->list )
+        return -1;
+    isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) );
+    if( !data )
+        return -1;
+    data->sample_number = sample_number;
+    if( lsmash_add_entry( stbl->stps->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_sdtp_entry( isom_stbl_t *stbl, lsmash_sample_property_t *prop, uint8_t avc_extensions )
+{
+    if( !prop )
+        return -1;
+    if( !stbl || !stbl->sdtp || !stbl->sdtp->list )
+        return -1;
+    isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) );
+    if( !data )
+        return -1;
+    /* isom_sdtp_entry_t is smaller than lsmash_sample_property_t. */
+    data->is_leading = (avc_extensions ? prop->leading : prop->allow_earlier) & 0x03;
+    data->sample_depends_on = prop->independent & 0x03;
+    data->sample_is_depended_on = prop->disposable & 0x03;
+    data->sample_has_redundancy = prop->redundant & 0x03;
+    if( lsmash_add_entry( stbl->sdtp->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_co64( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stco )
+        return -1;
+    isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_CO64 );
+    stco->large_presentation = 1;
+    stbl->stco = stco;
+    return 0;
+}
+
+static int isom_add_stco( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stco )
+        return -1;
+    isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_STCO );
+    stco->large_presentation = 0;
+    stbl->stco = stco;
+    return 0;
+}
+
+static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
+{
+    if( !stbl || !stbl->stco || !stbl->stco->list )
+        return -1;
+    isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) );
+    if( !data )
+        return -1;
+    data->chunk_offset = chunk_offset;
+    if( lsmash_add_entry( stbl->stco->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_convert_stco_to_co64( isom_stbl_t* stbl )
+{
+    /* backup stco */
+    isom_stco_t *stco = stbl->stco;
+    stbl->stco = NULL;
+    if( isom_add_co64( stbl ) )
+        return -1;
+    /* move chunk_offset to co64 from stco */
+    for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
+    {
+        isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
+        if( isom_add_co64_entry( stbl, data->chunk_offset ) )
+            return -1;
+    }
+    lsmash_remove_list( stco->list, NULL );
+    free( stco );
+    return 0;
+}
+
+static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
+{
+    if( !stbl || !stbl->stco || !stbl->stco->list )
+        return -1;
+    if( stbl->stco->large_presentation )
+        return isom_add_co64_entry( stbl, chunk_offset );
+    if( chunk_offset > UINT32_MAX )
+    {
+        if( isom_convert_stco_to_co64( stbl ) )
+            return -1;
+        return isom_add_co64_entry( stbl, chunk_offset );
+    }
+    isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) );
+    if( !data )
+        return -1;
+    data->chunk_offset = (uint32_t)chunk_offset;
+    if( lsmash_add_entry( stbl->stco->list, data ) )
+    {
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
+{
+    if( !stbl->sgpd_list )
+        return NULL;
+    for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
+    {
+        isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
+        if( !sgpd || !sgpd->list )
+            return NULL;
+        if( sgpd->grouping_type == grouping_type )
+            return sgpd;
+    }
+    return NULL;
+}
+
+isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
+{
+    if( !stbl->sbgp_list )
+        return NULL;
+    for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
+    {
+        isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)entry->data;
+        if( !sbgp || !sbgp->list )
+            return NULL;
+        if( sbgp->grouping_type == grouping_type )
+            return sbgp;
+    }
+    return NULL;
+}
+
+static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_entry_t *sgpd )
+{
+    if( !sgpd )
+        return NULL;
+    isom_rap_entry_t *data = malloc( sizeof(isom_rap_entry_t) );
+     if( !data )
+        return NULL;
+    data->description_length = 0;
+    data->num_leading_samples_known = 0;
+    data->num_leading_samples = 0;
+    if( lsmash_add_entry( sgpd->list, data ) )
+    {
+        free( data );
+        return NULL;
+    }
+    return data;
+}
+
+static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_entry_t *sgpd, int16_t roll_distance )
+{
+    if( !sgpd )
+        return NULL;
+    isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) );
+     if( !data )
+        return NULL;
+    data->description_length = 0;
+    data->roll_distance = roll_distance;
+    if( lsmash_add_entry( sgpd->list, data ) )
+    {
+        free( data );
+        return NULL;
+    }
+    return data;
+}
+
+static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_entry_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
+{
+    if( !sbgp )
+        return NULL;
+    isom_group_assignment_entry_t *data = malloc( sizeof(isom_group_assignment_entry_t) );
+    if( !data )
+        return NULL;
+    data->sample_count = sample_count;
+    data->group_description_index = group_description_index;
+    if( lsmash_add_entry( sbgp->list, data ) )
+    {
+        free( data );
+        return NULL;
+    }
+    return data;
+}
+
+static int isom_add_chpl_entry( isom_chpl_t *chpl, isom_chapter_entry_t *chap_data )
+{
+    if( !chap_data->chapter_name || !chpl || !chpl->list )
+        return -1;
+    isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) );
+    if( !data )
+        return -1;
+    data->start_time = chap_data->start_time;
+    data->chapter_name_length = strlen( chap_data->chapter_name );
+    data->chapter_name = (char *)malloc( data->chapter_name_length + 1 );
+    if( !data->chapter_name )
+    {
+        free( data );
+        return -1;
+    }
+    memcpy( data->chapter_name, chap_data->chapter_name, data->chapter_name_length );
+    data->chapter_name[data->chapter_name_length] = '\0';
+    if( lsmash_add_entry( chpl->list, data ) )
+    {
+        free( data->chapter_name );
+        free( data );
+        return -1;
+    }
+    return 0;
+}
+
+static isom_trex_entry_t *isom_add_trex( isom_mvex_t *mvex )
+{
+    if( !mvex )
+        return NULL;
+    if( !mvex->trex_list )
+    {
+        mvex->trex_list = lsmash_create_entry_list();
+        if( !mvex->trex_list )
+            return NULL;
+    }
+    isom_trex_entry_t *trex = lsmash_malloc_zero( sizeof(isom_trex_entry_t) );
+    if( !trex )
+        return NULL;
+    isom_init_box_common( trex, mvex, ISOM_BOX_TYPE_TREX );
+    if( lsmash_add_entry( mvex->trex_list, trex ) )
+    {
+        free( trex );
+        return NULL;
+    }
+    return trex;
+}
+
+static isom_trun_entry_t *isom_add_trun( isom_traf_entry_t *traf )
+{
+    if( !traf )
+        return NULL;
+    if( !traf->trun_list )
+    {
+        traf->trun_list = lsmash_create_entry_list();
+        if( !traf->trun_list )
+            return NULL;
+    }
+    isom_trun_entry_t *trun = lsmash_malloc_zero( sizeof(isom_trun_entry_t) );
+    if( !trun )
+        return NULL;
+    isom_init_box_common( trun, traf, ISOM_BOX_TYPE_TRUN );
+    if( lsmash_add_entry( traf->trun_list, trun ) )
+    {
+        free( trun );
+        return NULL;
+    }
+    return trun;
+}
+
+static isom_traf_entry_t *isom_add_traf( lsmash_root_t *root, isom_moof_entry_t *moof )
+{
+    if( !root || !root->moof_list || !moof )
+        return NULL;
+    if( !moof->traf_list )
+    {
+        moof->traf_list = lsmash_create_entry_list();
+        if( !moof->traf_list )
+            return NULL;
+    }
+    isom_traf_entry_t *traf = lsmash_malloc_zero( sizeof(isom_traf_entry_t) );
+    if( !traf )
+        return NULL;
+    isom_init_box_common( traf, moof, ISOM_BOX_TYPE_TRAF );
+    isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
+    if( !cache )
+    {
+        free( traf );
+        return NULL;
+    }
+    memset( cache, 0, sizeof(isom_cache_t) );
+    if( lsmash_add_entry( moof->traf_list, traf ) )
+    {
+        free( cache );
+        free( traf );
+        return NULL;
+    }
+    traf->cache = cache;
+    return traf;
+}
+
+static isom_moof_entry_t *isom_add_moof( lsmash_root_t *root )
+{
+    if( !root )
+        return NULL;
+    if( !root->moof_list )
+    {
+        root->moof_list = lsmash_create_entry_list();
+        if( !root->moof_list )
+            return NULL;
+    }
+    isom_moof_entry_t *moof = lsmash_malloc_zero( sizeof(isom_moof_entry_t) );
+    if( !moof )
+        return NULL;
+    isom_init_box_common( moof, root, ISOM_BOX_TYPE_MOOF );
+    if( lsmash_add_entry( root->moof_list, moof ) )
+    {
+        free( moof );
+        return NULL;
+    }
+    return moof;
+}
+
+static isom_tfra_entry_t *isom_add_tfra( isom_mfra_t *mfra )
+{
+    if( !mfra )
+        return NULL;
+    if( !mfra->tfra_list )
+    {
+        mfra->tfra_list = lsmash_create_entry_list();
+        if( !mfra->tfra_list )
+            return NULL;
+    }
+    isom_tfra_entry_t *tfra = lsmash_malloc_zero( sizeof(isom_tfra_entry_t) );
+    if( !tfra )
+        return NULL;
+    isom_init_box_common( tfra, mfra, ISOM_BOX_TYPE_TFRA );
+    if( lsmash_add_entry( mfra->tfra_list, tfra ) )
+    {
+        free( tfra );
+        return NULL;
+    }
+    return tfra;
+}
+
+static int isom_add_ftyp( lsmash_root_t *root )
+{
+    if( root->ftyp )
+        return -1;
+    isom_create_box( ftyp, root, ISOM_BOX_TYPE_FTYP );
+    ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
+    root->ftyp = ftyp;
+    return 0;
+}
+
+static int isom_add_moov( lsmash_root_t *root )
+{
+    if( root->moov )
+        return -1;
+    isom_create_box( moov, root, ISOM_BOX_TYPE_MOOV );
+    root->moov = moov;
+    return 0;
+}
+
+static int isom_add_mvhd( isom_moov_t *moov )
+{
+    if( !moov || moov->mvhd )
+        return -1;
+    isom_create_box( mvhd, moov, ISOM_BOX_TYPE_MVHD );
+    mvhd->rate = 0x00010000;
+    mvhd->volume = 0x0100;
+    mvhd->matrix[0] = 0x00010000;
+    mvhd->matrix[4] = 0x00010000;
+    mvhd->matrix[8] = 0x40000000;
+    mvhd->next_track_ID = 1;
+    moov->mvhd = mvhd;
+    return 0;
+}
+
+static int isom_scan_trak_profileLevelIndication( isom_trak_entry_t* trak, mp4a_audioProfileLevelIndication* audio_pli, mp4sys_visualProfileLevelIndication* visual_pli )
+{
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
+        return -1;
+    isom_stsd_t* stsd = trak->mdia->minf->stbl->stsd;
+    if( !stsd || !stsd->list || !stsd->list->head )
+        return -1;
+    for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
+    {
+        isom_sample_entry_t* sample_entry = (isom_sample_entry_t*)entry->data;
+        if( !sample_entry )
+            return -1;
+        switch( sample_entry->type )
+        {
+            case ISOM_CODEC_TYPE_AVC1_VIDEO :
+#if 0
+            case ISOM_CODEC_TYPE_AVC2_VIDEO :
+            case ISOM_CODEC_TYPE_AVCP_VIDEO :
+            case ISOM_CODEC_TYPE_SVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC2_VIDEO :
+#endif
+                /* FIXME: Do we have to arbitrate like audio? */
+                if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
+                    *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
+                break;
+            case ISOM_CODEC_TYPE_MP4A_AUDIO :
+            {
+                isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
+#ifdef LSMASH_DEMUXER_ENABLED
+                if( !audio->esds || !audio->esds->ES )
+                    return -1;
+                if( audio->summary.sample_type != ISOM_CODEC_TYPE_MP4A_AUDIO )
+                    /* This is needed when copying descriptions. */
+                    mp4sys_setup_summary_from_DecoderSpecificInfo( &audio->summary, audio->esds->ES );
+#endif
+                *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( &audio->summary ) );
+                break;
+            }
+#if 0
+            case ISOM_CODEC_TYPE_DRAC_VIDEO :
+            case ISOM_CODEC_TYPE_ENCV_VIDEO :
+            case ISOM_CODEC_TYPE_MJP2_VIDEO :
+            case ISOM_CODEC_TYPE_S263_VIDEO :
+            case ISOM_CODEC_TYPE_VC_1_VIDEO :
+                /* FIXME: Do we have to arbitrate like audio? */
+                if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
+                    *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
+                break;
+#endif
+            case ISOM_CODEC_TYPE_AC_3_AUDIO :
+            case ISOM_CODEC_TYPE_ALAC_AUDIO :
+            case ISOM_CODEC_TYPE_EC_3_AUDIO :
+            case ISOM_CODEC_TYPE_SAMR_AUDIO :
+            case ISOM_CODEC_TYPE_SAWB_AUDIO :
+#if 0
+            case ISOM_CODEC_TYPE_DRA1_AUDIO :
+            case ISOM_CODEC_TYPE_DTSC_AUDIO :
+            case ISOM_CODEC_TYPE_DTSH_AUDIO :
+            case ISOM_CODEC_TYPE_DTSL_AUDIO :
+            case ISOM_CODEC_TYPE_ENCA_AUDIO :
+            case ISOM_CODEC_TYPE_G719_AUDIO :
+            case ISOM_CODEC_TYPE_G726_AUDIO :
+            case ISOM_CODEC_TYPE_M4AE_AUDIO :
+            case ISOM_CODEC_TYPE_MLPA_AUDIO :
+            case ISOM_CODEC_TYPE_RAW_AUDIO :
+            case ISOM_CODEC_TYPE_SAWP_AUDIO :
+            case ISOM_CODEC_TYPE_SEVC_AUDIO :
+            case ISOM_CODEC_TYPE_SQCP_AUDIO :
+            case ISOM_CODEC_TYPE_SSMV_AUDIO :
+            case ISOM_CODEC_TYPE_TWOS_AUDIO :
+#endif
+                /* NOTE: These audio codecs other than mp4a does not have appropriate pli. */
+                *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
+                break;
+#if 0
+            case ISOM_CODEC_TYPE_FDP_HINT :
+            case ISOM_CODEC_TYPE_M2TS_HINT :
+            case ISOM_CODEC_TYPE_PM2T_HINT :
+            case ISOM_CODEC_TYPE_PRTP_HINT :
+            case ISOM_CODEC_TYPE_RM2T_HINT :
+            case ISOM_CODEC_TYPE_RRTP_HINT :
+            case ISOM_CODEC_TYPE_RSRP_HINT :
+            case ISOM_CODEC_TYPE_RTP_HINT  :
+            case ISOM_CODEC_TYPE_SM2T_HINT :
+            case ISOM_CODEC_TYPE_SRTP_HINT :
+                /* FIXME: Do we have to set OD_profileLevelIndication? */
+                break;
+            case ISOM_CODEC_TYPE_IXSE_META :
+            case ISOM_CODEC_TYPE_METT_META :
+            case ISOM_CODEC_TYPE_METX_META :
+            case ISOM_CODEC_TYPE_MLIX_META :
+            case ISOM_CODEC_TYPE_OKSD_META :
+            case ISOM_CODEC_TYPE_SVCM_META :
+            case ISOM_CODEC_TYPE_TEXT_META :
+            case ISOM_CODEC_TYPE_URIM_META :
+            case ISOM_CODEC_TYPE_XML_META  :
+                /* FIXME: Do we have to set OD_profileLevelIndication? */
+                break;
+#endif
+        }
+    }
+    return 0;
+}
+
+static int isom_add_iods( isom_moov_t *moov )
+{
+    if( !moov || !moov->trak_list || moov->iods )
+        return -1;
+    isom_create_box( iods, moov, ISOM_BOX_TYPE_IODS );
+    iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
+    if( !iods->OD )
+    {
+        free( iods );
+        return -1;
+    }
+    mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
+    mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
+    for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
+        if( !trak || !trak->tkhd )
+            return -1;
+        if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) )
+            return -1;
+        if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) )
+            return -1;
+    }
+    if( mp4sys_to_InitialObjectDescriptor( iods->OD,
+                                           0, /* FIXME: I'm not quite sure what the spec says. */
+                                           MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
+                                           audio_pli, visual_pli,
+                                           MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) )
+    {
+        free( iods );
+        return -1;
+    }
+    moov->iods = iods;
+    return 0;
+}
+
+static int isom_add_tkhd( isom_trak_entry_t *trak, uint32_t handler_type )
+{
+    if( !trak || !trak->root || !trak->root->moov || !trak->root->moov->mvhd || !trak->root->moov->trak_list )
+        return -1;
+    if( !trak->tkhd )
+    {
+        isom_create_box( tkhd, trak, ISOM_BOX_TYPE_TKHD );
+        if( handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
+            tkhd->volume = 0x0100;
+        tkhd->matrix[0] = 0x00010000;
+        tkhd->matrix[4] = 0x00010000;
+        tkhd->matrix[8] = 0x40000000;
+        tkhd->duration = 0xffffffff;
+        tkhd->track_ID = trak->root->moov->mvhd->next_track_ID;
+        ++ trak->root->moov->mvhd->next_track_ID;
+        trak->tkhd = tkhd;
+    }
+    return 0;
+}
+
+static int isom_add_clef( isom_tapt_t *tapt )
+{
+    if( tapt->clef )
+        return 0;
+    isom_create_box( clef, tapt, QT_BOX_TYPE_CLEF );
+    tapt->clef = clef;
+    return 0;
+}
+
+static int isom_add_prof( isom_tapt_t *tapt )
+{
+    if( tapt->prof )
+        return 0;
+    isom_create_box( prof, tapt, QT_BOX_TYPE_PROF );
+    tapt->prof = prof;
+    return 0;
+}
+
+static int isom_add_enof( isom_tapt_t *tapt )
+{
+    if( tapt->enof )
+        return 0;
+    isom_create_box( enof, tapt, QT_BOX_TYPE_ENOF );
+    tapt->enof = enof;
+    return 0;
+}
+
+static int isom_add_tapt( isom_trak_entry_t *trak )
+{
+    if( trak->tapt )
+        return 0;
+    isom_create_box( tapt, trak, QT_BOX_TYPE_TAPT );
+    trak->tapt = tapt;
+    return 0;
+}
+
+int isom_add_elst( isom_edts_t *edts )
+{
+    if( edts->elst )
+        return 0;
+    isom_create_list_box( elst, edts, ISOM_BOX_TYPE_ELST );
+    edts->elst = elst;
+    return 0;
+}
+
+int isom_add_edts( isom_trak_entry_t *trak )
+{
+    if( trak->edts )
+        return 0;
+    isom_create_box( edts, trak, ISOM_BOX_TYPE_EDTS );
+    trak->edts = edts;
+    return 0;
+}
+
+static int isom_add_tref( isom_trak_entry_t *trak )
+{
+    if( trak->tref )
+        return 0;
+    isom_create_box( tref, trak, ISOM_BOX_TYPE_TREF );
+    tref->ref_list = lsmash_create_entry_list();
+    if( !tref->ref_list )
+    {
+        free( tref );
+        return -1;
+    }
+    trak->tref = tref;
+    return 0;
+}
+
+static int isom_add_mdhd( isom_mdia_t *mdia, uint16_t default_language )
+{
+    if( !mdia || mdia->mdhd )
+        return -1;
+    isom_create_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD );
+    mdhd->language = default_language;
+    mdia->mdhd = mdhd;
+    return 0;
+}
+
+static int isom_add_mdia( isom_trak_entry_t *trak )
+{
+    if( !trak || trak->mdia )
+        return -1;
+    isom_create_box( mdia, trak, ISOM_BOX_TYPE_MDIA );
+    trak->mdia = mdia;
+    return 0;
+}
+
+static int isom_add_hdlr( isom_mdia_t *mdia, isom_meta_t *meta, isom_minf_t *minf, uint32_t media_type )
+{
+    if( (!mdia && !meta && !minf) || (mdia && meta) || (meta && minf) || (minf && mdia) )
+        return -1;    /* Either one must be given. */
+    if( (mdia && mdia->hdlr) || (meta && meta->hdlr) || (minf && minf->hdlr) )
+        return -1;    /* Selected one must not have hdlr yet. */
+    isom_box_t *parent = mdia ? (isom_box_t *)mdia : meta ? (isom_box_t *)meta : (isom_box_t *)minf;
+    isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR );
+    lsmash_root_t *root = hdlr->root;
+    uint32_t type = mdia ? (root->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
+    uint32_t subtype = media_type;
+    hdlr->componentType = type;
+    hdlr->componentSubtype = subtype;
+    char *type_name = NULL;
+    char *subtype_name = NULL;
+    uint8_t type_name_length = 0;
+    uint8_t subtype_name_length = 0;
+    if( mdia )
+        type_name = "Media ";
+    else if( meta )
+        type_name = "Metadata ";
+    else    /* if( minf ) */
+        type_name = "Data ";
+    type_name_length = strlen( type_name );
+    struct
+    {
+        uint32_t subtype;
+        char    *subtype_name;
+        uint8_t  subtype_name_length;
+    } subtype_table[] =
+        {
+            { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK,          "Sound ",    6 },
+            { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK,          "Video",     6 },
+            { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK,           "Hint ",     5 },
+            { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
+            { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK,           "Text ",     5 },
+            { ISOM_META_HANDLER_TYPE_ITUNES_METADATA,       "iTunes ",   7 },
+            { QT_REFERENCE_HANDLER_TYPE_ALIAS,              "Alias ",    6 },
+            { QT_REFERENCE_HANDLER_TYPE_RESOURCE,           "Resource ", 9 },
+            { QT_REFERENCE_HANDLER_TYPE_URL,                "URL ",      4 },
+            { subtype,                                      "Unknown ",  8 }
+        };
+    for( int i = 0; subtype_table[i].subtype; i++ )
+        if( subtype == subtype_table[i].subtype )
+        {
+            subtype_name        = subtype_table[i].subtype_name;
+            subtype_name_length = subtype_table[i].subtype_name_length;
+            break;
+        }
+    uint32_t name_length = 15 + subtype_name_length + type_name_length + root->isom_compatible + root->qt_compatible;
+    uint8_t *name = malloc( name_length );
+    if( !name )
+        return -1;
+    if( root->qt_compatible )
+        name[0] = name_length & 0xff;
+    memcpy( name + root->qt_compatible, "L-SMASH ", 8 );
+    memcpy( name + root->qt_compatible + 8, subtype_name, subtype_name_length );
+    memcpy( name + root->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
+    memcpy( name + root->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
+    if( root->isom_compatible )
+        name[name_length - 1] = 0;
+    hdlr->componentName = name;
+    hdlr->componentName_length = name_length;
+    if( mdia )
+        mdia->hdlr = hdlr;
+    else if( meta )
+        meta->hdlr = hdlr;
+    else
+        minf->hdlr = hdlr;
+    return 0;
+}
+
+static int isom_add_minf( isom_mdia_t *mdia )
+{
+    if( !mdia || mdia->minf )
+        return -1;
+    isom_create_box( minf, mdia, ISOM_BOX_TYPE_MINF );
+    mdia->minf = minf;
+    return 0;
+}
+
+static int isom_add_vmhd( isom_minf_t *minf )
+{
+    if( !minf || minf->vmhd )
+        return -1;
+    isom_create_box( vmhd, minf, ISOM_BOX_TYPE_VMHD );
+    vmhd->flags = 0x000001;
+    minf->vmhd = vmhd;
+    return 0;
+}
+
+static int isom_add_smhd( isom_minf_t *minf )
+{
+    if( !minf || minf->smhd )
+        return -1;
+    isom_create_box( smhd, minf, ISOM_BOX_TYPE_SMHD );
+    minf->smhd = smhd;
+    return 0;
+}
+
+static int isom_add_hmhd( isom_minf_t *minf )
+{
+    if( !minf || minf->hmhd )
+        return -1;
+    isom_create_box( hmhd, minf, ISOM_BOX_TYPE_HMHD );
+    minf->hmhd = hmhd;
+    return 0;
+}
+
+static int isom_add_nmhd( isom_minf_t *minf )
+{
+    if( !minf || minf->nmhd )
+        return -1;
+    isom_create_box( nmhd, minf, ISOM_BOX_TYPE_NMHD );
+    minf->nmhd = nmhd;
+    return 0;
+}
+
+static int isom_add_gmin( isom_gmhd_t *gmhd )
+{
+    if( !gmhd || gmhd->gmin )
+        return -1;
+    isom_create_box( gmin, gmhd, QT_BOX_TYPE_GMIN );
+    gmhd->gmin = gmin;
+    return 0;
+}
+
+static int isom_add_text( isom_gmhd_t *gmhd )
+{
+    if( !gmhd || gmhd->text )
+        return -1;
+    isom_create_box( text, gmhd, QT_BOX_TYPE_TEXT );
+    text->matrix[0] = 0x00010000;
+    text->matrix[4] = 0x00010000;
+    text->matrix[8] = 0x40000000;
+    gmhd->text = text;
+    return 0;
+}
+
+static int isom_add_gmhd( isom_minf_t *minf )
+{
+    if( !minf || minf->gmhd )
+        return -1;
+    isom_create_box( gmhd, minf, QT_BOX_TYPE_GMHD );
+    minf->gmhd = gmhd;
+    return 0;
+}
+
+static int isom_add_dinf( isom_minf_t *minf )
+{
+    if( !minf || minf->dinf )
+        return -1;
+    isom_create_box( dinf, minf, ISOM_BOX_TYPE_DINF );
+    minf->dinf = dinf;
+    return 0;
+}
+
+static int isom_add_dref( isom_dinf_t *dinf )
+{
+    if( !dinf || dinf->dref )
+        return -1;
+    isom_create_list_box( dref, dinf, ISOM_BOX_TYPE_DREF );
+    dinf->dref = dref;
+    if( isom_add_dref_entry( dref, 0x000001, NULL, NULL ) )
+        return -1;
+    return 0;
+}
+
+static int isom_add_stsd( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stsd )
+        return -1;
+    isom_create_list_box( stsd, stbl, ISOM_BOX_TYPE_STSD );
+    stbl->stsd = stsd;
+    return 0;
+}
+
+int isom_add_btrt( isom_visual_entry_t *visual )
+{
+    if( !visual || visual->btrt )
+        return -1;
+    isom_create_box( btrt, visual, ISOM_BOX_TYPE_BTRT );
+    visual->btrt = btrt;
+    return 0;
+}
+
+int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return -1;
+    isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
+    return isom_add_btrt( data );
+}
+
+static int isom_add_stts( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stts )
+        return -1;
+    isom_create_list_box( stts, stbl, ISOM_BOX_TYPE_STTS );
+    stbl->stts = stts;
+    return 0;
+}
+
+static int isom_add_ctts( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->ctts )
+        return -1;
+    isom_create_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS );
+    stbl->ctts = ctts;
+    return 0;
+}
+
+static int isom_add_cslg( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->cslg )
+        return -1;
+    isom_create_box( cslg, stbl, ISOM_BOX_TYPE_CSLG );
+    stbl->cslg = cslg;
+    return 0;
+}
+
+static int isom_add_stsc( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stsc )
+        return -1;
+    isom_create_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC );
+    stbl->stsc = stsc;
+    return 0;
+}
+
+static int isom_add_stsz( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stsz )
+        return -1;
+    isom_create_box( stsz, stbl, ISOM_BOX_TYPE_STSZ );  /* We don't create a list here. */
+    stbl->stsz = stsz;
+    return 0;
+}
+
+static int isom_add_stss( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stss )
+        return -1;
+    isom_create_list_box( stss, stbl, ISOM_BOX_TYPE_STSS );
+    stbl->stss = stss;
+    return 0;
+}
+
+static int isom_add_stps( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->stps )
+        return -1;
+    isom_create_list_box( stps, stbl, QT_BOX_TYPE_STPS );
+    stbl->stps = stps;
+    return 0;
+}
+
+static int isom_add_sdtp( isom_stbl_t *stbl )
+{
+    if( !stbl || stbl->sdtp )
+        return -1;
+    isom_create_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP );
+    stbl->sdtp = sdtp;
+    return 0;
+}
+
+static isom_sgpd_entry_t *isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type )
+{
+    if( !stbl )
+        return NULL;
+    if( !stbl->sgpd_list )
+    {
+        stbl->sgpd_list = lsmash_create_entry_list();
+        if( !stbl->sgpd_list )
+            return NULL;
+    }
+    isom_sgpd_entry_t *sgpd = lsmash_malloc_zero( sizeof(isom_sgpd_entry_t) );
+    if( !sgpd )
+        return NULL;
+    isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD );
+    sgpd->list = lsmash_create_entry_list();
+    if( !sgpd->list || lsmash_add_entry( stbl->sgpd_list, sgpd ) )
+    {
+        free( sgpd );
+        return NULL;
+    }
+    sgpd->grouping_type = grouping_type;
+    sgpd->version = 1;      /* We use version 1 because it is recommended in the spec. */
+    switch( grouping_type )
+    {
+        case ISOM_GROUP_TYPE_RAP :
+            sgpd->default_length = 1;
+            break;
+        case ISOM_GROUP_TYPE_ROLL :
+            sgpd->default_length = 2;
+            break;
+        default :
+            /* We don't consider other grouping types currently. */
+            break;
+    }
+    return sgpd;
+}
+
+static isom_sbgp_entry_t *isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type )
+{
+    if( !stbl )
+        return NULL;
+    if( !stbl->sbgp_list )
+    {
+        stbl->sbgp_list = lsmash_create_entry_list();
+        if( !stbl->sbgp_list )
+            return NULL;
+    }
+    isom_sbgp_entry_t *sbgp = lsmash_malloc_zero( sizeof(isom_sbgp_entry_t) );
+    if( !sbgp )
+        return NULL;
+    isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP );
+    sbgp->list = lsmash_create_entry_list();
+    if( !sbgp->list || lsmash_add_entry( stbl->sbgp_list, sbgp ) )
+    {
+        free( sbgp );
+        return NULL;
+    }
+    sbgp->grouping_type = grouping_type;
+    return sbgp;
+}
+
+static int isom_add_stbl( isom_minf_t *minf )
+{
+    if( !minf || minf->stbl )
+        return -1;
+    isom_create_box( stbl, minf, ISOM_BOX_TYPE_STBL );
+    minf->stbl = stbl;
+    return 0;
+}
+
+static int isom_add_chpl( isom_moov_t *moov )
+{
+    if( !moov || !moov->udta || moov->udta->chpl )
+        return -1;
+    isom_create_list_box( chpl, moov->udta, ISOM_BOX_TYPE_CHPL );
+    chpl->version = 1;      /* version = 1 is popular. */
+    moov->udta->chpl = chpl;
+    return 0;
+}
+
+static int isom_add_metaitem( isom_ilst_t *ilst, uint32_t type )
+{
+    if( !ilst || !ilst->item_list )
+        return -1;
+    isom_create_box( metaitem, ilst, type );
+    if( lsmash_add_entry( ilst->item_list, metaitem ) )
+    {
+        free( metaitem );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_mean( isom_metaitem_t *metaitem )
+{
+    if( !metaitem || metaitem->mean )
+        return -1;
+    isom_create_box( mean, metaitem, ISOM_BOX_TYPE_MEAN );
+    metaitem->mean = mean;
+    return 0;
+}
+
+static int isom_add_name( isom_metaitem_t *metaitem )
+{
+    if( !metaitem || metaitem->name )
+        return -1;
+    isom_create_box( name, metaitem, ISOM_BOX_TYPE_NAME );
+    metaitem->name = name;
+    return 0;
+}
+
+static int isom_add_data( isom_metaitem_t *metaitem )
+{
+    if( !metaitem || metaitem->data )
+        return -1;
+    isom_create_box( data, metaitem, ISOM_BOX_TYPE_DATA );
+    metaitem->data = data;
+    return 0;
+}
+
+static int isom_add_ilst( isom_moov_t *moov )
+{
+    if( !moov || !moov->udta || !moov->udta->meta || moov->udta->meta->ilst )
+        return -1;
+    isom_create_box( ilst, moov->udta->meta, ISOM_BOX_TYPE_ILST );
+    ilst->item_list = lsmash_create_entry_list();
+    if( !ilst->item_list )
+    {
+        free( ilst );
+        return -1;
+    }
+    moov->udta->meta->ilst = ilst;
+    return 0;
+}
+
+static int isom_add_meta( isom_box_t *parent )
+{
+    if( !parent )
+        return -1;
+    isom_create_box( meta, parent, ISOM_BOX_TYPE_META );
+    if( !parent->type )
+    {
+        lsmash_root_t *root = (lsmash_root_t *)root;
+        if( root->meta )
+            return -1;
+        root->meta = meta;
+    }
+    else if( parent->type == ISOM_BOX_TYPE_MOOV )
+    {
+        isom_moov_t *moov = (isom_moov_t *)parent;
+        if( moov->meta )
+            return -1;
+        moov->meta = meta;
+    }
+    else if( parent->type == ISOM_BOX_TYPE_TRAK )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)trak;
+        if( trak->meta )
+            return -1;
+        trak->meta = meta;
+    }
+    else
+    {
+        isom_udta_t *udta = (isom_udta_t *)parent;
+        if( udta->meta )
+            return -1;
+        udta->meta = meta;
+    }
+    return 0;
+}
+
+static int isom_add_cprt( isom_udta_t *udta )
+{
+    if( !udta )
+        return -1;
+    if( !udta->cprt_list )
+    {
+        udta->cprt_list = lsmash_create_entry_list();
+        if( !udta->cprt_list )
+            return -1;
+    }
+    isom_create_box( cprt, udta, ISOM_BOX_TYPE_CPRT );
+    if( lsmash_add_entry( udta->cprt_list, cprt ) )
+    {
+        free( cprt );
+        return -1;
+    }
+    return 0;
+}
+
+static int isom_add_udta( lsmash_root_t *root, uint32_t track_ID )
+{
+    /* track_ID == 0 means the direct addition to moov box */
+    if( !track_ID )
+    {
+        if( !root || !root->moov )
+            return -1;
+        if( root->moov->udta )
+            return 0;
+        isom_create_box( udta, root->moov, ISOM_BOX_TYPE_UDTA );
+        root->moov->udta = udta;
+        return 0;
+    }
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak )
+        return -1;
+    if( trak->udta )
+        return 0;
+    isom_create_box( udta, trak, ISOM_BOX_TYPE_UDTA );
+    trak->udta = udta;
+    return 0;
+}
+
+static isom_trak_entry_t *isom_add_trak( lsmash_root_t *root )
+{
+    if( !root || !root->moov )
+        return NULL;
+    isom_moov_t *moov = root->moov;
+    if( !moov->trak_list )
+    {
+        moov->trak_list = lsmash_create_entry_list();
+        if( !moov->trak_list )
+            return NULL;
+    }
+    isom_trak_entry_t *trak = lsmash_malloc_zero( sizeof(isom_trak_entry_t) );
+    if( !trak )
+        return NULL;
+    isom_init_box_common( trak, moov, ISOM_BOX_TYPE_TRAK );
+    isom_cache_t *cache = lsmash_malloc_zero( sizeof(isom_cache_t) );
+    if( !cache )
+    {
+        free( trak );
+        return NULL;
+    }
+    isom_fragment_t *fragment = NULL;
+    if( root->fragment )
+    {
+        fragment = lsmash_malloc_zero( sizeof(isom_fragment_t) );
+        if( !fragment )
+        {
+            free( cache );
+            free( trak );
+            return NULL;
+        }
+        cache->fragment = fragment;
+    }
+    if( lsmash_add_entry( moov->trak_list, trak ) )
+    {
+        if( fragment )
+            free( fragment );
+        free( cache );
+        free( trak );
+        return NULL;
+    }
+    trak->cache = cache;
+    return trak;
+}
+
+static int isom_add_mvex( isom_moov_t *moov )
+{
+    if( !moov || moov->mvex )
+        return -1;
+    isom_create_box( mvex, moov, ISOM_BOX_TYPE_MVEX );
+    moov->mvex = mvex;
+    return 0;
+}
+
+static int isom_add_mehd( isom_mvex_t *mvex )
+{
+    if( !mvex || mvex->mehd )
+        return -1;
+    isom_create_box( mehd, mvex, ISOM_BOX_TYPE_MEHD );
+    mvex->mehd = mehd;
+    return 0;
+}
+
+static int isom_add_tfhd( isom_traf_entry_t *traf )
+{
+    if( !traf || traf->tfhd )
+        return -1;
+    isom_create_box( tfhd, traf, ISOM_BOX_TYPE_TFHD );
+    traf->tfhd = tfhd;
+    return 0;
+}
+
+static int isom_add_mfhd( isom_moof_entry_t *moof )
+{
+    if( !moof || moof->mfhd )
+        return -1;
+    isom_create_box( mfhd, moof, ISOM_BOX_TYPE_MFHD );
+    moof->mfhd = mfhd;
+    return 0;
+}
+
+static int isom_add_mfra( lsmash_root_t *root )
+{
+    if( !root || root->mfra )
+        return -1;
+    isom_create_box( mfra, root, ISOM_BOX_TYPE_MFRA );
+    root->mfra = mfra;
+    return 0;
+}
+
+static int isom_add_mfro( isom_mfra_t *mfra )
+{
+    if( !mfra || mfra->mfro )
+        return -1;
+    isom_create_box( mfro, mfra, ISOM_BOX_TYPE_MFRO );
+    mfra->mfro = mfro;
+    return 0;
+}
+
+#define isom_remove_box( box_name, parent_type ) \
+    do \
+    { \
+        parent_type *parent = (parent_type *)box_name->parent; \
+        free( box_name ); \
+        if( parent ) \
+            parent->box_name = NULL; \
+    } while( 0 )
+
+static void isom_remove_ftyp( isom_ftyp_t *ftyp )
+{
+    if( !ftyp )
+        return;
+    if( ftyp->compatible_brands )
+        free( ftyp->compatible_brands );
+    isom_remove_box( ftyp, lsmash_root_t );
+}
+
+static void isom_remove_tkhd( isom_tkhd_t *tkhd )
+{
+    if( !tkhd )
+        return;
+    isom_remove_box( tkhd, isom_trak_entry_t );
+}
+
+static void isom_remove_clef( isom_clef_t *clef )
+{
+    if( !clef )
+        return;
+    isom_remove_box( clef, isom_tapt_t );
+}
+
+static void isom_remove_prof( isom_prof_t *prof )
+{
+    if( !prof )
+        return;
+    isom_remove_box( prof, isom_tapt_t );
+}
+
+static void isom_remove_enof( isom_enof_t *enof )
+{
+    if( !enof )
+        return;
+    isom_remove_box( enof, isom_tapt_t );
+}
+
+void isom_remove_tapt( isom_tapt_t *tapt )
+{
+    if( !tapt )
+        return;
+    isom_remove_clef( tapt->clef );
+    isom_remove_prof( tapt->prof );
+    isom_remove_enof( tapt->enof );
+    isom_remove_box( tapt, isom_trak_entry_t );
+}
+
+static void isom_remove_elst( isom_elst_t *elst )
+{
+    if( !elst )
+        return;
+    lsmash_remove_list( elst->list, NULL );
+    isom_remove_box( elst, isom_edts_t );
+}
+
+static void isom_remove_edts( isom_edts_t *edts )
+{
+    if( !edts )
+        return;
+    isom_remove_elst( edts->elst );
+    isom_remove_box( edts, isom_trak_entry_t );
+}
+
+static void isom_remove_track_reference_type( isom_tref_type_t *ref )
+{
+    if( !ref )
+        return;
+    if( ref->track_ID )
+        free( ref->track_ID );
+    free( ref );
+}
+
+static void isom_remove_tref( isom_tref_t *tref )
+{
+    if( !tref )
+        return;
+    lsmash_remove_list( tref->ref_list, isom_remove_track_reference_type );
+    isom_remove_box( tref, isom_trak_entry_t );
+}
+
+static void isom_remove_mdhd( isom_mdhd_t *mdhd )
+{
+    if( !mdhd )
+        return;
+    isom_remove_box( mdhd, isom_mdia_t );
+}
+
+static void isom_remove_vmhd( isom_vmhd_t *vmhd )
+{
+    if( !vmhd )
+        return;
+    isom_remove_box( vmhd, isom_minf_t );
+}
+
+static void isom_remove_smhd( isom_smhd_t *smhd )
+{
+    if( !smhd )
+        return;
+    isom_remove_box( smhd, isom_minf_t );
+}
+
+static void isom_remove_hmhd( isom_hmhd_t *hmhd )
+{
+    if( !hmhd )
+        return;
+    isom_remove_box( hmhd, isom_minf_t );
+}
+
+static void isom_remove_nmhd( isom_nmhd_t *nmhd )
+{
+    if( !nmhd )
+        return;
+    isom_remove_box( nmhd, isom_minf_t );
+}
+
+static void isom_remove_gmin( isom_gmin_t *gmin )
+{
+    if( !gmin )
+        return;
+    isom_remove_box( gmin, isom_gmhd_t );
+}
+
+static void isom_remove_text( isom_text_t *text )
+{
+    if( !text )
+        return;
+    isom_remove_box( text, isom_gmhd_t );
+}
+
+static void isom_remove_gmhd( isom_gmhd_t *gmhd )
+{
+    if( !gmhd )
+        return;
+    isom_remove_gmin( gmhd->gmin );
+    isom_remove_text( gmhd->text );
+    isom_remove_box( gmhd, isom_minf_t );
+}
+
+static void isom_remove_hdlr( isom_hdlr_t *hdlr )
+{
+    if( !hdlr )
+        return;
+    if( hdlr->componentName )
+        free( hdlr->componentName );
+    if( hdlr->parent )
+    {
+        if( hdlr->parent->type == ISOM_BOX_TYPE_MDIA )
+            isom_remove_box( hdlr, isom_mdia_t );
+        else if( hdlr->parent->type == ISOM_BOX_TYPE_META )
+            isom_remove_box( hdlr, isom_meta_t );
+        else if( hdlr->parent->type == ISOM_BOX_TYPE_MINF )
+            isom_remove_box( hdlr, isom_minf_t );
+        else
+            assert( 0 );
+        return;
+    }
+    free( hdlr );
+}
+
+void isom_remove_clap( isom_clap_t *clap )
+{
+    if( !clap )
+        return;
+    isom_remove_box( clap, isom_visual_entry_t );
+}
+
+void isom_remove_pasp( isom_pasp_t *pasp )
+{
+    if( !pasp )
+        return;
+    isom_remove_box( pasp, isom_visual_entry_t );
+}
+
+void isom_remove_colr( isom_colr_t *colr )
+{
+    if( !colr )
+        return;
+    isom_remove_box( colr, isom_visual_entry_t );
+}
+
+void isom_remove_stsl( isom_stsl_t *stsl )
+{
+    if( !stsl )
+        return;
+    isom_remove_box( stsl, isom_visual_entry_t );
+}
+
+static void isom_remove_esds( isom_esds_t *esds )
+{
+    if( !esds )
+        return;
+    mp4sys_remove_ES_Descriptor( esds->ES );
+    if( esds->parent )
+    {
+        switch( esds->parent->type )
+        {
+            case ISOM_CODEC_TYPE_MP4V_VIDEO :
+                isom_remove_box( esds, isom_visual_entry_t );
+                break;
+            case ISOM_CODEC_TYPE_MP4A_AUDIO :
+            case ISOM_CODEC_TYPE_M4AE_AUDIO :
+                isom_remove_box( esds, isom_audio_entry_t );
+                break;
+            case QT_BOX_TYPE_WAVE :
+                isom_remove_box( esds, isom_wave_t );
+                break;
+            case ISOM_CODEC_TYPE_MP4S_SYSTEM :
+                isom_remove_box( esds, isom_mp4s_entry_t );
+                break;
+            default :
+                assert( 0 );
+        }
+        return;
+    }
+    free( esds );
+}
+
+void isom_remove_avcC( isom_avcC_t *avcC )
+{
+    if( !avcC )
+        return;
+    lsmash_remove_list( avcC->sequenceParameterSets,   isom_remove_avcC_ps );
+    lsmash_remove_list( avcC->pictureParameterSets,    isom_remove_avcC_ps );
+    lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps );
+    isom_remove_box( avcC, isom_visual_entry_t );
+}
+
+void isom_remove_btrt( isom_btrt_t *btrt )
+{
+    if( !btrt )
+        return;
+    isom_remove_box( btrt, isom_visual_entry_t );
+}
+
+static void isom_remove_visual_extensions( isom_visual_entry_t *visual )
+{
+    if( !visual )
+        return;
+    isom_remove_avcC( visual->avcC );
+    isom_remove_btrt( visual->btrt );
+    isom_remove_esds( visual->esds );
+    isom_remove_colr( visual->colr );
+    isom_remove_stsl( visual->stsl );
+    isom_remove_clap( visual->clap );
+    isom_remove_pasp( visual->pasp );
+}
+
+static void isom_remove_font_record( isom_font_record_t *font_record )
+{
+    if( !font_record )
+        return;
+    if( font_record->font_name )
+        free( font_record->font_name );
+    free( font_record );
+}
+
+void isom_remove_ftab( isom_ftab_t *ftab )
+{
+    if( !ftab )
+        return;
+    lsmash_remove_list( ftab->list, isom_remove_font_record );
+    isom_remove_box( ftab, isom_tx3g_entry_t );
+}
+
+void isom_remove_frma( isom_frma_t *frma )
+{
+    if( !frma )
+        return;
+    isom_remove_box( frma, isom_wave_t );
+}
+
+void isom_remove_enda( isom_enda_t *enda )
+{
+    if( !enda )
+        return;
+    isom_remove_box( enda, isom_wave_t );
+}
+
+void isom_remove_mp4a( isom_mp4a_t *mp4a )
+{
+    if( !mp4a )
+        return;
+    isom_remove_box( mp4a, isom_wave_t );
+}
+
+void isom_remove_terminator( isom_terminator_t *terminator )
+{
+    if( !terminator )
+        return;
+    isom_remove_box( terminator, isom_wave_t );
+}
+
+void isom_remove_wave( isom_wave_t *wave )
+{
+    if( !wave )
+        return;
+    isom_remove_frma( wave->frma );
+    isom_remove_enda( wave->enda );
+    isom_remove_mp4a( wave->mp4a );
+    isom_remove_esds( wave->esds );
+    isom_remove_terminator( wave->terminator );
+    if( wave->exdata )
+        free( wave->exdata );
+    isom_remove_box( wave, isom_audio_entry_t );
+}
+
+void isom_remove_chan( isom_chan_t *chan )
+{
+    if( !chan )
+        return;
+    if( chan->channelDescriptions )
+        free( chan->channelDescriptions );
+    isom_remove_box( chan, isom_audio_entry_t );
+}
+
+void isom_remove_sample_description( isom_sample_entry_t *sample )
+{
+    if( !sample )
+        return;
+    switch( sample->type )
+    {
+        case ISOM_CODEC_TYPE_AVC1_VIDEO :
+        case ISOM_CODEC_TYPE_AVC2_VIDEO :
+        case ISOM_CODEC_TYPE_AVCP_VIDEO :
+        case ISOM_CODEC_TYPE_SVC1_VIDEO :
+        case ISOM_CODEC_TYPE_MVC1_VIDEO :
+        case ISOM_CODEC_TYPE_MVC2_VIDEO :
+        case ISOM_CODEC_TYPE_MP4V_VIDEO :
+        case ISOM_CODEC_TYPE_DRAC_VIDEO :
+        case ISOM_CODEC_TYPE_ENCV_VIDEO :
+        case ISOM_CODEC_TYPE_MJP2_VIDEO :
+        case ISOM_CODEC_TYPE_S263_VIDEO :
+        case ISOM_CODEC_TYPE_VC_1_VIDEO :
+        {
+            isom_visual_entry_t *visual = (isom_visual_entry_t *)sample;
+            isom_remove_visual_extensions( (isom_visual_entry_t *)visual );
+            free( visual );
+            break;
+        }
+        case ISOM_CODEC_TYPE_MP4A_AUDIO :
+        case ISOM_CODEC_TYPE_AC_3_AUDIO :
+        case ISOM_CODEC_TYPE_ALAC_AUDIO :
+        case ISOM_CODEC_TYPE_SAMR_AUDIO :
+        case ISOM_CODEC_TYPE_SAWB_AUDIO :
+        case QT_CODEC_TYPE_23NI_AUDIO :
+        case QT_CODEC_TYPE_NONE_AUDIO :
+        case QT_CODEC_TYPE_LPCM_AUDIO :
+        case QT_CODEC_TYPE_RAW_AUDIO :
+        case QT_CODEC_TYPE_SOWT_AUDIO :
+        case QT_CODEC_TYPE_TWOS_AUDIO :
+        case QT_CODEC_TYPE_FL32_AUDIO :
+        case QT_CODEC_TYPE_FL64_AUDIO :
+        case QT_CODEC_TYPE_IN24_AUDIO :
+        case QT_CODEC_TYPE_IN32_AUDIO :
+        case QT_CODEC_TYPE_NOT_SPECIFIED :
+        case ISOM_CODEC_TYPE_DRA1_AUDIO :
+        case ISOM_CODEC_TYPE_DTSC_AUDIO :
+        case ISOM_CODEC_TYPE_DTSH_AUDIO :
+        case ISOM_CODEC_TYPE_DTSL_AUDIO :
+        case ISOM_CODEC_TYPE_EC_3_AUDIO :
+        case ISOM_CODEC_TYPE_ENCA_AUDIO :
+        case ISOM_CODEC_TYPE_G719_AUDIO :
+        case ISOM_CODEC_TYPE_G726_AUDIO :
+        case ISOM_CODEC_TYPE_M4AE_AUDIO :
+        case ISOM_CODEC_TYPE_MLPA_AUDIO :
+        //case ISOM_CODEC_TYPE_RAW_AUDIO :
+        case ISOM_CODEC_TYPE_SAWP_AUDIO :
+        case ISOM_CODEC_TYPE_SEVC_AUDIO :
+        case ISOM_CODEC_TYPE_SQCP_AUDIO :
+        case ISOM_CODEC_TYPE_SSMV_AUDIO :
+        //case ISOM_CODEC_TYPE_TWOS_AUDIO :
+        {
+            isom_audio_entry_t *audio = (isom_audio_entry_t *)sample;
+            isom_remove_esds( audio->esds );
+            isom_remove_wave( audio->wave );
+            isom_remove_chan( audio->chan );
+            if( audio->exdata )
+                free( audio->exdata );
+            free( audio );
+            break;
+        }
+        case ISOM_CODEC_TYPE_FDP_HINT :
+        case ISOM_CODEC_TYPE_M2TS_HINT :
+        case ISOM_CODEC_TYPE_PM2T_HINT :
+        case ISOM_CODEC_TYPE_PRTP_HINT :
+        case ISOM_CODEC_TYPE_RM2T_HINT :
+        case ISOM_CODEC_TYPE_RRTP_HINT :
+        case ISOM_CODEC_TYPE_RSRP_HINT :
+        case ISOM_CODEC_TYPE_RTP_HINT  :
+        case ISOM_CODEC_TYPE_SM2T_HINT :
+        case ISOM_CODEC_TYPE_SRTP_HINT :
+        {
+            isom_hint_entry_t *hint = (isom_hint_entry_t *)sample;
+            if( hint->data )
+                free( hint->data );
+            free( hint );
+            break;
+        }
+        case ISOM_CODEC_TYPE_IXSE_META :
+        case ISOM_CODEC_TYPE_METT_META :
+        case ISOM_CODEC_TYPE_METX_META :
+        case ISOM_CODEC_TYPE_MLIX_META :
+        case ISOM_CODEC_TYPE_OKSD_META :
+        case ISOM_CODEC_TYPE_SVCM_META :
+        //case ISOM_CODEC_TYPE_TEXT_META :
+        case ISOM_CODEC_TYPE_URIM_META :
+        case ISOM_CODEC_TYPE_XML_META  :
+        {
+            isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)sample;
+            free( metadata );
+            break;
+        }
+        case ISOM_CODEC_TYPE_TX3G_TEXT :
+        {
+            isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)sample;
+            if( tx3g->ftab )
+                isom_remove_ftab( tx3g->ftab );
+            free( tx3g );
+            break;
+        }
+        case QT_CODEC_TYPE_TEXT_TEXT :
+        {
+            isom_text_entry_t *text = (isom_text_entry_t *)sample;
+            if( text->font_name )
+                free( text->font_name );
+            free( text );
+            break;
+        }
+        case ISOM_CODEC_TYPE_MP4S_SYSTEM :
+        {
+            isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)sample;
+            isom_remove_esds( mp4s->esds );
+            free( mp4s );
+            break;
+        }
+        default :
+            break;
+    }
+}
+
+static void isom_remove_stsd( isom_stsd_t *stsd )
+{
+    if( !stsd )
+        return;
+    lsmash_remove_list( stsd->list, isom_remove_sample_description );
+    isom_remove_box( stsd, isom_stbl_t );
+}
+
+static void isom_remove_stts( isom_stts_t *stts )
+{
+    if( !stts )
+        return;
+    lsmash_remove_list( stts->list, NULL );
+    isom_remove_box( stts, isom_stbl_t );
+}
+
+static void isom_remove_ctts( isom_ctts_t *ctts )
+{
+    if( !ctts )
+        return;
+    lsmash_remove_list( ctts->list, NULL );
+    isom_remove_box( ctts, isom_stbl_t );
+}
+
+static void isom_remove_cslg( isom_cslg_t *cslg )
+{
+    if( !cslg )
+        return;
+    isom_remove_box( cslg, isom_stbl_t );
+}
+
+static void isom_remove_stsc( isom_stsc_t *stsc )
+{
+    if( !stsc )
+        return;
+    lsmash_remove_list( stsc->list, NULL );
+    isom_remove_box( stsc, isom_stbl_t );
+}
+
+static void isom_remove_stsz( isom_stsz_t *stsz )
+{
+    if( !stsz )
+        return;
+    lsmash_remove_list( stsz->list, NULL );
+    isom_remove_box( stsz, isom_stbl_t );
+}
+
+static void isom_remove_stss( isom_stss_t *stss )
+{
+    if( !stss )
+        return;
+    lsmash_remove_list( stss->list, NULL );
+    isom_remove_box( stss, isom_stbl_t );
+}
+
+static void isom_remove_stps( isom_stps_t *stps )
+{
+    if( !stps )
+        return;
+    lsmash_remove_list( stps->list, NULL );
+    isom_remove_box( stps, isom_stbl_t );
+}
+
+static void isom_remove_sdtp( isom_sdtp_t *sdtp )
+{
+    if( !sdtp )
+        return;
+    lsmash_remove_list( sdtp->list, NULL );
+    isom_remove_box( sdtp, isom_stbl_t );
+}
+
+static void isom_remove_stco( isom_stco_t *stco )
+{
+    if( !stco )
+        return;
+    lsmash_remove_list( stco->list, NULL );
+    isom_remove_box( stco, isom_stbl_t );
+}
+
+static void isom_remove_sgpd( isom_sgpd_entry_t *sgpd )
+{
+    if( !sgpd )
+        return;
+    lsmash_remove_list( sgpd->list, NULL );
+    free( sgpd );
+}
+
+static void isom_remove_sbgp( isom_sbgp_entry_t *sbgp )
+{
+    if( !sbgp )
+        return;
+    lsmash_remove_list( sbgp->list, NULL );
+    free( sbgp );
+}
+
+static void isom_remove_stbl( isom_stbl_t *stbl )
+{
+    if( !stbl )
+        return;
+    isom_remove_stsd( stbl->stsd );
+    isom_remove_stts( stbl->stts );
+    isom_remove_ctts( stbl->ctts );
+    isom_remove_cslg( stbl->cslg );
+    isom_remove_stsc( stbl->stsc );
+    isom_remove_stsz( stbl->stsz );
+    isom_remove_stss( stbl->stss );
+    isom_remove_stps( stbl->stps );
+    isom_remove_sdtp( stbl->sdtp );
+    isom_remove_stco( stbl->stco );
+    lsmash_remove_list( stbl->sgpd_list, isom_remove_sgpd );
+    lsmash_remove_list( stbl->sbgp_list, isom_remove_sbgp );
+    isom_remove_box( stbl, isom_minf_t );
+}
+
+static void isom_remove_dref( isom_dref_t *dref )
+{
+    if( !dref )
+        return;
+    if( !dref->list )
+    {
+        free( dref );
+        return;
+    }
+    for( lsmash_entry_t *entry = dref->list->head; entry; )
+    {
+        isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
+        if( data )
+        {
+            if( data->name )
+                free( data->name );
+            if( data->location )
+                free( data->location );
+            free( data );
+        }
+        lsmash_entry_t *next = entry->next;
+        free( entry );
+        entry = next;
+    }
+    free( dref->list );
+    isom_remove_box( dref, isom_dinf_t );
+}
+
+static void isom_remove_dinf( isom_dinf_t *dinf )
+{
+    if( !dinf )
+        return;
+    isom_remove_dref( dinf->dref );
+    isom_remove_box( dinf, isom_minf_t );
+}
+
+static void isom_remove_minf( isom_minf_t *minf )
+{
+    if( !minf )
+        return;
+    isom_remove_vmhd( minf->vmhd );
+    isom_remove_smhd( minf->smhd );
+    isom_remove_hmhd( minf->hmhd );
+    isom_remove_nmhd( minf->nmhd );
+    isom_remove_gmhd( minf->gmhd );
+    isom_remove_hdlr( minf->hdlr );
+    isom_remove_dinf( minf->dinf );
+    isom_remove_stbl( minf->stbl );
+    isom_remove_box( minf, isom_mdia_t );
+}
+
+static void isom_remove_mdia( isom_mdia_t *mdia )
+{
+    if( !mdia )
+        return;
+    isom_remove_mdhd( mdia->mdhd );
+    isom_remove_minf( mdia->minf );
+    isom_remove_hdlr( mdia->hdlr );
+    isom_remove_box( mdia, isom_trak_entry_t );
+}
+
+static void isom_remove_chpl( isom_chpl_t *chpl )
+{
+    if( !chpl )
+        return;
+    if( !chpl->list )
+    {
+        free( chpl );
+        return;
+    }
+    for( lsmash_entry_t *entry = chpl->list->head; entry; )
+    {
+        isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
+        if( data )
+        {
+            if( data->chapter_name )
+                free( data->chapter_name );
+            free( data );
+        }
+        lsmash_entry_t *next = entry->next;
+        free( entry );
+        entry = next;
+    }
+    free( chpl->list );
+    isom_remove_box( chpl, isom_udta_t );
+}
+
+static void isom_remove_keys_entry( isom_keys_entry_t *data )
+{
+    if( !data )
+        return;
+    if( data->key_value )
+        free( data->key_value );
+    free( data );
+}
+
+static void isom_remove_keys( isom_keys_t *keys )
+{
+    if( !keys )
+        return;
+    lsmash_remove_list( keys->list, isom_remove_keys_entry );
+    isom_remove_box( keys, isom_meta_t );
+}
+
+static void isom_remove_mean( isom_mean_t *mean )
+{
+    if( !mean )
+        return;
+    if( mean->meaning_string )
+        free( mean->meaning_string );
+    isom_remove_box( mean, isom_metaitem_t );
+}
+
+static void isom_remove_name( isom_name_t *name )
+{
+    if( !name )
+        return;
+    if( name->name )
+        free( name->name );
+    isom_remove_box( name, isom_metaitem_t );
+}
+
+static void isom_remove_data( isom_data_t *data )
+{
+    if( !data )
+        return;
+    if( data->value )
+        free( data->value );
+    isom_remove_box( data, isom_metaitem_t );
+}
+
+static void isom_remove_metaitem( isom_metaitem_t *metaitem )
+{
+    if( !metaitem )
+        return;
+    isom_remove_mean( metaitem->mean );
+    isom_remove_name( metaitem->name );
+    isom_remove_data( metaitem->data );
+    free( metaitem );
+}
+
+static void isom_remove_ilst( isom_ilst_t *ilst )
+{
+    if( !ilst )
+        return;
+    lsmash_remove_list( ilst->item_list, isom_remove_metaitem );
+    isom_remove_box( ilst, isom_meta_t );
+}
+
+static void isom_remove_meta( isom_meta_t *meta )
+{
+    if( !meta )
+        return;
+    isom_remove_hdlr( meta->hdlr );
+    isom_remove_dinf( meta->dinf );
+    isom_remove_keys( meta->keys );
+    isom_remove_ilst( meta->ilst );
+    if( meta->parent )
+    {
+        if( !meta->parent->type )
+            isom_remove_box( meta, lsmash_root_t );
+        else if( meta->parent->type == ISOM_BOX_TYPE_MOOV )
+            isom_remove_box( meta, isom_moov_t );
+        else if( meta->parent->type == ISOM_BOX_TYPE_TRAK )
+            isom_remove_box( meta, isom_trak_entry_t );
+        else if( meta->parent->type == ISOM_BOX_TYPE_UDTA )
+            isom_remove_box( meta, isom_udta_t );
+        else
+            assert( 0 );
+        return;
+    }
+    free( meta );
+}
+
+static void isom_remove_cprt( isom_cprt_t *cprt )
+{
+    if( !cprt )
+        return;
+    if( cprt->notice )
+        free( cprt->notice );
+    free( cprt );
+}
+
+static void isom_remove_udta( isom_udta_t *udta )
+{
+    if( !udta )
+        return;
+    isom_remove_chpl( udta->chpl );
+    isom_remove_meta( udta->meta );
+    free( udta->WLOC );
+    free( udta->LOOP );
+    free( udta->SelO );
+    free( udta->AllF );
+    lsmash_remove_list( udta->cprt_list, isom_remove_cprt );
+    if( udta->parent )
+    {
+        if( udta->parent->type == ISOM_BOX_TYPE_MOOV )
+            isom_remove_box( udta, isom_moov_t );
+        else if( udta->parent->type == ISOM_BOX_TYPE_TRAK )
+            isom_remove_box( udta, isom_trak_entry_t );
+        else
+            assert( 0 );
+        return;
+    }
+    free( udta );
+}
+
+static void isom_remove_sample_pool( isom_sample_pool_t *pool );
+
+static void isom_remove_trak( isom_trak_entry_t *trak )
+{
+    if( !trak )
+        return;
+    isom_remove_tkhd( trak->tkhd );
+    isom_remove_tapt( trak->tapt );
+    isom_remove_edts( trak->edts );
+    isom_remove_tref( trak->tref );
+    isom_remove_mdia( trak->mdia );
+    isom_remove_udta( trak->udta );
+    isom_remove_meta( trak->meta );
+    if( trak->cache )
+    {
+        isom_remove_sample_pool( trak->cache->chunk.pool );
+        lsmash_remove_list( trak->cache->roll.pool, NULL );
+        if( trak->cache->rap )
+            free( trak->cache->rap );
+        free( trak->cache );
+    }
+    free( trak );   /* Note: the list that contains this trak still has the address of the entry. */
+}
+
+static void isom_remove_iods( isom_iods_t *iods )
+{
+    if( !iods )
+        return;
+    mp4sys_remove_ObjectDescriptor( iods->OD );
+    isom_remove_box( iods, isom_moov_t );
+}
+
+static void isom_remove_mehd( isom_mehd_t *mehd )
+{
+    if( !mehd )
+        return;
+    isom_remove_box( mehd, isom_mvex_t );
+}
+
+static void isom_remove_mvex( isom_mvex_t *mvex )
+{
+    if( !mvex )
+        return;
+    isom_remove_mehd( mvex->mehd );
+    lsmash_remove_list( mvex->trex_list, NULL );
+    isom_remove_box( mvex, isom_moov_t );
+}
+
+static void isom_remove_moov( lsmash_root_t *root )
+{
+    if( !root || !root->moov )
+        return;
+    isom_moov_t *moov = root->moov;
+    if( moov->mvhd )
+        free( moov->mvhd );
+    isom_remove_iods( moov->iods );
+    lsmash_remove_list( moov->trak_list, isom_remove_trak );
+    isom_remove_udta( moov->udta );
+    isom_remove_meta( moov->meta );
+    isom_remove_mvex( moov->mvex );
+    free( moov );
+    root->moov = NULL;
+}
+
+static void isom_remove_mfhd( isom_mfhd_t *mfhd )
+{
+    if( !mfhd )
+        return;
+    isom_remove_box( mfhd, isom_moof_entry_t );
+}
+
+static void isom_remove_tfhd( isom_tfhd_t *tfhd )
+{
+    if( !tfhd )
+        return;
+    isom_remove_box( tfhd, isom_traf_entry_t );
+}
+
+static void isom_remove_trun( isom_trun_entry_t *trun )
+{
+    if( !trun )
+        return;
+    lsmash_remove_list( trun->optional, NULL );
+    free( trun );   /* Note: the list that contains this trun still has the address of the entry. */
+}
+
+static void isom_remove_traf( isom_traf_entry_t *traf )
+{
+    if( !traf )
+        return;
+    isom_remove_tfhd( traf->tfhd );
+    lsmash_remove_list( traf->trun_list, isom_remove_trun );
+    free( traf );   /* Note: the list that contains this traf still has the address of the entry. */
+}
+
+static void isom_remove_moof( isom_moof_entry_t *moof )
+{
+    if( !moof )
+        return;
+    isom_remove_mfhd( moof->mfhd );
+    lsmash_remove_list( moof->traf_list, isom_remove_traf );
+    free( moof );
+}
+
+static void isom_remove_mdat( isom_mdat_t *mdat )
+{
+    if( !mdat )
+        return;
+    isom_remove_box( mdat, lsmash_root_t );
+}
+
+static void isom_remove_free( isom_free_t *skip )
+{
+    if( !skip )
+        return;
+    if( skip->data )
+        free( skip->data );
+    lsmash_root_t *root = (lsmash_root_t *)skip->parent;
+    free( skip );
+    root->free = NULL;
+}
+
+static void isom_remove_tfra( isom_tfra_entry_t *tfra )
+{
+    if( !tfra )
+        return;
+    lsmash_remove_list( tfra->list, NULL );
+    free( tfra );
+}
+
+static void isom_remove_mfro( isom_mfro_t *mfro )
+{
+    if( !mfro )
+        return;
+    isom_remove_box( mfro, isom_mfra_t );
+}
+
+static void isom_remove_mfra( isom_mfra_t *mfra )
+{
+    if( !mfra )
+        return;
+    lsmash_remove_list( mfra->tfra_list, isom_remove_tfra );
+    isom_remove_mfro( mfra->mfro );
+    isom_remove_box( mfra, lsmash_root_t );
+}
+
+/* Box writers */
+static int isom_write_tkhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_tkhd_t *tkhd = trak->tkhd;
+    if( !tkhd )
+        return -1;
+    isom_bs_put_box_common( bs, tkhd );
+    if( tkhd->version )
+    {
+        lsmash_bs_put_be64( bs, tkhd->creation_time );
+        lsmash_bs_put_be64( bs, tkhd->modification_time );
+        lsmash_bs_put_be32( bs, tkhd->track_ID );
+        lsmash_bs_put_be32( bs, tkhd->reserved1 );
+        lsmash_bs_put_be64( bs, tkhd->duration );
+    }
+    else
+    {
+        lsmash_bs_put_be32( bs, (uint32_t)tkhd->creation_time );
+        lsmash_bs_put_be32( bs, (uint32_t)tkhd->modification_time );
+        lsmash_bs_put_be32( bs, tkhd->track_ID );
+        lsmash_bs_put_be32( bs, tkhd->reserved1 );
+        lsmash_bs_put_be32( bs, (uint32_t)tkhd->duration );
+    }
+    lsmash_bs_put_be32( bs, tkhd->reserved2[0] );
+    lsmash_bs_put_be32( bs, tkhd->reserved2[1] );
+    lsmash_bs_put_be16( bs, tkhd->layer );
+    lsmash_bs_put_be16( bs, tkhd->alternate_group );
+    lsmash_bs_put_be16( bs, tkhd->volume );
+    lsmash_bs_put_be16( bs, tkhd->reserved3 );
+    for( uint32_t i = 0; i < 9; i++ )
+        lsmash_bs_put_be32( bs, tkhd->matrix[i] );
+    lsmash_bs_put_be32( bs, tkhd->width );
+    lsmash_bs_put_be32( bs, tkhd->height );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_clef( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_clef_t *clef = trak->tapt->clef;
+    if( !clef )
+        return 0;
+    isom_bs_put_box_common( bs, clef );
+    lsmash_bs_put_be32( bs, clef->width );
+    lsmash_bs_put_be32( bs, clef->height );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_prof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_prof_t *prof = trak->tapt->prof;
+    if( !prof )
+        return 0;
+    isom_bs_put_box_common( bs, prof );
+    lsmash_bs_put_be32( bs, prof->width );
+    lsmash_bs_put_be32( bs, prof->height );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_enof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_enof_t *enof = trak->tapt->enof;
+    if( !enof )
+        return 0;
+    isom_bs_put_box_common( bs, enof );
+    lsmash_bs_put_be32( bs, enof->width );
+    lsmash_bs_put_be32( bs, enof->height );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_tapt( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_tapt_t *tapt = trak->tapt;
+    if( !tapt )
+        return 0;
+    isom_bs_put_box_common( bs, tapt );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_clef( bs, trak )
+     || isom_write_prof( bs, trak )
+     || isom_write_enof( bs, trak ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_elst( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_elst_t *elst = trak->edts->elst;
+    if( !elst )
+        return -1;
+    if( !elst->list->entry_count )
+        return 0;
+    if( elst->root->fragment && elst->root->bs->stream != stdout )
+        elst->pos = elst->root->bs->written;    /* Remember to rewrite entries. */
+    isom_bs_put_box_common( bs, elst );
+    lsmash_bs_put_be32( bs, elst->list->entry_count );
+    for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next )
+    {
+        isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        if( elst->version )
+        {
+            lsmash_bs_put_be64( bs, data->segment_duration );
+            lsmash_bs_put_be64( bs, data->media_time );
+        }
+        else
+        {
+            lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
+            lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
+        }
+        lsmash_bs_put_be32( bs, data->media_rate );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_edts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_edts_t *edts = trak->edts;
+    if( !edts )
+        return 0;
+    isom_bs_put_box_common( bs, edts );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return isom_write_elst( bs, trak );
+}
+
+static int isom_write_tref( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_tref_t *tref = trak->tref;
+    if( !tref )
+        return 0;
+    isom_bs_put_box_common( bs, tref );
+    if( tref->ref_list )
+        for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
+        {
+            isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
+            if( !ref )
+                return -1;
+            isom_bs_put_box_common( bs, ref );
+            for( uint32_t i = 0; i < ref->ref_count; i++ )
+                lsmash_bs_put_be32( bs, ref->track_ID[i] );
+        }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mdhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_mdhd_t *mdhd = trak->mdia->mdhd;
+    if( !mdhd )
+        return -1;
+    isom_bs_put_box_common( bs, mdhd );
+    if( mdhd->version )
+    {
+        lsmash_bs_put_be64( bs, mdhd->creation_time );
+        lsmash_bs_put_be64( bs, mdhd->modification_time );
+        lsmash_bs_put_be32( bs, mdhd->timescale );
+        lsmash_bs_put_be64( bs, mdhd->duration );
+    }
+    else
+    {
+        lsmash_bs_put_be32( bs, (uint32_t)mdhd->creation_time );
+        lsmash_bs_put_be32( bs, (uint32_t)mdhd->modification_time );
+        lsmash_bs_put_be32( bs, mdhd->timescale );
+        lsmash_bs_put_be32( bs, (uint32_t)mdhd->duration );
+    }
+    lsmash_bs_put_be16( bs, mdhd->language );
+    lsmash_bs_put_be16( bs, mdhd->quality );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_hdlr( lsmash_bs_t *bs, isom_hdlr_t *hdlr, uint32_t parent_type )
+{
+    if( !hdlr )
+        return parent_type == ISOM_BOX_TYPE_MINF ? 0 : -1;
+    isom_bs_put_box_common( bs, hdlr );
+    lsmash_bs_put_be32( bs, hdlr->componentType );
+    lsmash_bs_put_be32( bs, hdlr->componentSubtype );
+    lsmash_bs_put_be32( bs, hdlr->componentManufacturer );
+    lsmash_bs_put_be32( bs, hdlr->componentFlags );
+    lsmash_bs_put_be32( bs, hdlr->componentFlagsMask );
+    lsmash_bs_put_bytes( bs, hdlr->componentName, hdlr->componentName_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_vmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_vmhd_t *vmhd = trak->mdia->minf->vmhd;
+    if( !vmhd )
+        return -1;
+    isom_bs_put_box_common( bs, vmhd );
+    lsmash_bs_put_be16( bs, vmhd->graphicsmode );
+    for( uint32_t i = 0; i < 3; i++ )
+        lsmash_bs_put_be16( bs, vmhd->opcolor[i] );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_smhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_smhd_t *smhd = trak->mdia->minf->smhd;
+    if( !smhd )
+        return -1;
+    isom_bs_put_box_common( bs, smhd );
+    lsmash_bs_put_be16( bs, smhd->balance );
+    lsmash_bs_put_be16( bs, smhd->reserved );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_hmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_hmhd_t *hmhd = trak->mdia->minf->hmhd;
+    if( !hmhd )
+        return -1;
+    isom_bs_put_box_common( bs, hmhd );
+    lsmash_bs_put_be16( bs, hmhd->maxPDUsize );
+    lsmash_bs_put_be16( bs, hmhd->avgPDUsize );
+    lsmash_bs_put_be32( bs, hmhd->maxbitrate );
+    lsmash_bs_put_be32( bs, hmhd->avgbitrate );
+    lsmash_bs_put_be32( bs, hmhd->reserved );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_nmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_nmhd_t *nmhd = trak->mdia->minf->nmhd;
+    if( !nmhd )
+        return -1;
+    isom_bs_put_box_common( bs, nmhd );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_gmin( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_gmin_t *gmin = trak->mdia->minf->gmhd->gmin;
+    if( !gmin )
+        return -1;
+    isom_bs_put_box_common( bs, gmin );
+    lsmash_bs_put_be16( bs, gmin->graphicsmode );
+    for( uint32_t i = 0; i < 3; i++ )
+        lsmash_bs_put_be16( bs, gmin->opcolor[i] );
+    lsmash_bs_put_be16( bs, gmin->balance );
+    lsmash_bs_put_be16( bs, gmin->reserved );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_text( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_text_t *text = trak->mdia->minf->gmhd->text;
+    if( !text )
+        return -1;
+    isom_bs_put_box_common( bs, text );
+    for( uint32_t i = 0; i < 9; i++ )
+        lsmash_bs_put_be32( bs, text->matrix[i] );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_gmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_gmhd_t *gmhd = trak->mdia->minf->gmhd;
+    if( !gmhd )
+        return -1;
+    isom_bs_put_box_common( bs, gmhd );
+    if( isom_write_gmin( bs, trak ) ||
+        isom_write_text( bs, trak ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_dref( lsmash_bs_t *bs, isom_dref_t *dref )
+{
+    if( !dref || !dref->list )
+        return -1;
+    isom_bs_put_box_common( bs, dref );
+    lsmash_bs_put_be32( bs, dref->list->entry_count );
+    for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
+    {
+        isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        isom_bs_put_box_common( bs, data );
+        if( data->type == ISOM_BOX_TYPE_URN )
+            lsmash_bs_put_bytes( bs, data->name, data->name_length );
+        lsmash_bs_put_bytes( bs, data->location, data->location_length );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_dinf( lsmash_bs_t *bs, isom_dinf_t *dinf, uint32_t parent_type )
+{
+    if( !dinf )
+        return parent_type == ISOM_BOX_TYPE_MINF ? -1 : 0;
+    isom_bs_put_box_common( bs, dinf );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return isom_write_dref( bs, dinf->dref );
+}
+
+static int isom_write_pasp( lsmash_bs_t *bs, isom_pasp_t *pasp )
+{
+    if( !pasp )
+        return 0;
+    isom_bs_put_box_common( bs, pasp );
+    lsmash_bs_put_be32( bs, pasp->hSpacing );
+    lsmash_bs_put_be32( bs, pasp->vSpacing );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_clap( lsmash_bs_t *bs, isom_clap_t *clap )
+{
+    if( !clap )
+        return 0;
+    isom_bs_put_box_common( bs, clap );
+    lsmash_bs_put_be32( bs, clap->cleanApertureWidthN );
+    lsmash_bs_put_be32( bs, clap->cleanApertureWidthD );
+    lsmash_bs_put_be32( bs, clap->cleanApertureHeightN );
+    lsmash_bs_put_be32( bs, clap->cleanApertureHeightD );
+    lsmash_bs_put_be32( bs, clap->horizOffN );
+    lsmash_bs_put_be32( bs, clap->horizOffD );
+    lsmash_bs_put_be32( bs, clap->vertOffN );
+    lsmash_bs_put_be32( bs, clap->vertOffD );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_colr( lsmash_bs_t *bs, isom_colr_t *colr )
+{
+    if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
+        return 0;
+    isom_bs_put_box_common( bs, colr );
+    lsmash_bs_put_be32( bs, colr->color_parameter_type );
+    lsmash_bs_put_be16( bs, colr->primaries_index );
+    lsmash_bs_put_be16( bs, colr->transfer_function_index );
+    lsmash_bs_put_be16( bs, colr->matrix_index );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stsl( lsmash_bs_t *bs, isom_stsl_t *stsl )
+{
+    if( !stsl )
+        return 0;
+    isom_bs_put_box_common( bs, stsl );
+    lsmash_bs_put_byte( bs, stsl->constraint_flag );
+    lsmash_bs_put_byte( bs, stsl->scale_method );
+    lsmash_bs_put_be16( bs, stsl->display_center_x );
+    lsmash_bs_put_be16( bs, stsl->display_center_y );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_esds( lsmash_bs_t *bs, isom_esds_t *esds )
+{
+    if( !esds )
+        return 0;
+    isom_bs_put_box_common( bs, esds );
+    return mp4sys_write_ES_Descriptor( bs, esds->ES );
+}
+
+static int isom_put_ps_entries( lsmash_bs_t *bs, lsmash_entry_list_t *list )
+{
+    for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
+    {
+        isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be16( bs, data->parameterSetLength );
+        lsmash_bs_put_bytes( bs, data->parameterSetNALUnit, data->parameterSetLength );
+    }
+    return 0;
+}
+
+static int isom_write_avcC( lsmash_bs_t *bs, isom_avcC_t *avcC )
+{
+    if( !avcC )
+        return 0;
+    if( !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
+        return -1;
+    isom_bs_put_box_common( bs, avcC );
+    lsmash_bs_put_byte( bs, avcC->configurationVersion );
+    lsmash_bs_put_byte( bs, avcC->AVCProfileIndication );
+    lsmash_bs_put_byte( bs, avcC->profile_compatibility );
+    lsmash_bs_put_byte( bs, avcC->AVCLevelIndication );
+    lsmash_bs_put_byte( bs, avcC->lengthSizeMinusOne | 0xfc );            /* upper 6-bits are reserved as 111111b */
+    lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSets | 0xe0 );    /* upper 3-bits are reserved as 111b */
+    if( isom_put_ps_entries( bs, avcC->sequenceParameterSets ) )
+        return -1;
+    lsmash_bs_put_byte( bs, avcC->numOfPictureParameterSets );
+    if( isom_put_ps_entries( bs, avcC->pictureParameterSets ) )
+        return -1;
+    if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
+    {
+        lsmash_bs_put_byte( bs, avcC->chroma_format | 0xfc );             /* upper 6-bits are reserved as 111111b */
+        lsmash_bs_put_byte( bs, avcC->bit_depth_luma_minus8 | 0xf8 );     /* upper 5-bits are reserved as 11111b */
+        lsmash_bs_put_byte( bs, avcC->bit_depth_chroma_minus8 | 0xf8 );   /* upper 5-bits are reserved as 11111b */
+        lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSetExt );
+        if( isom_put_ps_entries( bs, avcC->sequenceParameterSetExt ) )
+            return -1;
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_btrt( lsmash_bs_t *bs, isom_btrt_t *btrt )
+{
+    if( !btrt )
+        return 0;
+    isom_bs_put_box_common( bs, btrt );
+    lsmash_bs_put_be32( bs, btrt->bufferSizeDB );
+    lsmash_bs_put_be32( bs, btrt->maxBitrate );
+    lsmash_bs_put_be32( bs, btrt->avgBitrate );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_visual_extensions( lsmash_bs_t *bs, isom_visual_entry_t *visual )
+{
+    if( !visual )
+        return 0;
+    if( isom_write_avcC( bs, visual->avcC )
+     || isom_write_btrt( bs, visual->btrt )
+     || isom_write_esds( bs, visual->esds )
+     || isom_write_colr( bs, visual->colr )
+     || isom_write_stsl( bs, visual->stsl )
+     || isom_write_clap( bs, visual->clap )
+     || isom_write_pasp( bs, visual->pasp ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_frma( lsmash_bs_t *bs, isom_frma_t *frma )
+{
+    if( !frma )
+        return -1;
+    isom_bs_put_box_common( bs, frma );
+    lsmash_bs_put_be32( bs, frma->data_format );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_enda( lsmash_bs_t *bs, isom_enda_t *enda )
+{
+    if( !enda )
+        return 0;
+    isom_bs_put_box_common( bs, enda );
+    lsmash_bs_put_be16( bs, enda->littleEndian );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mp4a( lsmash_bs_t *bs, isom_mp4a_t *mp4a )
+{
+    if( !mp4a )
+        return 0;
+    isom_bs_put_box_common( bs, mp4a );
+    lsmash_bs_put_be32( bs, mp4a->unknown );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_terminator( lsmash_bs_t *bs, isom_terminator_t *terminator )
+{
+    if( !terminator )
+        return -1;
+    isom_bs_put_box_common( bs, terminator );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_wave( lsmash_bs_t *bs, isom_wave_t *wave )
+{
+    if( !wave )
+        return 0;
+    isom_bs_put_box_common( bs, wave );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_frma( bs, wave->frma )
+     || isom_write_enda( bs, wave->enda )
+     || isom_write_mp4a( bs, wave->mp4a ) )
+        return -1;
+    lsmash_bs_put_bytes( bs, wave->exdata, wave->exdata_length );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_esds( bs, wave->esds ) )
+        return -1;
+    return isom_write_terminator( bs, wave->terminator );
+}
+
+static int isom_write_chan( lsmash_bs_t *bs, isom_chan_t *chan )
+{
+    if( !chan )
+        return 0;
+    isom_bs_put_box_common( bs, chan );
+    lsmash_bs_put_be32( bs, chan->channelLayoutTag );
+    lsmash_bs_put_be32( bs, chan->channelBitmap );
+    lsmash_bs_put_be32( bs, chan->numberChannelDescriptions );
+    if( chan->channelDescriptions )
+        for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ )
+        {
+            isom_channel_description_t *channelDescriptions = (isom_channel_description_t *)(&chan->channelDescriptions[i]);
+            if( !channelDescriptions )
+                return -1;
+            lsmash_bs_put_be32( bs, channelDescriptions->channelLabel );
+            lsmash_bs_put_be32( bs, channelDescriptions->channelFlags );
+            lsmash_bs_put_be32( bs, channelDescriptions->coordinates[0] );
+            lsmash_bs_put_be32( bs, channelDescriptions->coordinates[1] );
+            lsmash_bs_put_be32( bs, channelDescriptions->coordinates[2] );
+        }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_audio_extensions( lsmash_bs_t *bs, isom_audio_entry_t *audio )
+{
+    if( !audio )
+        return 0;
+    if( isom_write_esds( bs, audio->esds )
+     || isom_write_wave( bs, audio->wave )
+     || isom_write_chan( bs, audio->chan ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_visual_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_visual_entry_t *data = (isom_visual_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    lsmash_bs_put_be16( bs, data->version );
+    lsmash_bs_put_be16( bs, data->revision_level );
+    lsmash_bs_put_be32( bs, data->vendor );
+    lsmash_bs_put_be32( bs, data->temporalQuality );
+    lsmash_bs_put_be32( bs, data->spatialQuality );
+    lsmash_bs_put_be16( bs, data->width );
+    lsmash_bs_put_be16( bs, data->height );
+    lsmash_bs_put_be32( bs, data->horizresolution );
+    lsmash_bs_put_be32( bs, data->vertresolution );
+    lsmash_bs_put_be32( bs, data->dataSize );
+    lsmash_bs_put_be16( bs, data->frame_count );
+    lsmash_bs_put_bytes( bs, data->compressorname, 32 );
+    lsmash_bs_put_be16( bs, data->depth );
+    lsmash_bs_put_be16( bs, data->color_table_ID );
+    lsmash_bs_put_bytes( bs, data->exdata, data->exdata_length );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return isom_write_visual_extensions( bs, data );
+}
+
+static int isom_write_audio_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_audio_entry_t *data = (isom_audio_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    lsmash_bs_put_be16( bs, data->version );
+    lsmash_bs_put_be16( bs, data->revision_level );
+    lsmash_bs_put_be32( bs, data->vendor );
+    lsmash_bs_put_be16( bs, data->channelcount );
+    lsmash_bs_put_be16( bs, data->samplesize );
+    lsmash_bs_put_be16( bs, data->compression_ID );
+    lsmash_bs_put_be16( bs, data->packet_size );
+    lsmash_bs_put_be32( bs, data->samplerate );
+    if( data->version == 1 )
+    {
+        lsmash_bs_put_be32( bs, data->samplesPerPacket );
+        lsmash_bs_put_be32( bs, data->bytesPerPacket );
+        lsmash_bs_put_be32( bs, data->bytesPerFrame );
+        lsmash_bs_put_be32( bs, data->bytesPerSample );
+    }
+    else if( data->version == 2 )
+    {
+        lsmash_bs_put_be32( bs, data->sizeOfStructOnly );
+        lsmash_bs_put_be64( bs, data->audioSampleRate );
+        lsmash_bs_put_be32( bs, data->numAudioChannels );
+        lsmash_bs_put_be32( bs, data->always7F000000 );
+        lsmash_bs_put_be32( bs, data->constBitsPerChannel );
+        lsmash_bs_put_be32( bs, data->formatSpecificFlags );
+        lsmash_bs_put_be32( bs, data->constBytesPerAudioPacket );
+        lsmash_bs_put_be32( bs, data->constLPCMFramesPerAudioPacket );
+    }
+    lsmash_bs_put_bytes( bs, data->exdata, data->exdata_length );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return isom_write_audio_extensions( bs, data );
+}
+
+#if 0
+static int isom_write_hint_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_hint_entry_t *data = (isom_hint_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    if( data->data && data->data_length )
+        lsmash_bs_put_bytes( bs, data->data, data->data_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_metadata_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_metadata_entry_t *data = (isom_metadata_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    return lsmash_bs_write_data( bs );
+}
+#endif
+
+static int isom_write_text_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_text_entry_t *data = (isom_text_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    lsmash_bs_put_be32( bs, data->displayFlags );
+    lsmash_bs_put_be32( bs, data->textJustification );
+    for( uint32_t i = 0; i < 3; i++ )
+        lsmash_bs_put_be16( bs, data->bgColor[i] );
+    lsmash_bs_put_be16( bs, data->top );
+    lsmash_bs_put_be16( bs, data->left );
+    lsmash_bs_put_be16( bs, data->bottom );
+    lsmash_bs_put_be16( bs, data->right );
+    lsmash_bs_put_be32( bs, data->scrpStartChar );
+    lsmash_bs_put_be16( bs, data->scrpHeight );
+    lsmash_bs_put_be16( bs, data->scrpAscent );
+    lsmash_bs_put_be16( bs, data->scrpFont );
+    lsmash_bs_put_be16( bs, data->scrpFace );
+    lsmash_bs_put_be16( bs, data->scrpSize );
+    for( uint32_t i = 0; i < 3; i++ )
+        lsmash_bs_put_be16( bs, data->scrpColor[i] );
+    lsmash_bs_put_byte( bs, data->font_name_length );
+    if( data->font_name && data->font_name_length )
+        lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_put_ftab( lsmash_bs_t *bs, isom_ftab_t *ftab )
+{
+    if( !ftab || !ftab->list )
+        return -1;
+    isom_bs_put_box_common( bs, ftab );
+    lsmash_bs_put_be16( bs, ftab->list->entry_count );
+    for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
+    {
+        isom_font_record_t *data = (isom_font_record_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be16( bs, data->font_ID );
+        lsmash_bs_put_byte( bs, data->font_name_length );
+        if( data->font_name && data->font_name_length )
+            lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
+    }
+    return 0;
+}
+
+static int isom_write_tx3g_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
+{
+    isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)entry->data;
+    if( !data )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_bytes( bs, data->reserved, 6 );
+    lsmash_bs_put_be16( bs, data->data_reference_index );
+    lsmash_bs_put_be32( bs, data->displayFlags );
+    lsmash_bs_put_byte( bs, data->horizontal_justification );
+    lsmash_bs_put_byte( bs, data->vertical_justification );
+    for( uint32_t i = 0; i < 4; i++ )
+        lsmash_bs_put_byte( bs, data->background_color_rgba[i] );
+    lsmash_bs_put_be16( bs, data->top );
+    lsmash_bs_put_be16( bs, data->left );
+    lsmash_bs_put_be16( bs, data->bottom );
+    lsmash_bs_put_be16( bs, data->right );
+    lsmash_bs_put_be16( bs, data->startChar );
+    lsmash_bs_put_be16( bs, data->endChar );
+    lsmash_bs_put_be16( bs, data->font_ID );
+    lsmash_bs_put_byte( bs, data->face_style_flags );
+    lsmash_bs_put_byte( bs, data->font_size );
+    for( uint32_t i = 0; i < 4; i++ )
+        lsmash_bs_put_byte( bs, data->text_color_rgba[i] );
+    isom_put_ftab( bs, data->ftab );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stsd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
+    if( !stsd || !stsd->list || !stsd->list->head )
+        return -1;
+    isom_bs_put_box_common( bs, stsd );
+    lsmash_bs_put_be32( bs, stsd->list->entry_count );
+    int ret = -1;
+    for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
+    {
+        isom_sample_entry_t *sample = (isom_sample_entry_t *)entry->data;
+        if( !sample )
+            return -1;
+        switch( sample->type )
+        {
+            case ISOM_CODEC_TYPE_AVC1_VIDEO :
+#ifdef LSMASH_DEMUXER_ENABLED
+            case ISOM_CODEC_TYPE_MP4V_VIDEO :
+#endif
+#if 0
+            case ISOM_CODEC_TYPE_AVC2_VIDEO :
+            case ISOM_CODEC_TYPE_AVCP_VIDEO :
+            case ISOM_CODEC_TYPE_SVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC2_VIDEO :
+            case ISOM_CODEC_TYPE_DRAC_VIDEO :
+            case ISOM_CODEC_TYPE_ENCV_VIDEO :
+            case ISOM_CODEC_TYPE_MJP2_VIDEO :
+            case ISOM_CODEC_TYPE_S263_VIDEO :
+            case ISOM_CODEC_TYPE_VC_1_VIDEO :
+#endif
+                ret = isom_write_visual_entry( bs, entry );
+                break;
+            case ISOM_CODEC_TYPE_MP4A_AUDIO :
+            case ISOM_CODEC_TYPE_AC_3_AUDIO :
+            case ISOM_CODEC_TYPE_ALAC_AUDIO :
+            case ISOM_CODEC_TYPE_EC_3_AUDIO :
+            case ISOM_CODEC_TYPE_SAMR_AUDIO :
+            case ISOM_CODEC_TYPE_SAWB_AUDIO :
+            case QT_CODEC_TYPE_23NI_AUDIO :
+            case QT_CODEC_TYPE_NONE_AUDIO :
+            case QT_CODEC_TYPE_LPCM_AUDIO :
+            case QT_CODEC_TYPE_RAW_AUDIO :
+            case QT_CODEC_TYPE_SOWT_AUDIO :
+            case QT_CODEC_TYPE_TWOS_AUDIO :
+            case QT_CODEC_TYPE_FL32_AUDIO :
+            case QT_CODEC_TYPE_FL64_AUDIO :
+            case QT_CODEC_TYPE_IN24_AUDIO :
+            case QT_CODEC_TYPE_IN32_AUDIO :
+            case QT_CODEC_TYPE_NOT_SPECIFIED :
+#if 0
+            case ISOM_CODEC_TYPE_DRA1_AUDIO :
+            case ISOM_CODEC_TYPE_DTSC_AUDIO :
+            case ISOM_CODEC_TYPE_DTSH_AUDIO :
+            case ISOM_CODEC_TYPE_DTSL_AUDIO :
+            case ISOM_CODEC_TYPE_ENCA_AUDIO :
+            case ISOM_CODEC_TYPE_G719_AUDIO :
+            case ISOM_CODEC_TYPE_G726_AUDIO :
+            case ISOM_CODEC_TYPE_M4AE_AUDIO :
+            case ISOM_CODEC_TYPE_MLPA_AUDIO :
+            case ISOM_CODEC_TYPE_RAW_AUDIO :
+            case ISOM_CODEC_TYPE_SAWP_AUDIO :
+            case ISOM_CODEC_TYPE_SEVC_AUDIO :
+            case ISOM_CODEC_TYPE_SQCP_AUDIO :
+            case ISOM_CODEC_TYPE_SSMV_AUDIO :
+            case ISOM_CODEC_TYPE_TWOS_AUDIO :
+#endif
+                ret = isom_write_audio_entry( bs, entry );
+                break;
+#if 0
+            case ISOM_CODEC_TYPE_FDP_HINT :
+            case ISOM_CODEC_TYPE_M2TS_HINT :
+            case ISOM_CODEC_TYPE_PM2T_HINT :
+            case ISOM_CODEC_TYPE_PRTP_HINT :
+            case ISOM_CODEC_TYPE_RM2T_HINT :
+            case ISOM_CODEC_TYPE_RRTP_HINT :
+            case ISOM_CODEC_TYPE_RSRP_HINT :
+            case ISOM_CODEC_TYPE_RTP_HINT  :
+            case ISOM_CODEC_TYPE_SM2T_HINT :
+            case ISOM_CODEC_TYPE_SRTP_HINT :
+                ret = isom_write_hint_entry( bs, entry );
+                break;
+            case ISOM_CODEC_TYPE_IXSE_META :
+            case ISOM_CODEC_TYPE_METT_META :
+            case ISOM_CODEC_TYPE_METX_META :
+            case ISOM_CODEC_TYPE_MLIX_META :
+            case ISOM_CODEC_TYPE_OKSD_META :
+            case ISOM_CODEC_TYPE_SVCM_META :
+            case ISOM_CODEC_TYPE_TEXT_META :
+            case ISOM_CODEC_TYPE_URIM_META :
+            case ISOM_CODEC_TYPE_XML_META  :
+                ret = isom_write_metadata_entry( bs, entry );
+                break;
+#endif
+            case ISOM_CODEC_TYPE_TX3G_TEXT :
+                ret = isom_write_tx3g_entry( bs, entry );
+                break;
+            case QT_CODEC_TYPE_TEXT_TEXT :
+                ret = isom_write_text_entry( bs, entry );
+                break;
+            default :
+                break;
+        }
+        if( ret )
+            break;
+    }
+    return ret;
+}
+
+static int isom_write_stts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stts_t *stts = trak->mdia->minf->stbl->stts;
+    if( !stts || !stts->list )
+        return -1;
+    isom_bs_put_box_common( bs, stts );
+    lsmash_bs_put_be32( bs, stts->list->entry_count );
+    for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
+    {
+        isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->sample_count );
+        lsmash_bs_put_be32( bs, data->sample_delta );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_ctts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_ctts_t *ctts = trak->mdia->minf->stbl->ctts;
+    if( !ctts )
+        return 0;
+    if( !ctts->list )
+        return -1;
+    isom_bs_put_box_common( bs, ctts );
+    lsmash_bs_put_be32( bs, ctts->list->entry_count );
+    for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next )
+    {
+        isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->sample_count );
+        lsmash_bs_put_be32( bs, data->sample_offset );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_cslg( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_cslg_t *cslg = trak->mdia->minf->stbl->cslg;
+    if( !cslg )
+        return 0;
+    isom_bs_put_box_common( bs, cslg );
+    lsmash_bs_put_be32( bs, cslg->compositionToDTSShift );
+    lsmash_bs_put_be32( bs, cslg->leastDecodeToDisplayDelta );
+    lsmash_bs_put_be32( bs, cslg->greatestDecodeToDisplayDelta );
+    lsmash_bs_put_be32( bs, cslg->compositionStartTime );
+    lsmash_bs_put_be32( bs, cslg->compositionEndTime );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stsz( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stsz_t *stsz = trak->mdia->minf->stbl->stsz;
+    if( !stsz )
+        return -1;
+    isom_bs_put_box_common( bs, stsz );
+    lsmash_bs_put_be32( bs, stsz->sample_size );
+    lsmash_bs_put_be32( bs, stsz->sample_count );
+    if( stsz->sample_size == 0 && stsz->list )
+        for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next )
+        {
+            isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data;
+            if( !data )
+                return -1;
+            lsmash_bs_put_be32( bs, data->entry_size );
+        }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stss( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stss_t *stss = trak->mdia->minf->stbl->stss;
+    if( !stss )
+        return 0;   /* If the sync sample box is not present, every sample is a random access point. */
+    if( !stss->list )
+        return -1;
+    isom_bs_put_box_common( bs, stss );
+    lsmash_bs_put_be32( bs, stss->list->entry_count );
+    for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next )
+    {
+        isom_stss_entry_t *data = (isom_stss_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->sample_number );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stps( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stps_t *stps = trak->mdia->minf->stbl->stps;
+    if( !stps )
+        return 0;
+    if( !stps->list )
+        return -1;
+    isom_bs_put_box_common( bs, stps );
+    lsmash_bs_put_be32( bs, stps->list->entry_count );
+    for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next )
+    {
+        isom_stps_entry_t *data = (isom_stps_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->sample_number );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_sdtp( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_sdtp_t *sdtp = trak->mdia->minf->stbl->sdtp;
+    if( !sdtp )
+        return 0;
+    if( !sdtp->list )
+        return -1;
+    isom_bs_put_box_common( bs, sdtp );
+    for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next )
+    {
+        isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        uint8_t temp = (data->is_leading            << 6)
+                     | (data->sample_depends_on     << 4)
+                     | (data->sample_is_depended_on << 2)
+                     |  data->sample_has_redundancy;
+        lsmash_bs_put_byte( bs, temp );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stsc( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
+    if( !stsc || !stsc->list )
+        return -1;
+    isom_bs_put_box_common( bs, stsc );
+    lsmash_bs_put_be32( bs, stsc->list->entry_count );
+    for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next )
+    {
+        isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->first_chunk );
+        lsmash_bs_put_be32( bs, data->samples_per_chunk );
+        lsmash_bs_put_be32( bs, data->sample_description_index );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_co64( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stco_t *co64 = trak->mdia->minf->stbl->stco;
+    if( !co64 || !co64->list )
+        return -1;
+    isom_bs_put_box_common( bs, co64 );
+    lsmash_bs_put_be32( bs, co64->list->entry_count );
+    for( lsmash_entry_t *entry = co64->list->head; entry; entry = entry->next )
+    {
+        isom_co64_entry_t *data = (isom_co64_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be64( bs, data->chunk_offset );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stco( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stco_t *stco = trak->mdia->minf->stbl->stco;
+    if( !stco || !stco->list )
+        return -1;
+    if( stco->large_presentation )
+        return isom_write_co64( bs, trak );
+    isom_bs_put_box_common( bs, stco );
+    lsmash_bs_put_be32( bs, stco->list->entry_count );
+    for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
+    {
+        isom_stco_entry_t *data = (isom_stco_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->chunk_offset );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_sgpd( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
+{
+    isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sgpd_list, grouping_number );
+    if( !sgpd || !sgpd->list )
+        return -1;
+    isom_bs_put_box_common( bs, sgpd );
+    lsmash_bs_put_be32( bs, sgpd->grouping_type );
+    if( sgpd->version == 1 )
+        lsmash_bs_put_be32( bs, sgpd->default_length );
+    lsmash_bs_put_be32( bs, sgpd->list->entry_count );
+    for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
+    {
+        if( !entry->data )
+            return -1;
+        switch( sgpd->grouping_type )
+        {
+            case ISOM_GROUP_TYPE_RAP :
+            {
+                isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data;
+                uint8_t temp = (rap->num_leading_samples_known << 7) 
+                             |  rap->num_leading_samples;
+                lsmash_bs_put_byte( bs, temp );
+                break;
+            }
+            case ISOM_GROUP_TYPE_ROLL :
+                lsmash_bs_put_be16( bs, ((isom_roll_entry_t *)entry->data)->roll_distance );
+                break;
+            default :
+                /* We don't consider other grouping types currently. */
+                // if( sgpd->version == 1 && !sgpd->default_length )
+                //     lsmash_bs_put_be32( bs, ((isom_sgpd_entry_t *)entry->data)->description_length );
+                break;
+        }
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_sbgp( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
+{
+    isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sbgp_list, grouping_number );
+    if( !sbgp || !sbgp->list )
+        return -1;
+    isom_bs_put_box_common( bs, sbgp );
+    lsmash_bs_put_be32( bs, sbgp->grouping_type );
+    if( sbgp->version == 1 )
+        lsmash_bs_put_be32( bs, sbgp->grouping_type_parameter );
+    lsmash_bs_put_be32( bs, sbgp->list->entry_count );
+    for( lsmash_entry_t *entry = sbgp->list->head; entry; entry = entry->next )
+    {
+        isom_group_assignment_entry_t *data = (isom_group_assignment_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be32( bs, data->sample_count );
+        lsmash_bs_put_be32( bs, data->group_description_index );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_stbl( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    if( !stbl )
+        return -1;
+    isom_bs_put_box_common( bs, stbl );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_stsd( bs, trak )
+     || isom_write_stts( bs, trak )
+     || isom_write_ctts( bs, trak )
+     || isom_write_cslg( bs, trak )
+     || isom_write_stss( bs, trak )
+     || isom_write_stps( bs, trak )
+     || isom_write_sdtp( bs, trak )
+     || isom_write_stsc( bs, trak )
+     || isom_write_stsz( bs, trak )
+     || isom_write_stco( bs, trak ) )
+        return -1;
+    if( stbl->sgpd_list )
+        for( uint32_t i = 1; i <= stbl->sgpd_list->entry_count; i++ )
+            if( isom_write_sgpd( bs, trak, i ) )
+                return -1;
+    if( stbl->sbgp_list )
+        for( uint32_t i = 1; i <= stbl->sbgp_list->entry_count; i++ )
+            if( isom_write_sbgp( bs, trak, i ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_minf( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_minf_t *minf = trak->mdia->minf;
+    if( !minf )
+        return -1;
+    isom_bs_put_box_common( bs, minf );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( (minf->vmhd && isom_write_vmhd( bs, trak ))
+     || (minf->smhd && isom_write_smhd( bs, trak ))
+     || (minf->hmhd && isom_write_hmhd( bs, trak ))
+     || (minf->nmhd && isom_write_nmhd( bs, trak ))
+     || (minf->gmhd && isom_write_gmhd( bs, trak )) )
+        return -1;
+    if( isom_write_hdlr( bs, minf->hdlr, minf->type )
+     || isom_write_dinf( bs, minf->dinf, minf->type )
+     || isom_write_stbl( bs, trak ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_mdia( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    isom_mdia_t *mdia = trak->mdia;
+    if( !mdia )
+        return -1;
+    isom_bs_put_box_common( bs, mdia );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_mdhd( bs, trak )
+     || isom_write_hdlr( bs, mdia->hdlr, mdia->type )
+     || isom_write_minf( bs, trak ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_chpl( lsmash_bs_t *bs, isom_chpl_t *chpl )
+{
+    if( !chpl )
+        return 0;
+    if( !chpl->list || chpl->version > 1 )
+        return -1;
+    isom_bs_put_box_common( bs, chpl );
+    if( chpl->version == 1 )
+    {
+        lsmash_bs_put_byte( bs, chpl->unknown );
+        lsmash_bs_put_be32( bs, chpl->list->entry_count );
+    }
+    else    /* chpl->version == 0 */
+        lsmash_bs_put_byte( bs, (uint8_t)chpl->list->entry_count );
+    for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
+    {
+        isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        lsmash_bs_put_be64( bs, data->start_time );
+        lsmash_bs_put_byte( bs, data->chapter_name_length );
+        lsmash_bs_put_bytes( bs, data->chapter_name, data->chapter_name_length );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mean( lsmash_bs_t *bs, isom_mean_t *mean )
+{
+    if( !mean )
+        return 0;
+    isom_bs_put_box_common( bs, mean );
+    if( mean->meaning_string && mean->meaning_string_length )
+        lsmash_bs_put_bytes( bs, mean->meaning_string, mean->meaning_string_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_name( lsmash_bs_t *bs, isom_name_t *name )
+{
+    if( !name )
+        return 0;
+    isom_bs_put_box_common( bs, name );
+    if( name->name && name->name_length )
+        lsmash_bs_put_bytes( bs, name->name, name->name_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_data( lsmash_bs_t *bs, isom_data_t *data )
+{
+    if( !data || data->size < 16 )
+        return -1;
+    isom_bs_put_box_common( bs, data );
+    lsmash_bs_put_be16( bs, data->reserved );
+    lsmash_bs_put_byte( bs, data->type_set_identifier );
+    lsmash_bs_put_byte( bs, data->type_code );
+    lsmash_bs_put_be32( bs, data->the_locale );
+    if( data->value && data->value_length )
+        lsmash_bs_put_bytes( bs, data->value, data->value_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_metaitem( lsmash_bs_t *bs, isom_metaitem_t *metaitem )
+{
+    if( !metaitem )
+        return -1;
+    isom_bs_put_box_common( bs, metaitem );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_mean( bs, metaitem->mean )
+     || isom_write_name( bs, metaitem->name )
+     || isom_write_data( bs, metaitem->data ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_ilst( lsmash_bs_t *bs, isom_ilst_t *ilst )
+{
+    if( !ilst )
+        return 0;
+    isom_bs_put_box_common( bs, ilst );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( ilst->item_list )
+        for( lsmash_entry_t *entry = ilst->item_list->head; entry; entry = entry->next )
+            if( isom_write_metaitem( bs, (isom_metaitem_t *)entry->data ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_meta( lsmash_bs_t *bs, isom_meta_t *meta )
+{
+    if( !meta )
+        return 0;
+    isom_bs_put_box_common( bs, meta );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_hdlr( bs, meta->hdlr, meta->type )
+     || isom_write_dinf( bs, meta->dinf, meta->type )
+     || isom_write_ilst( bs, meta->ilst ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_cprt( lsmash_bs_t *bs, isom_cprt_t *cprt )
+{
+    if( !cprt )
+        return -1;
+    isom_bs_put_box_common( bs, cprt );
+    lsmash_bs_put_be16( bs, cprt->language );
+    lsmash_bs_put_bytes( bs, cprt->notice, cprt->notice_length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_udta( lsmash_bs_t *bs, isom_moov_t *moov, isom_trak_entry_t *trak )
+{
+    /* Setting non-NULL pointer to trak means trak->udta data will be written in stream.
+     * If trak is set by NULL while moov is set by non-NULL pointer, moov->udta data will be written in stream. */
+    isom_udta_t *udta = trak ? trak->udta : moov ? moov->udta : NULL;
+    if( !udta )
+        return 0;
+    isom_bs_put_box_common( bs, udta );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( moov && isom_write_chpl( bs, udta->chpl ) )
+        return -1;
+    if( isom_write_meta( bs, udta->meta ) )
+        return -1;
+    if( udta->cprt_list )
+        for( lsmash_entry_t *entry = udta->cprt_list->head; entry; entry = entry->next )
+            if( isom_write_cprt( bs, (isom_cprt_t *)entry->data ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_trak( lsmash_bs_t *bs, isom_trak_entry_t *trak )
+{
+    if( !trak )
+        return -1;
+    isom_bs_put_box_common( bs, trak );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_tkhd( bs, trak )
+     || isom_write_tapt( bs, trak )
+     || isom_write_edts( bs, trak )
+     || isom_write_tref( bs, trak )
+     || isom_write_mdia( bs, trak )
+     || isom_write_udta( bs, NULL, trak )
+     || isom_write_meta( bs, trak->meta ) )
+        return -1;
+    return 0;
+}
+
+static int isom_write_iods( lsmash_root_t *root )
+{
+    if( !root || !root->moov )
+        return -1;
+    if( !root->moov->iods )
+        return 0;
+    isom_iods_t *iods = root->moov->iods;
+    lsmash_bs_t *bs = root->bs;
+    isom_bs_put_box_common( bs, iods );
+    return mp4sys_write_ObjectDescriptor( bs, iods->OD );
+}
+
+static int isom_write_mvhd( lsmash_root_t *root )
+{
+    if( !root || !root->moov || !root->moov->mvhd )
+        return -1;
+    isom_mvhd_t *mvhd = root->moov->mvhd;
+    lsmash_bs_t *bs = root->bs;
+    isom_bs_put_box_common( bs, mvhd );
+    if( mvhd->version )
+    {
+        lsmash_bs_put_be64( bs, mvhd->creation_time );
+        lsmash_bs_put_be64( bs, mvhd->modification_time );
+        lsmash_bs_put_be32( bs, mvhd->timescale );
+        lsmash_bs_put_be64( bs, mvhd->duration );
+    }
+    else
+    {
+        lsmash_bs_put_be32( bs, (uint32_t)mvhd->creation_time );
+        lsmash_bs_put_be32( bs, (uint32_t)mvhd->modification_time );
+        lsmash_bs_put_be32( bs, mvhd->timescale );
+        lsmash_bs_put_be32( bs, (uint32_t)mvhd->duration );
+    }
+    lsmash_bs_put_be32( bs, mvhd->rate );
+    lsmash_bs_put_be16( bs, mvhd->volume );
+    lsmash_bs_put_be16( bs, mvhd->reserved );
+    lsmash_bs_put_be32( bs, mvhd->preferredLong[0] );
+    lsmash_bs_put_be32( bs, mvhd->preferredLong[1] );
+    for( int i = 0; i < 9; i++ )
+        lsmash_bs_put_be32( bs, mvhd->matrix[i] );
+    lsmash_bs_put_be32( bs, mvhd->previewTime );
+    lsmash_bs_put_be32( bs, mvhd->previewDuration );
+    lsmash_bs_put_be32( bs, mvhd->posterTime );
+    lsmash_bs_put_be32( bs, mvhd->selectionTime );
+    lsmash_bs_put_be32( bs, mvhd->selectionDuration );
+    lsmash_bs_put_be32( bs, mvhd->currentTime );
+    lsmash_bs_put_be32( bs, mvhd->next_track_ID );
+    return lsmash_bs_write_data( bs );
+}
+
+static void isom_bs_put_sample_flags( lsmash_bs_t *bs, isom_sample_flags_t *flags )
+{
+    uint32_t temp = (flags->reserved                  << 28)
+                  | (flags->is_leading                << 26)
+                  | (flags->sample_depends_on         << 24)
+                  | (flags->sample_is_depended_on     << 22)
+                  | (flags->sample_has_redundancy     << 20)
+                  | (flags->sample_padding_value      << 17)
+                  | (flags->sample_is_non_sync_sample << 16)
+                  |  flags->sample_degradation_priority;
+    lsmash_bs_put_be32( bs, temp );
+}
+
+static int isom_write_mehd( lsmash_bs_t *bs, isom_mehd_t *mehd )
+{
+    if( !mehd )
+        return -1;
+    isom_bs_put_box_common( bs, mehd );
+    if( mehd->version == 1 )
+        lsmash_bs_put_be64( bs, mehd->fragment_duration );
+    else
+        lsmash_bs_put_be32( bs, (uint32_t)mehd->fragment_duration );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_trex( lsmash_bs_t *bs, isom_trex_entry_t *trex )
+{
+    if( !trex )
+        return -1;
+    isom_bs_put_box_common( bs, trex );
+    lsmash_bs_put_be32( bs, trex->track_ID );
+    lsmash_bs_put_be32( bs, trex->default_sample_description_index );
+    lsmash_bs_put_be32( bs, trex->default_sample_duration );
+    lsmash_bs_put_be32( bs, trex->default_sample_size );
+    isom_bs_put_sample_flags( bs, &trex->default_sample_flags );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_bs_write_movie_extends_placeholder( lsmash_bs_t *bs )
+{
+    /* The following will be overwritten by Movie Extends Header Box.
+     * We use version 1 Movie Extends Header Box since it causes extra 4 bytes region
+     * we cannot replace with empty Free Space Box as we place version 0 one.  */
+    lsmash_bs_put_be32( bs, ISOM_FULLBOX_COMMON_SIZE + 8 );
+    lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
+    lsmash_bs_put_be32( bs, 0 );
+    lsmash_bs_put_be64( bs, 0 );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mvex( lsmash_bs_t *bs, isom_mvex_t *mvex )
+{
+    if( !mvex )
+        return 0;
+    isom_bs_put_box_common( bs, mvex );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    /* Movie Extends Header Box is not written immediately.
+     * It's done after finishing all movie fragments. */
+    if( mvex->mehd )
+    {
+        if( isom_write_mehd( bs, mvex->mehd ) )
+            return -1;
+    }
+    else if( bs->stream != stdout )
+    {
+        /*
+            [ROOT]
+             |--[ftyp]
+             |--[moov]
+                 |--[mvhd]
+                 |--[trak]
+                 *
+                 *
+                 *
+                 |--[mvex]
+                     |--[mehd] <--- mehd->pos == mvex->placeholder_pos
+        */
+        mvex->placeholder_pos = mvex->root->bs->written;
+        if( isom_bs_write_movie_extends_placeholder( bs ) )
+            return -1;
+    }
+    if( mvex->trex_list )
+        for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
+            if( isom_write_trex( bs, (isom_trex_entry_t *)entry->data ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_mfhd( lsmash_bs_t *bs, isom_mfhd_t *mfhd )
+{
+    if( !mfhd )
+        return -1;
+    isom_bs_put_box_common( bs, mfhd );
+    lsmash_bs_put_be32( bs, mfhd->sequence_number );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_tfhd( lsmash_bs_t *bs, isom_tfhd_t *tfhd )
+{
+    if( !tfhd )
+        return -1;
+    isom_bs_put_box_common( bs, tfhd );
+    lsmash_bs_put_be32( bs, tfhd->track_ID );
+    if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT         ) lsmash_bs_put_be64( bs, tfhd->base_data_offset );
+    if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_bs_put_be32( bs, tfhd->sample_description_index );
+    if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT  ) lsmash_bs_put_be32( bs, tfhd->default_sample_duration );
+    if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT      ) lsmash_bs_put_be32( bs, tfhd->default_sample_size );
+    if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT     ) isom_bs_put_sample_flags( bs, &tfhd->default_sample_flags );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_trun( lsmash_bs_t *bs, isom_trun_entry_t *trun )
+{
+    if( !trun )
+        return -1;
+    isom_bs_put_box_common( bs, trun );
+    lsmash_bs_put_be32( bs, trun->sample_count );
+    if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT        ) lsmash_bs_put_be32( bs, trun->data_offset );
+    if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &trun->first_sample_flags );
+    if( trun->optional )
+        for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
+        {
+            isom_trun_optional_row_t *data = (isom_trun_optional_row_t *)entry->data;
+            if( !data )
+                return -1;
+            if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT                ) lsmash_bs_put_be32( bs, data->sample_duration );
+            if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT                    ) lsmash_bs_put_be32( bs, data->sample_size );
+            if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT                   ) isom_bs_put_sample_flags( bs, &data->sample_flags );
+            if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, data->sample_composition_time_offset );
+        }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_traf( lsmash_bs_t *bs, isom_traf_entry_t *traf )
+{
+    if( !traf )
+        return -1;
+    isom_bs_put_box_common( bs, traf );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_tfhd( bs, traf->tfhd ) )
+        return -1;
+    if( traf->trun_list )
+        for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
+            if( isom_write_trun( bs, (isom_trun_entry_t *)entry->data ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_moof( lsmash_bs_t *bs, isom_moof_entry_t *moof )
+{
+    if( !moof )
+        return -1;
+    isom_bs_put_box_common( bs, moof );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_mfhd( bs, moof->mfhd ) )
+        return -1;
+    if( moof->traf_list )
+        for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+            if( isom_write_traf( bs, (isom_traf_entry_t *)entry->data ) )
+                return -1;
+    return 0;
+}
+
+static int isom_write_tfra( lsmash_bs_t *bs, isom_tfra_entry_t *tfra )
+{
+    if( !tfra )
+        return -1;
+    isom_bs_put_box_common( bs, tfra );
+    uint32_t temp = (tfra->reserved                << 6)
+                  | (tfra->length_size_of_traf_num << 4)
+                  | (tfra->length_size_of_trun_num << 2)
+                  |  tfra->length_size_of_sample_num;
+    lsmash_bs_put_be32( bs, tfra->track_ID );
+    lsmash_bs_put_be32( bs, temp );
+    lsmash_bs_put_be32( bs, tfra->number_of_entry );
+    if( tfra->list )
+    {
+        void (*bs_put_funcs[5])( lsmash_bs_t *, uint64_t ) =
+            {
+                lsmash_bs_put_byte_from_64,
+                lsmash_bs_put_be16_from_64,
+                lsmash_bs_put_be24_from_64,
+                lsmash_bs_put_be32_from_64,
+                lsmash_bs_put_be64
+            };
+        void (*bs_put_time)         ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1)        ];
+        void (*bs_put_moof_offset)  ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1)        ];
+        void (*bs_put_traf_number)  ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_traf_num   ];
+        void (*bs_put_trun_number)  ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_trun_num   ];
+        void (*bs_put_sample_number)( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_sample_num ];
+        for( lsmash_entry_t *entry = tfra->list->head; entry; entry = entry->next )
+        {
+            isom_tfra_location_time_entry_t *data = (isom_tfra_location_time_entry_t *)entry->data;
+            if( !data )
+                return -1;
+            bs_put_time         ( bs, data->time          );
+            bs_put_moof_offset  ( bs, data->moof_offset   );
+            bs_put_traf_number  ( bs, data->traf_number   );
+            bs_put_trun_number  ( bs, data->trun_number   );
+            bs_put_sample_number( bs, data->sample_number );
+        }
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mfro( lsmash_bs_t *bs, isom_mfro_t *mfro )
+{
+    if( !mfro )
+        return -1;
+    isom_bs_put_box_common( bs, mfro );
+    lsmash_bs_put_be32( bs, mfro->length );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mfra( lsmash_bs_t *bs, isom_mfra_t *mfra )
+{
+    if( !mfra )
+        return -1;
+    isom_bs_put_box_common( bs, mfra );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( mfra->tfra_list )
+        for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
+            if( isom_write_tfra( bs, (isom_tfra_entry_t *)entry->data ) )
+                return -1;
+    return isom_write_mfro( bs, mfra->mfro );
+}
+
+static int isom_bs_write_largesize_placeholder( lsmash_bs_t *bs )
+{
+    lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE );
+    lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mdat_header( lsmash_root_t *root, uint64_t media_size )
+{
+    if( !root || !root->bs || !root->mdat )
+        return -1;
+    isom_mdat_t *mdat = root->mdat;
+    lsmash_bs_t *bs = root->bs;
+    if( media_size )
+    {
+        mdat->size = ISOM_BASEBOX_COMMON_SIZE + media_size;
+        if( mdat->size > UINT32_MAX )
+            mdat->size += 8;    /* large_size */
+        isom_bs_put_box_common( bs, mdat );
+        return 0;
+    }
+    mdat->placeholder_pos = lsmash_ftell( bs->stream );
+    if( isom_bs_write_largesize_placeholder( bs ) )
+        return -1;
+    mdat->size = ISOM_BASEBOX_COMMON_SIZE;
+    isom_bs_put_box_common( bs, mdat );
+    return lsmash_bs_write_data( bs );
+}
+
+static int isom_write_mdat_size( lsmash_root_t *root )
+{
+    if( !root || !root->bs || !root->bs->stream )
+        return -1;
+    if( !root->mdat )
+        return 0;
+    isom_mdat_t *mdat = root->mdat;
+    uint8_t large_flag = mdat->size > UINT32_MAX;
+    lsmash_bs_t *bs = root->bs;
+    FILE *stream = bs->stream;
+    uint64_t current_pos = lsmash_ftell( stream );
+    if( large_flag )
+    {
+        lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET );
+        lsmash_bs_put_be32( bs, 1 );
+        lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
+        lsmash_bs_put_be64( bs, mdat->size + ISOM_BASEBOX_COMMON_SIZE );
+    }
+    else
+    {
+        lsmash_fseek( stream, mdat->placeholder_pos + ISOM_BASEBOX_COMMON_SIZE, SEEK_SET );
+        lsmash_bs_put_be32( bs, mdat->size );
+        lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
+    }
+    int ret = lsmash_bs_write_data( bs );
+    lsmash_fseek( stream, current_pos, SEEK_SET );
+    return ret;
+}
+
+/* We put a placeholder for 64-bit media data if the media_size of the argument is set to 0.
+ * If a Media Data Box already exists and we don't pick movie fragments structure,
+ * write the actual size of the current one and start a new one. */
+static int isom_new_mdat( lsmash_root_t *root, uint64_t media_size )
+{
+    if( !root )
+        return 0;
+    if( root->mdat )
+    {
+        /* Write the actual size of the current Media Data Box. */
+        if( !root->fragment && isom_write_mdat_size( root ) )
+            return -1;
+    }
+    else
+    {
+        isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT );
+        root->mdat = mdat;
+    }
+    /* Start a new Media Data Box. */
+    return isom_write_mdat_header( root, media_size );
+}
+
+int isom_check_compatibility( lsmash_root_t *root )
+{
+    if( !root )
+        return -1;
+    root->qt_compatible = 0;
+    /* Check brand to decide mandatory boxes. */
+    if( !root->ftyp || !root->ftyp->brand_count )
+    {
+        /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
+        if( root->moov && root->moov->iods )
+        {
+            root->mp4_version1 = 1;
+            root->isom_compatible = 1;
+        }
+        else
+            root->qt_compatible = 1;
+        return 0;
+    }
+    for( uint32_t i = 0; i < root->ftyp->brand_count; i++ )
+    {
+        switch( root->ftyp->compatible_brands[i] )
+        {
+            case ISOM_BRAND_TYPE_QT :
+                root->qt_compatible = 1;
+                break;
+            case ISOM_BRAND_TYPE_MP41 :
+                root->mp4_version1 = 1;
+                break;
+            case ISOM_BRAND_TYPE_MP42 :
+                root->mp4_version2 = 1;
+                break;
+            case ISOM_BRAND_TYPE_AVC1 :
+            case ISOM_BRAND_TYPE_ISOM :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 1 );
+                break;
+            case ISOM_BRAND_TYPE_ISO2 :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 2 );
+                break;
+            case ISOM_BRAND_TYPE_ISO3 :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 3 );
+                break;
+            case ISOM_BRAND_TYPE_ISO4 :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 4 );
+                break;
+            case ISOM_BRAND_TYPE_ISO5 :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 5 );
+                break;
+            case ISOM_BRAND_TYPE_ISO6 :
+                root->max_isom_version = LSMASH_MAX( root->max_isom_version, 6 );
+                break;
+            case ISOM_BRAND_TYPE_M4A :
+            case ISOM_BRAND_TYPE_M4B :
+            case ISOM_BRAND_TYPE_M4P :
+            case ISOM_BRAND_TYPE_M4V :
+                root->itunes_movie = 1;
+                break;
+            case ISOM_BRAND_TYPE_3GP4 :
+                root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 4 );
+                break;
+            case ISOM_BRAND_TYPE_3GP5 :
+                root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 5 );
+                break;
+            case ISOM_BRAND_TYPE_3GE6 :
+            case ISOM_BRAND_TYPE_3GG6 :
+            case ISOM_BRAND_TYPE_3GP6 :
+            case ISOM_BRAND_TYPE_3GR6 :
+            case ISOM_BRAND_TYPE_3GS6 :
+                root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 6 );
+                break;
+            default :
+                break;
+        }
+        switch( root->ftyp->compatible_brands[i] )
+        {
+            case ISOM_BRAND_TYPE_AVC1 :
+            case ISOM_BRAND_TYPE_ISO2 :
+            case ISOM_BRAND_TYPE_ISO3 :
+            case ISOM_BRAND_TYPE_ISO4 :
+            case ISOM_BRAND_TYPE_ISO5 :
+            case ISOM_BRAND_TYPE_ISO6 :
+                root->avc_extensions = 1;
+                break;
+            default :
+                break;
+        }
+    }
+    root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_movie || root->max_3gpp_version;
+    return 0;
+}
+
+static uint32_t isom_get_sample_count( isom_trak_entry_t *trak )
+{
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsz )
+        return 0;
+    return trak->mdia->minf->stbl->stsz->sample_count;
+}
+
+static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
+{
+    if( !stts || !stts->list )
+        return 0;
+    uint64_t dts = 0;
+    uint32_t i = 1;
+    lsmash_entry_t *entry;
+    isom_stts_entry_t *data;
+    for( entry = stts->list->head; entry; entry = entry->next )
+    {
+        data = (isom_stts_entry_t *)entry->data;
+        if( !data )
+            return 0;
+        if( i + data->sample_count > sample_number )
+            break;
+        dts += (uint64_t)data->sample_delta * data->sample_count;
+        i += data->sample_count;
+    }
+    if( !entry )
+        return 0;
+    dts += (uint64_t)data->sample_delta * (sample_number - i);
+    return dts;
+}
+
+#if 0
+static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
+{
+    if( !stts || !stts->list )
+        return 0;
+    if( !ctts )
+        return isom_get_dts( stts, sample_number );
+    uint32_t i = 1;     /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */
+    lsmash_entry_t *entry;
+    isom_ctts_entry_t *data;
+    if( sample_number == 0 )
+        return 0;
+    for( entry = ctts->list->head; entry; entry = entry->next )
+    {
+        data = (isom_ctts_entry_t *)entry->data;
+        if( !data )
+            return 0;
+        if( i + data->sample_count > sample_number )
+            break;
+        i += data->sample_count;
+    }
+    if( !entry )
+        return 0;
+    return isom_get_dts( stts, sample_number ) + data->sample_offset;
+}
+#endif
+
+static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
+{
+    if( !stbl || !stbl->stts || !stbl->stts->list || !stbl->stts->list->tail || !stbl->stts->list->tail->data )
+        return -1;
+    isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
+    if( sample_delta != last_stts_data->sample_delta )
+    {
+        if( last_stts_data->sample_count > 1 )
+        {
+            last_stts_data->sample_count -= 1;
+            if( isom_add_stts_entry( stbl, sample_delta ) )
+                return -1;
+        }
+        else
+            last_stts_data->sample_delta = sample_delta;
+    }
+    return 0;
+}
+
+static int isom_update_mdhd_duration( isom_trak_entry_t *trak, uint32_t last_sample_delta )
+{
+    if( !trak || !trak->root || !trak->cache || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf
+     || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
+        return -1;
+    lsmash_root_t *root = trak->root;
+    isom_mdhd_t *mdhd = trak->mdia->mdhd;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_stts_t *stts = stbl->stts;
+    isom_ctts_t *ctts = stbl->ctts;
+    isom_cslg_t *cslg = stbl->cslg;
+    mdhd->duration = 0;
+    uint32_t sample_count = isom_get_sample_count( trak );
+    if( !sample_count )
+    {
+        /* Return error if non-fragmented movie has no samples. */
+        if( !root->fragment && !stts->list->entry_count )
+            return -1;
+        return 0;
+    }
+    /* Now we have at least 1 sample, so do stts_entry. */
+    lsmash_entry_t *last_stts = stts->list->tail;
+    isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
+    if( sample_count == 1 )
+        mdhd->duration = last_stts_data->sample_delta;
+    /* Now we have at least 2 samples,
+     * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
+    else if( !ctts )
+    {
+        /* use dts instead of cts */
+        mdhd->duration = isom_get_dts( stts, sample_count );
+        if( last_sample_delta )
+        {
+            mdhd->duration += last_sample_delta;
+            if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
+                return -1;
+        }
+        else if( last_stts_data->sample_count > 1 )
+            mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
+        else
+        {
+            /* Remove the last entry. */
+            if( lsmash_remove_entry( stts->list, stts->list->entry_count, NULL ) )
+                return -1;
+            /* copy the previous sample_delta. */
+            ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
+            mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
+        }
+    }
+    else
+    {
+        if( !ctts->list || ctts->list->entry_count == 0 )
+            return -1;
+        uint64_t dts = 0;
+        uint64_t max_cts = 0, max2_cts = 0, min_cts = UINT64_MAX;
+        uint32_t max_offset = 0, min_offset = UINT32_MAX;
+        int32_t  ctd_shift = trak->cache->timestamp.ctd_shift;
+        uint32_t j, k;
+        lsmash_entry_t *stts_entry = stts->list->head;
+        lsmash_entry_t *ctts_entry = ctts->list->head;
+        j = k = 0;
+        for( uint32_t i = 0; i < sample_count; i++ )
+        {
+            if( !ctts_entry || !stts_entry )
+                return -1;
+            isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
+            isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
+            if( !stts_data || !ctts_data )
+                return -1;
+            uint64_t cts;
+            if( ctd_shift )
+            {
+                /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
+                int32_t sample_offset = (int32_t)ctts_data->sample_offset;
+                cts = dts + sample_offset + ctd_shift;
+                max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset );
+                min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset );
+            }
+            else
+            {
+                cts = dts + ctts_data->sample_offset;
+                max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
+                min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
+            }
+            min_cts = LSMASH_MIN( min_cts, cts );
+            if( max_cts < cts )
+            {
+                max2_cts = max_cts;
+                max_cts = cts;
+            }
+            else if( max2_cts < cts )
+                max2_cts = cts;
+            dts += stts_data->sample_delta;
+            /* If finished sample_count of current entry, move to next. */
+            if( ++j == ctts_data->sample_count )
+            {
+                ctts_entry = ctts_entry->next;
+                j = 0;
+            }
+            if( ++k == stts_data->sample_count )
+            {
+                stts_entry = stts_entry->next;
+                k = 0;
+            }
+        }
+        dts -= last_stts_data->sample_delta;
+        if( root->fragment )
+            /* Overall presentation is extended exceeding this initial movie.
+             * So, any players shall display the movie exceeding the durations
+             * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
+             * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
+             * In the absence of Movie Extends Header Box, all samples shall be displayed. */
+            mdhd->duration += dts + last_sample_delta;
+        else
+        {
+            if( !last_sample_delta )
+            {
+                /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
+                last_sample_delta = max_cts - max2_cts;
+            }
+            mdhd->duration = max_cts - min_cts + last_sample_delta;
+            /* To match dts and media duration, update stts and mdhd relatively. */
+            if( mdhd->duration > dts )
+                last_sample_delta = mdhd->duration - dts;
+            else
+                mdhd->duration = dts + last_sample_delta;   /* media duration must not less than last dts. */
+        }
+        if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
+            return -1;
+        /* Explicit composition information and timeline shifting  */
+        if( cslg || root->qt_compatible || root->max_isom_version >= 4 )
+        {
+            if( ctd_shift )
+            {
+                /* Remove composition to decode timeline shift. */
+                max_cts  -= ctd_shift;
+                max2_cts -= ctd_shift;
+                min_cts  -= ctd_shift;
+            }
+            int64_t composition_end_time = max_cts + (max_cts - max2_cts);
+            if( !root->fragment
+             && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX)
+             && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
+            {
+                if( !cslg )
+                {
+                    if( isom_add_cslg( trak->mdia->minf->stbl ) )
+                        return -1;
+                    cslg = stbl->cslg;
+                }
+                cslg->compositionToDTSShift = ctd_shift;
+                cslg->leastDecodeToDisplayDelta = min_offset;
+                cslg->greatestDecodeToDisplayDelta = max_offset;
+                cslg->compositionStartTime = min_cts;
+                cslg->compositionEndTime = composition_end_time;
+            }
+            else
+            {
+                if( cslg )
+                    free( cslg );
+                stbl->cslg = NULL;
+            }
+        }
+    }
+    if( mdhd->duration > UINT32_MAX )
+        mdhd->version = 1;
+    return 0;
+}
+
+static int isom_update_mvhd_duration( isom_moov_t *moov )
+{
+    if( !moov || !moov->mvhd )
+        return -1;
+    isom_mvhd_t *mvhd = moov->mvhd;
+    mvhd->duration = 0;
+    for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        /* We pick maximum track duration as movie duration. */
+        isom_trak_entry_t *data = (isom_trak_entry_t *)entry->data;
+        if( !data || !data->tkhd )
+            return -1;
+        mvhd->duration = entry != moov->trak_list->head ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) : data->tkhd->duration;
+    }
+    if( mvhd->duration > UINT32_MAX )
+        mvhd->version = 1;
+    return 0;
+}
+
+static int isom_update_tkhd_duration( isom_trak_entry_t *trak )
+{
+    if( !trak || !trak->tkhd || !trak->root || !trak->root->moov )
+        return -1;
+    lsmash_root_t *root = trak->root;
+    isom_tkhd_t *tkhd = trak->tkhd;
+    tkhd->duration = 0;
+    if( root->fragment || !trak->edts || !trak->edts->elst )
+    {
+        /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
+        if( !trak->mdia || !trak->mdia->mdhd || !root->moov->mvhd || !trak->mdia->mdhd->timescale )
+            return -1;
+        if( !trak->mdia->mdhd->duration && isom_update_mdhd_duration( trak, 0 ) )
+            return -1;
+        tkhd->duration = trak->mdia->mdhd->duration * ((double)root->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
+    }
+    else
+    {
+        /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
+        for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
+        {
+            isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
+            if( !data )
+                return -1;
+            tkhd->duration += data->segment_duration;
+        }
+    }
+    if( tkhd->duration > UINT32_MAX )
+        tkhd->version = 1;
+    if( !root->fragment && !tkhd->duration )
+        tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
+    return isom_update_mvhd_duration( root->moov );
+}
+
+int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak )
+        return -1;
+    if( isom_update_mdhd_duration( trak, last_sample_delta ) )
+        return -1;
+    /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
+    return (!root->fragment && trak->edts && trak->edts->elst)
+         ? isom_update_mvhd_duration( root->moov )  /* Only update movie duration. */
+         : isom_update_tkhd_duration( trak );       /* Also update movie duration internally. */
+}
+
+int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number,
+    uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility, uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne,
+    uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
+        return -1;
+    isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
+    if( !data )
+        return -1;
+    isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
+    if( !avcC )
+        return -1;
+    avcC->configurationVersion = configurationVersion;
+    avcC->AVCProfileIndication = AVCProfileIndication;
+    avcC->profile_compatibility = profile_compatibility;
+    avcC->AVCLevelIndication = AVCLevelIndication;
+    avcC->lengthSizeMinusOne = lengthSizeMinusOne;
+    if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) )
+    {
+        avcC->chroma_format = chroma_format;
+        avcC->bit_depth_luma_minus8 = bit_depth_luma_minus8;
+        avcC->bit_depth_chroma_minus8 = bit_depth_chroma_minus8;
+    }
+    return 0;
+}
+
+static int isom_update_bitrate_info( isom_mdia_t *mdia )
+{
+    if( !mdia || !mdia->mdhd || !mdia->minf || !mdia->minf->stbl
+     || !mdia->minf->stbl->stsd || !mdia->minf->stbl->stsd->list
+     || !mdia->minf->stbl->stsz || !mdia->minf->stbl->stts || !mdia->minf->stbl->stts->list )
+        return -1;
+    /* Not supporting multi sample entries yet. */
+    isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( mdia->minf->stbl->stsd->list, 1 );
+    if( !sample_entry )
+        return -1;
+    struct bitrate_info_t
+    {
+        uint32_t bufferSizeDB;
+        uint32_t maxBitrate;
+        uint32_t avgBitrate;
+    } info = { 0, 0, 0 };
+    uint32_t i = 0;
+    uint32_t rate = 0;
+    uint32_t time_wnd = 0;
+    uint32_t timescale = mdia->mdhd->timescale;
+    uint64_t dts = 0;
+    isom_stsz_t *stsz = mdia->minf->stbl->stsz;
+    lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
+    lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
+    isom_stts_entry_t *stts_data = NULL;
+    while( stts_entry )
+    {
+        uint32_t size;
+        if( stsz->list )
+        {
+            if( !stsz_entry )
+                break;
+            isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
+            if( !stsz_data )
+                return -1;
+            size = stsz_data->entry_size;
+            stsz_entry = stsz_entry->next;
+        }
+        else
+            size = stsz->sample_size;
+        if( stts_data )
+            dts += stts_data->sample_delta;
+        stts_data = (isom_stts_entry_t *)stts_entry->data;
+        if( ++i == stts_data->sample_count )
+        {
+            stts_entry = stts_entry->next;
+            i = 0;
+        }
+        if( info.bufferSizeDB < size )
+            info.bufferSizeDB = size;
+        info.avgBitrate += size;
+        rate += size;
+        if( dts > time_wnd + timescale )
+        {
+            if( rate > info.maxBitrate )
+                info.maxBitrate = rate;
+            time_wnd = dts;
+            rate = 0;
+        }
+    }
+    double duration = (double)mdia->mdhd->duration / timescale;
+    info.avgBitrate = (uint32_t)(info.avgBitrate / duration);
+    if( !info.maxBitrate )
+        info.maxBitrate = info.avgBitrate;
+    /* move to bps */
+    info.maxBitrate *= 8;
+    info.avgBitrate *= 8;
+    /* set bitrate info */
+    switch( sample_entry->type )
+    {
+        case ISOM_CODEC_TYPE_AVC1_VIDEO :
+        case ISOM_CODEC_TYPE_AVC2_VIDEO :
+        case ISOM_CODEC_TYPE_AVCP_VIDEO :
+        {
+            isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
+            if( !stsd_data )
+                return -1;
+            isom_btrt_t *btrt = stsd_data->btrt;
+            if( btrt )
+            {
+                btrt->bufferSizeDB = info.bufferSizeDB;
+                btrt->maxBitrate   = info.maxBitrate;
+                btrt->avgBitrate   = info.avgBitrate;
+            }
+            break;
+        }
+        case ISOM_CODEC_TYPE_MP4V_VIDEO :
+        {
+            isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
+            if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES )
+                return -1;
+            isom_esds_t *esds = stsd_data->esds;
+            /* FIXME: avgBitrate is 0 only if VBR in proper. */
+            if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) )
+                return -1;
+            break;
+        }
+        case ISOM_CODEC_TYPE_MP4A_AUDIO :
+        {
+            isom_esds_t *esds = NULL;
+            if( ((isom_audio_entry_t *)sample_entry)->version )
+            {
+                /* MPEG-4 Audio in QTFF */
+                isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
+                if( !stsd_data || !stsd_data->wave || !stsd_data->wave->esds || !stsd_data->wave->esds->ES )
+                    return -1;
+                esds = stsd_data->wave->esds;
+            }
+            else
+            {
+                isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
+                if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES )
+                    return -1;
+                esds = stsd_data->esds;
+            }
+            /* FIXME: avgBitrate is 0 only if VBR in proper. */
+            if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) )
+                return -1;
+            break;
+        }
+        case ISOM_CODEC_TYPE_ALAC_AUDIO :
+        {
+            isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
+            if( !alac )
+                return -1;
+            if( alac->exdata_length < 36 || !alac->exdata )
+            {
+                isom_wave_t *wave = alac->wave;
+                if( !wave || wave->exdata_length < 36 || !wave->exdata )
+                    return -1;
+                break;      /* Apparently, average bitrate field is 0. */
+            }
+            uint8_t *exdata = (uint8_t *)alac->exdata + 28;
+            exdata[0] = (info.avgBitrate >> 24) & 0xff;
+            exdata[1] = (info.avgBitrate >> 16) & 0xff;
+            exdata[2] = (info.avgBitrate >>  8) & 0xff;
+            exdata[3] =  info.avgBitrate        & 0xff;
+            break;
+        }
+        case ISOM_CODEC_TYPE_EC_3_AUDIO :
+        {
+            isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry;
+            if( !eac3 )
+                return -1;
+            if( eac3->exdata_length < 10 || !eac3->exdata )
+                return -1;
+            uint16_t bitrate;
+            if( stsz->list )
+                bitrate = info.maxBitrate / 1000;   /* Use maximum bitrate if VBR. */
+            else
+                bitrate = stsz->sample_size * (eac3->samplerate >> 16) / 192000;    /* 192000 == 1536 * 1000 / 8 */
+            uint8_t *exdata = (uint8_t *)eac3->exdata + 8;
+            exdata[0] = (bitrate >> 5) & 0xff;
+            exdata[1] = (bitrate & 0x1f) << 3;
+            break;
+        }
+        default :
+            break;
+    }
+    return 0;
+}
+
+static int isom_check_mandatory_boxes( lsmash_root_t *root )
+{
+    if( !root )
+        return -1;
+    if( !root->moov || !root->moov->mvhd )
+        return -1;
+    if( !root->moov->trak_list )
+        return -1;
+    /* A movie requires at least one track. */
+    if( !root->moov->trak_list->head )
+        return -1;
+    for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak
+         || !trak->tkhd
+         || !trak->mdia
+         || !trak->mdia->mdhd
+         || !trak->mdia->hdlr
+         || !trak->mdia->minf
+         || !trak->mdia->minf->dinf
+         || !trak->mdia->minf->dinf->dref
+         || !trak->mdia->minf->stbl
+         || !trak->mdia->minf->stbl->stsd
+         || !trak->mdia->minf->stbl->stsz
+         || !trak->mdia->minf->stbl->stts
+         || !trak->mdia->minf->stbl->stsc
+         || !trak->mdia->minf->stbl->stco )
+            return -1;
+        if( root->qt_compatible && !trak->mdia->minf->hdlr )
+            return -1;
+        isom_stbl_t *stbl = trak->mdia->minf->stbl;
+        if( !stbl->stsd->list || !stbl->stsd->list->head )
+            return -1;
+        if( !root->fragment
+         && (!stbl->stsd->list || !stbl->stsd->list->head
+         || !stbl->stts->list || !stbl->stts->list->head
+         || !stbl->stsc->list || !stbl->stsc->list->head
+         || !stbl->stco->list || !stbl->stco->list->head) )
+            return -1;
+    }
+    if( !root->fragment )
+        return 0;
+    if( !root->moov->mvex || !root->moov->mvex->trex_list )
+        return -1;
+    for( lsmash_entry_t *entry = root->moov->mvex->trex_list->head; entry; entry = entry->next )
+        if( !entry->data )  /* trex */
+            return -1;
+    return 0;
+}
+
+static inline uint64_t isom_get_current_mp4time( void )
+{
+    return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
+}
+
+static int isom_set_media_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
+{
+    if( !trak->mdia || !trak->mdia->mdhd )
+        return -1;
+    isom_mdhd_t *mdhd = trak->mdia->mdhd;
+    if( !mdhd->creation_time )
+        mdhd->creation_time = mdhd->modification_time = current_mp4time;
+    return 0;
+}
+
+static int isom_set_track_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
+{
+    if( !trak || !trak->tkhd )
+        return -1;
+    isom_tkhd_t *tkhd = trak->tkhd;
+    if( !tkhd->creation_time )
+        tkhd->creation_time = tkhd->modification_time = current_mp4time;
+    if( isom_set_media_creation_time( trak, current_mp4time ) )
+        return -1;
+    return 0;
+}
+
+static int isom_set_movie_creation_time( lsmash_root_t *root )
+{
+    if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list )
+        return -1;
+    uint64_t current_mp4time = isom_get_current_mp4time();
+    for( uint32_t i = 1; i <= root->moov->trak_list->entry_count; i++ )
+        if( isom_set_track_creation_time( isom_get_trak( root, i ), current_mp4time ) )
+            return -1;
+    isom_mvhd_t *mvhd = root->moov->mvhd;
+    if( !mvhd->creation_time )
+        mvhd->creation_time = mvhd->modification_time = current_mp4time;
+    return 0;
+}
+
+#define CHECK_LARGESIZE( size ) if( (size) > UINT32_MAX ) (size) += 8
+
+static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd )
+{
+    if( !mvhd )
+        return 0;
+    mvhd->version = 0;
+    if( mvhd->creation_time > UINT32_MAX || mvhd->modification_time > UINT32_MAX || mvhd->duration > UINT32_MAX )
+        mvhd->version = 1;
+    mvhd->size = ISOM_FULLBOX_COMMON_SIZE + 96 + (uint64_t)mvhd->version * 12;
+    CHECK_LARGESIZE( mvhd->size );
+    return mvhd->size;
+}
+
+static uint64_t isom_update_iods_size( isom_iods_t *iods )
+{
+    if( !iods || !iods->OD )
+        return 0;
+    iods->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ObjectDescriptor_size( iods->OD );
+    CHECK_LARGESIZE( iods->size );
+    return iods->size;
+}
+
+static uint64_t isom_update_tkhd_size( isom_tkhd_t *tkhd )
+{
+    if( !tkhd )
+        return 0;
+    tkhd->version = 0;
+    if( tkhd->creation_time > UINT32_MAX || tkhd->modification_time > UINT32_MAX || tkhd->duration > UINT32_MAX )
+        tkhd->version = 1;
+    tkhd->size = ISOM_FULLBOX_COMMON_SIZE + 80 + (uint64_t)tkhd->version * 12;
+    CHECK_LARGESIZE( tkhd->size );
+    return tkhd->size;
+}
+
+static uint64_t isom_update_clef_size( isom_clef_t *clef )
+{
+    if( !clef )
+        return 0;
+    clef->size = ISOM_FULLBOX_COMMON_SIZE + 8;
+    CHECK_LARGESIZE( clef->size );
+    return clef->size;
+}
+
+static uint64_t isom_update_prof_size( isom_prof_t *prof )
+{
+    if( !prof )
+        return 0;
+    prof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
+    CHECK_LARGESIZE( prof->size );
+    return prof->size;
+}
+
+static uint64_t isom_update_enof_size( isom_enof_t *enof )
+{
+    if( !enof )
+        return 0;
+    enof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
+    CHECK_LARGESIZE( enof->size );
+    return enof->size;
+}
+
+static uint64_t isom_update_tapt_size( isom_tapt_t *tapt )
+{
+    if( !tapt )
+        return 0;
+    tapt->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_clef_size( tapt->clef )
+        + isom_update_prof_size( tapt->prof )
+        + isom_update_enof_size( tapt->enof );
+    CHECK_LARGESIZE( tapt->size );
+    return tapt->size;
+}
+
+static uint64_t isom_update_elst_size( isom_elst_t *elst )
+{
+    if( !elst || !elst->list )
+        return 0;
+    uint32_t i = 0;
+    elst->version = 0;
+    for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next, i++ )
+    {
+        isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
+        if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
+            elst->version = 1;
+    }
+    elst->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)i * ( elst->version ? 20 : 12 );
+    CHECK_LARGESIZE( elst->size );
+    return elst->size;
+}
+
+static uint64_t isom_update_edts_size( isom_edts_t *edts )
+{
+    if( !edts )
+        return 0;
+    edts->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_elst_size( edts->elst );
+    CHECK_LARGESIZE( edts->size );
+    return edts->size;
+}
+
+static uint64_t isom_update_tref_size( isom_tref_t *tref )
+{
+    if( !tref )
+        return 0;
+    tref->size = ISOM_BASEBOX_COMMON_SIZE;
+    if( tref->ref_list )
+        for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
+        {
+            isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
+            ref->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)ref->ref_count * 4;
+            CHECK_LARGESIZE( ref->size );
+            tref->size += ref->size;
+        }
+    CHECK_LARGESIZE( tref->size );
+    return tref->size;
+}
+
+static uint64_t isom_update_mdhd_size( isom_mdhd_t *mdhd )
+{
+    if( !mdhd )
+        return 0;
+    mdhd->version = 0;
+    if( mdhd->creation_time > UINT32_MAX || mdhd->modification_time > UINT32_MAX || mdhd->duration > UINT32_MAX )
+        mdhd->version = 1;
+    mdhd->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)mdhd->version * 12;
+    CHECK_LARGESIZE( mdhd->size );
+    return mdhd->size;
+}
+
+static uint64_t isom_update_hdlr_size( isom_hdlr_t *hdlr )
+{
+    if( !hdlr )
+        return 0;
+    hdlr->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)hdlr->componentName_length;
+    CHECK_LARGESIZE( hdlr->size );
+    return hdlr->size;
+}
+
+static uint64_t isom_update_dref_entry_size( isom_dref_entry_t *urln )
+{
+    if( !urln )
+        return 0;
+    urln->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)urln->name_length + urln->location_length;
+    CHECK_LARGESIZE( urln->size );
+    return urln->size;
+}
+
+static uint64_t isom_update_dref_size( isom_dref_t *dref )
+{
+    if( !dref || !dref->list )
+        return 0;
+    dref->size = ISOM_LIST_FULLBOX_COMMON_SIZE;
+    if( dref->list )
+        for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
+        {
+            isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
+            dref->size += isom_update_dref_entry_size( data );
+        }
+    CHECK_LARGESIZE( dref->size );
+    return dref->size;
+}
+
+static uint64_t isom_update_dinf_size( isom_dinf_t *dinf )
+{
+    if( !dinf )
+        return 0;
+    dinf->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_dref_size( dinf->dref );
+    CHECK_LARGESIZE( dinf->size );
+    return dinf->size;
+}
+
+static uint64_t isom_update_vmhd_size( isom_vmhd_t *vmhd )
+{
+    if( !vmhd )
+        return 0;
+    vmhd->size = ISOM_FULLBOX_COMMON_SIZE + 8;
+    CHECK_LARGESIZE( vmhd->size );
+    return vmhd->size;
+}
+
+static uint64_t isom_update_smhd_size( isom_smhd_t *smhd )
+{
+    if( !smhd )
+        return 0;
+    smhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
+    CHECK_LARGESIZE( smhd->size );
+    return smhd->size;
+}
+
+static uint64_t isom_update_hmhd_size( isom_hmhd_t *hmhd )
+{
+    if( !hmhd )
+        return 0;
+    hmhd->size = ISOM_FULLBOX_COMMON_SIZE + 16;
+    CHECK_LARGESIZE( hmhd->size );
+    return hmhd->size;
+}
+
+static uint64_t isom_update_nmhd_size( isom_nmhd_t *nmhd )
+{
+    if( !nmhd )
+        return 0;
+    nmhd->size = ISOM_FULLBOX_COMMON_SIZE;
+    CHECK_LARGESIZE( nmhd->size );
+    return nmhd->size;
+}
+
+static uint64_t isom_update_gmin_size( isom_gmin_t *gmin )
+{
+    if( !gmin )
+        return 0;
+    gmin->size = ISOM_FULLBOX_COMMON_SIZE + 12;
+    CHECK_LARGESIZE( gmin->size );
+    return gmin->size;
+}
+
+static uint64_t isom_update_text_size( isom_text_t *text )
+{
+    if( !text )
+        return 0;
+    text->size = ISOM_BASEBOX_COMMON_SIZE + 36;
+    CHECK_LARGESIZE( text->size );
+    return text->size;
+}
+
+static uint64_t isom_update_gmhd_size( isom_gmhd_t *gmhd )
+{
+    if( !gmhd )
+        return 0;
+    gmhd->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_gmin_size( gmhd->gmin )
+        + isom_update_text_size( gmhd->text );
+    CHECK_LARGESIZE( gmhd->size );
+    return gmhd->size;
+}
+
+static uint64_t isom_update_pasp_size( isom_pasp_t *pasp )
+{
+    if( !pasp )
+        return 0;
+    pasp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
+    CHECK_LARGESIZE( pasp->size );
+    return pasp->size;
+}
+
+static uint64_t isom_update_clap_size( isom_clap_t *clap )
+{
+    if( !clap )
+        return 0;
+    clap->size = ISOM_BASEBOX_COMMON_SIZE + 32;
+    CHECK_LARGESIZE( clap->size );
+    return clap->size;
+}
+
+static uint64_t isom_update_colr_size( isom_colr_t *colr )
+{
+    if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
+        return 0;
+    colr->size = ISOM_BASEBOX_COMMON_SIZE + 10;
+    CHECK_LARGESIZE( colr->size );
+    return colr->size;
+}
+
+static uint64_t isom_update_stsl_size( isom_stsl_t *stsl )
+{
+    if( !stsl )
+        return 0;
+    stsl->size = ISOM_FULLBOX_COMMON_SIZE + 6;
+    CHECK_LARGESIZE( stsl->size );
+    return stsl->size;
+}
+
+static uint64_t isom_update_esds_size( isom_esds_t *esds )
+{
+    if( !esds )
+        return 0;
+    esds->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES );
+    CHECK_LARGESIZE( esds->size );
+    return esds->size;
+}
+
+static uint64_t isom_update_avcC_size( isom_avcC_t *avcC )
+{
+    if( !avcC || !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
+        return 0;
+    uint64_t size = ISOM_BASEBOX_COMMON_SIZE + 7;
+    for( lsmash_entry_t *entry = avcC->sequenceParameterSets->head; entry; entry = entry->next )
+    {
+        isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
+        size += 2 + data->parameterSetLength;
+    }
+    for( lsmash_entry_t *entry = avcC->pictureParameterSets->head; entry; entry = entry->next )
+    {
+        isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
+        size += 2 + data->parameterSetLength;
+    }
+    if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
+    {
+        size += 4;
+        for( lsmash_entry_t *entry = avcC->sequenceParameterSetExt->head; entry; entry = entry->next )
+        {
+            isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
+            size += 2 + data->parameterSetLength;
+        }
+    }
+    avcC->size = size;
+    CHECK_LARGESIZE( avcC->size );
+    return avcC->size;
+}
+
+static uint64_t isom_update_btrt_size( isom_btrt_t *btrt )
+{
+    if( !btrt )
+        return 0;
+    btrt->size = ISOM_BASEBOX_COMMON_SIZE + 12;
+    CHECK_LARGESIZE( btrt->size );
+    return btrt->size;
+}
+
+static uint64_t isom_update_visual_entry_size( isom_visual_entry_t *visual )
+{
+    if( !visual )
+        return 0;
+    visual->size = ISOM_BASEBOX_COMMON_SIZE + 78
+        + isom_update_avcC_size( visual->avcC )
+        + isom_update_btrt_size( visual->btrt )
+        + isom_update_esds_size( visual->esds )
+        + isom_update_colr_size( visual->colr )
+        + isom_update_stsl_size( visual->stsl )
+        + isom_update_clap_size( visual->clap )
+        + isom_update_pasp_size( visual->pasp )
+        + (uint64_t)visual->exdata_length;
+    CHECK_LARGESIZE( visual->size );
+    return visual->size;
+}
+
+#if 0
+static uint64_t isom_update_mp4s_entry_size( isom_mp4s_entry_t *mp4s )
+{
+    if( !mp4s || mp4s->type != ISOM_CODEC_TYPE_MP4S_SYSTEM )
+        return 0;
+    mp4s->size = ISOM_BASEBOX_COMMON_SIZE + 8 + isom_update_esds_size( mp4s->esds );
+    CHECK_LARGESIZE( mp4s->size );
+    return mp4s->size;
+}
+#endif
+
+static uint64_t isom_update_frma_size( isom_frma_t *frma )
+{
+    if( !frma )
+        return 0;
+    frma->size = ISOM_BASEBOX_COMMON_SIZE + 4;
+    CHECK_LARGESIZE( frma->size );
+    return frma->size;
+}
+
+static uint64_t isom_update_enda_size( isom_enda_t *enda )
+{
+    if( !enda )
+        return 0;
+    enda->size = ISOM_BASEBOX_COMMON_SIZE + 2;
+    CHECK_LARGESIZE( enda->size );
+    return enda->size;
+}
+
+static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a )
+{
+    if( !mp4a )
+        return 0;
+    mp4a->size = ISOM_BASEBOX_COMMON_SIZE + 4;
+    CHECK_LARGESIZE( mp4a->size );
+    return mp4a->size;
+}
+
+static uint64_t isom_update_terminator_size( isom_terminator_t *terminator )
+{
+    if( !terminator )
+        return 0;
+    terminator->size = ISOM_BASEBOX_COMMON_SIZE;
+    CHECK_LARGESIZE( terminator->size );
+    return terminator->size;
+}
+
+static uint64_t isom_update_wave_size( isom_wave_t *wave )
+{
+    if( !wave )
+        return 0;
+    wave->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_frma_size( wave->frma )
+        + isom_update_enda_size( wave->enda )
+        + isom_update_mp4a_size( wave->mp4a )
+        + isom_update_esds_size( wave->esds )
+        + isom_update_terminator_size( wave->terminator )
+        + (uint64_t)wave->exdata_length;
+    CHECK_LARGESIZE( wave->size );
+    return wave->size;
+}
+
+static uint64_t isom_update_chan_size( isom_chan_t *chan )
+{
+    if( !chan )
+        return 0;
+    chan->size = ISOM_FULLBOX_COMMON_SIZE + 12 + 20 * (uint64_t)chan->numberChannelDescriptions;
+    CHECK_LARGESIZE( chan->size );
+    return chan->size;
+}
+
+static uint64_t isom_update_audio_entry_size( isom_audio_entry_t *audio )
+{
+    if( !audio )
+        return 0;
+    audio->size = ISOM_BASEBOX_COMMON_SIZE + 28
+        + isom_update_esds_size( audio->esds )
+        + isom_update_wave_size( audio->wave )
+        + isom_update_chan_size( audio->chan )
+        + (uint64_t)audio->exdata_length;
+    if( audio->version == 1 )
+        audio->size += 16;
+    else if( audio->version == 2 )
+        audio->size += 36;
+    CHECK_LARGESIZE( audio->size );
+    return audio->size;
+}
+
+static uint64_t isom_update_text_entry_size( isom_text_entry_t *text )
+{
+    if( !text )
+        return 0;
+    text->size = ISOM_BASEBOX_COMMON_SIZE + 51 + (uint64_t)text->font_name_length;
+    CHECK_LARGESIZE( text->size );
+    return text->size;
+}
+
+static uint64_t isom_update_ftab_size( isom_ftab_t *ftab )
+{
+    if( !ftab || !ftab->list )
+        return 0;
+    ftab->size = ISOM_BASEBOX_COMMON_SIZE + 2;
+    for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
+    {
+        isom_font_record_t *data = (isom_font_record_t *)entry->data;
+        ftab->size += 3 + data->font_name_length;
+    }
+    CHECK_LARGESIZE( ftab->size );
+    return ftab->size;
+}
+
+static uint64_t isom_update_tx3g_entry_size( isom_tx3g_entry_t *tx3g )
+{
+    if( !tx3g )
+        return 0;
+    tx3g->size = ISOM_BASEBOX_COMMON_SIZE + 38 + isom_update_ftab_size( tx3g->ftab );
+    CHECK_LARGESIZE( tx3g->size );
+    return tx3g->size;
+}
+
+static uint64_t isom_update_stsd_size( isom_stsd_t *stsd )
+{
+    if( !stsd || !stsd->list )
+        return 0;
+    uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE;
+    for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
+    {
+        isom_sample_entry_t *data = (isom_sample_entry_t *)entry->data;
+        switch( data->type )
+        {
+            case ISOM_CODEC_TYPE_AVC1_VIDEO :
+#ifdef LSMASH_DEMUXER_ENABLED
+            case ISOM_CODEC_TYPE_MP4V_VIDEO :
+#endif
+#if 0
+            case ISOM_CODEC_TYPE_AVC2_VIDEO :
+            case ISOM_CODEC_TYPE_AVCP_VIDEO :
+            case ISOM_CODEC_TYPE_SVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC1_VIDEO :
+            case ISOM_CODEC_TYPE_MVC2_VIDEO :
+            case ISOM_CODEC_TYPE_DRAC_VIDEO :
+            case ISOM_CODEC_TYPE_ENCV_VIDEO :
+            case ISOM_CODEC_TYPE_MJP2_VIDEO :
+            case ISOM_CODEC_TYPE_S263_VIDEO :
+            case ISOM_CODEC_TYPE_VC_1_VIDEO :
+#endif
+                size += isom_update_visual_entry_size( (isom_visual_entry_t *)data );
+                break;
+#if 0
+            case ISOM_CODEC_TYPE_MP4S_SYSTEM :
+                size += isom_update_mp4s_entry_size( (isom_mp4s_entry_t *)data );
+                break;
+#endif
+            case ISOM_CODEC_TYPE_MP4A_AUDIO :
+            case ISOM_CODEC_TYPE_AC_3_AUDIO :
+            case ISOM_CODEC_TYPE_ALAC_AUDIO :
+            case ISOM_CODEC_TYPE_EC_3_AUDIO :
+            case ISOM_CODEC_TYPE_SAMR_AUDIO :
+            case ISOM_CODEC_TYPE_SAWB_AUDIO :
+            case QT_CODEC_TYPE_23NI_AUDIO :
+            case QT_CODEC_TYPE_NONE_AUDIO :
+            case QT_CODEC_TYPE_LPCM_AUDIO :
+            case QT_CODEC_TYPE_RAW_AUDIO :
+            case QT_CODEC_TYPE_SOWT_AUDIO :
+            case QT_CODEC_TYPE_TWOS_AUDIO :
+            case QT_CODEC_TYPE_FL32_AUDIO :
+            case QT_CODEC_TYPE_FL64_AUDIO :
+            case QT_CODEC_TYPE_IN24_AUDIO :
+            case QT_CODEC_TYPE_IN32_AUDIO :
+            case QT_CODEC_TYPE_NOT_SPECIFIED :
+#if 0
+            case ISOM_CODEC_TYPE_DRA1_AUDIO :
+            case ISOM_CODEC_TYPE_DTSC_AUDIO :
+            case ISOM_CODEC_TYPE_DTSH_AUDIO :
+            case ISOM_CODEC_TYPE_DTSL_AUDIO :
+            case ISOM_CODEC_TYPE_ENCA_AUDIO :
+            case ISOM_CODEC_TYPE_G719_AUDIO :
+            case ISOM_CODEC_TYPE_G726_AUDIO :
+            case ISOM_CODEC_TYPE_M4AE_AUDIO :
+            case ISOM_CODEC_TYPE_MLPA_AUDIO :
+            case ISOM_CODEC_TYPE_RAW_AUDIO :
+            case ISOM_CODEC_TYPE_SAWP_AUDIO :
+            case ISOM_CODEC_TYPE_SEVC_AUDIO :
+            case ISOM_CODEC_TYPE_SQCP_AUDIO :
+            case ISOM_CODEC_TYPE_SSMV_AUDIO :
+            case ISOM_CODEC_TYPE_TWOS_AUDIO :
+#endif
+                size += isom_update_audio_entry_size( (isom_audio_entry_t *)data );
+                break;
+            case ISOM_CODEC_TYPE_TX3G_TEXT :
+                size += isom_update_tx3g_entry_size( (isom_tx3g_entry_t *)data );
+                break;
+            case QT_CODEC_TYPE_TEXT_TEXT :
+                size += isom_update_text_entry_size( (isom_text_entry_t *)data );
+                break;
+            default :
+                break;
+        }
+    }
+    stsd->size = size;
+    CHECK_LARGESIZE( stsd->size );
+    return stsd->size;
+}
+
+static uint64_t isom_update_stts_size( isom_stts_t *stts )
+{
+    if( !stts || !stts->list )
+        return 0;
+    stts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stts->list->entry_count * 8;
+    CHECK_LARGESIZE( stts->size );
+    return stts->size;
+}
+
+static uint64_t isom_update_ctts_size( isom_ctts_t *ctts )
+{
+    if( !ctts || !ctts->list )
+        return 0;
+    ctts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)ctts->list->entry_count * 8;
+    CHECK_LARGESIZE( ctts->size );
+    return ctts->size;
+}
+
+static uint64_t isom_update_cslg_size( isom_cslg_t *cslg )
+{
+    if( !cslg )
+        return 0;
+    cslg->size = ISOM_FULLBOX_COMMON_SIZE + 20;
+    CHECK_LARGESIZE( cslg->size );
+    return cslg->size;
+}
+
+static uint64_t isom_update_stsz_size( isom_stsz_t *stsz )
+{
+    if( !stsz )
+        return 0;
+    stsz->size = ISOM_FULLBOX_COMMON_SIZE + 8 + ( stsz->list ? (uint64_t)stsz->list->entry_count * 4 : 0 );
+    CHECK_LARGESIZE( stsz->size );
+    return stsz->size;
+}
+
+static uint64_t isom_update_stss_size( isom_stss_t *stss )
+{
+    if( !stss || !stss->list )
+        return 0;
+    stss->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stss->list->entry_count * 4;
+    CHECK_LARGESIZE( stss->size );
+    return stss->size;
+}
+
+static uint64_t isom_update_stps_size( isom_stps_t *stps )
+{
+    if( !stps || !stps->list )
+        return 0;
+    stps->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stps->list->entry_count * 4;
+    CHECK_LARGESIZE( stps->size );
+    return stps->size;
+}
+
+static uint64_t isom_update_sdtp_size( isom_sdtp_t *sdtp )
+{
+    if( !sdtp || !sdtp->list )
+        return 0;
+    sdtp->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)sdtp->list->entry_count;
+    CHECK_LARGESIZE( sdtp->size );
+    return sdtp->size;
+}
+
+static uint64_t isom_update_stsc_size( isom_stsc_t *stsc )
+{
+    if( !stsc || !stsc->list )
+        return 0;
+    stsc->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stsc->list->entry_count * 12;
+    CHECK_LARGESIZE( stsc->size );
+    return stsc->size;
+}
+
+static uint64_t isom_update_stco_size( isom_stco_t *stco )
+{
+    if( !stco || !stco->list )
+        return 0;
+    stco->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stco->list->entry_count * (stco->large_presentation ? 8 : 4);
+    CHECK_LARGESIZE( stco->size );
+    return stco->size;
+}
+
+static uint64_t isom_update_sbgp_size( isom_sbgp_entry_t *sbgp )
+{
+    if( !sbgp || !sbgp->list )
+        return 0;
+    sbgp->size = ISOM_LIST_FULLBOX_COMMON_SIZE + 4 + (uint64_t)sbgp->list->entry_count * 8;
+    CHECK_LARGESIZE( sbgp->size );
+    return sbgp->size;
+}
+
+static uint64_t isom_update_sgpd_size( isom_sgpd_entry_t *sgpd )
+{
+    if( !sgpd || !sgpd->list )
+        return 0;
+    uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE + (1 + (sgpd->version == 1)) * 4;
+    size += (uint64_t)sgpd->list->entry_count * ((sgpd->version == 1) && !sgpd->default_length) * 4;
+    switch( sgpd->grouping_type )
+    {
+        case ISOM_GROUP_TYPE_RAP :
+            size += sgpd->list->entry_count;
+            break;
+        case ISOM_GROUP_TYPE_ROLL :
+            size += (uint64_t)sgpd->list->entry_count * 2;
+            break;
+        default :
+            /* We don't consider other grouping types currently. */
+            break;
+    }
+    sgpd->size = size;
+    CHECK_LARGESIZE( sgpd->size );
+    return sgpd->size;
+}
+
+static uint64_t isom_update_stbl_size( isom_stbl_t *stbl )
+{
+    if( !stbl )
+        return 0;
+    stbl->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_stsd_size( stbl->stsd )
+        + isom_update_stts_size( stbl->stts )
+        + isom_update_ctts_size( stbl->ctts )
+        + isom_update_cslg_size( stbl->cslg )
+        + isom_update_stsz_size( stbl->stsz )
+        + isom_update_stss_size( stbl->stss )
+        + isom_update_stps_size( stbl->stps )
+        + isom_update_sdtp_size( stbl->sdtp )
+        + isom_update_stsc_size( stbl->stsc )
+        + isom_update_stco_size( stbl->stco );
+    if( stbl->sgpd_list )
+        for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
+            stbl->size += isom_update_sgpd_size( (isom_sgpd_entry_t *)entry->data );
+    if( stbl->sbgp_list )
+        for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
+            stbl->size += isom_update_sbgp_size( (isom_sbgp_entry_t *)entry->data );
+    CHECK_LARGESIZE( stbl->size );
+    return stbl->size;
+}
+
+static uint64_t isom_update_minf_size( isom_minf_t *minf )
+{
+    if( !minf )
+        return 0;
+    minf->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_vmhd_size( minf->vmhd )
+        + isom_update_smhd_size( minf->smhd )
+        + isom_update_hmhd_size( minf->hmhd )
+        + isom_update_nmhd_size( minf->nmhd )
+        + isom_update_gmhd_size( minf->gmhd )
+        + isom_update_hdlr_size( minf->hdlr )
+        + isom_update_dinf_size( minf->dinf )
+        + isom_update_stbl_size( minf->stbl );
+    CHECK_LARGESIZE( minf->size );
+    return minf->size;
+}
+
+static uint64_t isom_update_mdia_size( isom_mdia_t *mdia )
+{
+    if( !mdia )
+        return 0;
+    mdia->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_mdhd_size( mdia->mdhd )
+        + isom_update_hdlr_size( mdia->hdlr )
+        + isom_update_minf_size( mdia->minf );
+    CHECK_LARGESIZE( mdia->size );
+    return mdia->size;
+}
+
+static uint64_t isom_update_chpl_size( isom_chpl_t *chpl )
+{
+    if( !chpl )
+        return 0;
+    chpl->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (chpl->version == 1) + 1;
+    for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
+    {
+        isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
+        chpl->size += 9 + data->chapter_name_length;
+    }
+    CHECK_LARGESIZE( chpl->size );
+    return chpl->size;
+}
+
+static uint64_t isom_update_mean_size( isom_mean_t *mean )
+{
+    if( !mean )
+        return 0;
+    mean->size = ISOM_FULLBOX_COMMON_SIZE + mean->meaning_string_length;
+    CHECK_LARGESIZE( mean->size );
+    return mean->size;
+}
+
+static uint64_t isom_update_name_size( isom_name_t *name )
+{
+    if( !name )
+        return 0;
+    name->size = ISOM_FULLBOX_COMMON_SIZE + name->name_length;
+    CHECK_LARGESIZE( name->size );
+    return name->size;
+}
+
+static uint64_t isom_update_data_size( isom_data_t *data )
+{
+    if( !data )
+        return 0;
+    data->size = ISOM_BASEBOX_COMMON_SIZE + 8 + data->value_length;
+    CHECK_LARGESIZE( data->size );
+    return data->size;
+}
+
+static uint64_t isom_update_metaitem_size( isom_metaitem_t *metaitem )
+{
+    if( !metaitem )
+        return 0;
+    metaitem->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_mean_size( metaitem->mean )
+        + isom_update_name_size( metaitem->name )
+        + isom_update_data_size( metaitem->data );
+    CHECK_LARGESIZE( metaitem->size );
+    return metaitem->size;
+}
+
+static uint64_t isom_update_ilst_size( isom_ilst_t *ilst )
+{
+    if( !ilst )
+        return 0;
+    ilst->size = ISOM_BASEBOX_COMMON_SIZE;
+    for( lsmash_entry_t *entry = ilst->item_list->head; entry; entry = entry->next )
+        ilst->size += isom_update_metaitem_size( (isom_metaitem_t *)entry->data );
+    CHECK_LARGESIZE( ilst->size );
+    return ilst->size;
+}
+
+static uint64_t isom_update_meta_size( isom_meta_t *meta )
+{
+    if( !meta )
+        return 0;
+    meta->size = ISOM_FULLBOX_COMMON_SIZE
+        + isom_update_hdlr_size( meta->hdlr )
+        + isom_update_dinf_size( meta->dinf )
+        + isom_update_ilst_size( meta->ilst );
+    CHECK_LARGESIZE( meta->size );
+    return meta->size;
+}
+
+static uint64_t isom_update_cprt_size( isom_cprt_t *cprt )
+{
+    if( !cprt )
+        return 0;
+    cprt->size = ISOM_FULLBOX_COMMON_SIZE + 2 + cprt->notice_length;
+    CHECK_LARGESIZE( cprt->size );
+    return cprt->size;
+}
+
+static uint64_t isom_update_udta_size( isom_udta_t *udta_moov, isom_udta_t *udta_trak )
+{
+    isom_udta_t *udta = udta_trak ? udta_trak : udta_moov ? udta_moov : NULL;
+    if( !udta )
+        return 0;
+    udta->size = ISOM_BASEBOX_COMMON_SIZE
+        + (udta_moov ? isom_update_chpl_size( udta->chpl ) : 0)
+        + isom_update_meta_size( udta->meta );
+    if( udta->cprt_list )
+        for( lsmash_entry_t *entry = udta->cprt_list->head; entry; entry = entry->next )
+            udta->size += isom_update_cprt_size( (isom_cprt_t *)entry->data );
+    CHECK_LARGESIZE( udta->size );
+    return udta->size;
+}
+
+static uint64_t isom_update_trak_entry_size( isom_trak_entry_t *trak )
+{
+    if( !trak )
+        return 0;
+    trak->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_tkhd_size( trak->tkhd )
+        + isom_update_tapt_size( trak->tapt )
+        + isom_update_edts_size( trak->edts )
+        + isom_update_tref_size( trak->tref )
+        + isom_update_mdia_size( trak->mdia )
+        + isom_update_udta_size( NULL, trak->udta )
+        + isom_update_meta_size( trak->meta );
+    CHECK_LARGESIZE( trak->size );
+    return trak->size;
+}
+
+static uint64_t isom_update_mehd_size( isom_mehd_t *mehd )
+{
+    if( !mehd )
+        return 0;
+    if( mehd->fragment_duration > UINT32_MAX )
+        mehd->version = 1;
+    mehd->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (mehd->version == 1));
+    CHECK_LARGESIZE( mehd->size );
+    return mehd->size;
+}
+
+static uint64_t isom_update_trex_entry_size( isom_trex_entry_t *trex )
+{
+    if( !trex )
+        return 0;
+    trex->size = ISOM_FULLBOX_COMMON_SIZE + 20;
+    CHECK_LARGESIZE( trex->size );
+    return trex->size;
+}
+
+static uint64_t isom_update_mvex_size( isom_mvex_t *mvex )
+{
+    if( !mvex )
+        return 0;
+    mvex->size = ISOM_BASEBOX_COMMON_SIZE;
+    if( mvex->trex_list )
+        for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
+        {
+            isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
+            mvex->size += isom_update_trex_entry_size( trex );
+        }
+    if( mvex->root->bs->stream != stdout )
+        mvex->size += mvex->mehd ? isom_update_mehd_size( mvex->mehd ) : 20;    /* 20 bytes is of placeholder. */
+    CHECK_LARGESIZE( mvex->size );
+    return mvex->size;
+}
+
+static int isom_update_moov_size( isom_moov_t *moov )
+{
+    if( !moov )
+        return -1;
+    moov->size = ISOM_BASEBOX_COMMON_SIZE
+        + isom_update_mvhd_size( moov->mvhd )
+        + isom_update_iods_size( moov->iods )
+        + isom_update_udta_size( moov->udta, NULL )
+        + isom_update_meta_size( moov->meta )
+        + isom_update_mvex_size( moov->mvex );
+    if( moov->trak_list )
+        for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+        {
+            isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+            moov->size += isom_update_trak_entry_size( trak );
+        }
+    CHECK_LARGESIZE( moov->size );
+    return 0;
+}
+
+static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd )
+{
+    if( !mfhd )
+        return 0;
+    mfhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
+    CHECK_LARGESIZE( mfhd->size );
+    return mfhd->size;
+}
+
+static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd )
+{
+    if( !tfhd )
+        return 0;
+    tfhd->size = ISOM_FULLBOX_COMMON_SIZE
+               + 4
+               + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT         )
+               + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
+               + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT  )
+               + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT      )
+               + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT     );
+    CHECK_LARGESIZE( tfhd->size );
+    return tfhd->size;
+}
+
+static uint64_t isom_update_trun_entry_size( isom_trun_entry_t *trun )
+{
+    if( !trun )
+        return 0;
+    trun->size = ISOM_FULLBOX_COMMON_SIZE
+               + 4
+               + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT        )
+               + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT );
+    uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT                )
+                      + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT                    )
+                      + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT                   )
+                      + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT );
+    trun->size += row_size * trun->sample_count;
+    CHECK_LARGESIZE( trun->size );
+    return trun->size;
+}
+
+static uint64_t isom_update_traf_entry_size( isom_traf_entry_t *traf )
+{
+    if( !traf )
+        return 0;
+    traf->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_tfhd_size( traf->tfhd );
+    if( traf->trun_list )
+        for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
+        {
+            isom_trun_entry_t *trun = (isom_trun_entry_t *)entry->data;
+            traf->size += isom_update_trun_entry_size( trun );
+        }
+    CHECK_LARGESIZE( traf->size );
+    return traf->size;
+}
+
+static int isom_update_moof_entry_size( isom_moof_entry_t *moof )
+{
+    if( !moof )
+        return -1;
+    moof->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_mfhd_size( moof->mfhd );
+    if( moof->traf_list )
+        for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+        {
+            isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
+            moof->size += isom_update_traf_entry_size( traf );
+        }
+    CHECK_LARGESIZE( moof->size );
+    return 0;
+}
+
+static uint64_t isom_update_tfra_entry_size( isom_tfra_entry_t *tfra )
+{
+    if( !tfra )
+        return 0;
+    tfra->size = ISOM_FULLBOX_COMMON_SIZE + 12;
+    uint32_t entry_size = 8 * (1 + (tfra->version == 1))
+                        + tfra->length_size_of_traf_num   + 1
+                        + tfra->length_size_of_trun_num   + 1
+                        + tfra->length_size_of_sample_num + 1;
+    tfra->size += entry_size * tfra->number_of_entry;
+    CHECK_LARGESIZE( tfra->size );
+    return tfra->size;
+}
+
+static uint64_t isom_update_mfro_size( isom_mfro_t *mfro )
+{
+    if( !mfro )
+        return 0;
+    mfro->size = ISOM_FULLBOX_COMMON_SIZE + 4;
+    CHECK_LARGESIZE( mfro->size );
+    return mfro->size;
+}
+
+static int isom_update_mfra_size( isom_mfra_t *mfra )
+{
+    if( !mfra )
+        return -1;
+    mfra->size = ISOM_BASEBOX_COMMON_SIZE;
+    if( mfra->tfra_list )
+        for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
+        {
+            isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
+            mfra->size += isom_update_tfra_entry_size( tfra );
+        }
+    CHECK_LARGESIZE( mfra->size );
+    if( mfra->mfro )
+    {
+        mfra->size += isom_update_mfro_size( mfra->mfro );
+        mfra->mfro->length = mfra->size;
+    }
+    return 0;
+}
+
+/*******************************
+    public interfaces
+*******************************/
+
+/*---- track manipulators ----*/
+
+void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
+{
+    if( !root || !root->moov || !root->moov->trak_list )
+        return;
+    for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak || !trak->tkhd )
+            return;
+        if( trak->tkhd->track_ID == track_ID )
+        {
+            lsmash_entry_t *next = entry->next;
+            lsmash_entry_t *prev = entry->prev;
+            isom_remove_trak( trak );
+            free( entry );
+            entry = next;
+            if( entry )
+            {
+                if( prev )
+                    prev->next = entry;
+                entry->prev = prev;
+            }
+            return;
+        }
+    }
+}
+
+uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
+{
+    isom_trak_entry_t *trak = isom_add_trak( root );
+    if( !trak )
+        return 0;
+    if( isom_add_tkhd( trak, media_type )
+     || isom_add_mdia( trak )
+     || isom_add_mdhd( trak->mdia, root->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED )
+     || isom_add_minf( trak->mdia )
+     || isom_add_stbl( trak->mdia->minf )
+     || isom_add_dinf( trak->mdia->minf )
+     || isom_add_dref( trak->mdia->minf->dinf )
+     || isom_add_stsd( trak->mdia->minf->stbl )
+     || isom_add_stts( trak->mdia->minf->stbl )
+     || isom_add_stsc( trak->mdia->minf->stbl )
+     || isom_add_stco( trak->mdia->minf->stbl )
+     || isom_add_stsz( trak->mdia->minf->stbl ) )
+        return 0;
+    if( isom_add_hdlr( trak->mdia, NULL, NULL, media_type ) )
+        return 0;
+    if( root->qt_compatible && isom_add_hdlr( NULL, NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL ) )
+        return 0;
+    switch( media_type )
+    {
+        case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
+            if( isom_add_vmhd( trak->mdia->minf ) )
+                return 0;
+            break;
+        case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
+            if( isom_add_smhd( trak->mdia->minf ) )
+                return 0;
+            break;
+        case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
+            if( isom_add_hmhd( trak->mdia->minf ) )
+                return 0;
+            break;
+        case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
+            if( root->qt_compatible || root->itunes_movie )
+            {
+                if( isom_add_gmhd( trak->mdia->minf )
+                 || isom_add_gmin( trak->mdia->minf->gmhd )
+                 || isom_add_text( trak->mdia->minf->gmhd ) )
+                    return 0;
+            }
+            else
+                return 0;   /* We support only reference text media track for chapter yet. */
+            break;
+        default :
+            if( isom_add_nmhd( trak->mdia->minf ) )
+                return 0;
+            break;
+    }
+    return trak->tkhd->track_ID;
+}
+
+uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
+{
+    if( !root || !root->moov )
+        return 0;
+    isom_trak_entry_t *trak = (isom_trak_entry_t *)lsmash_get_entry_data( root->moov->trak_list, track_number );
+    if( !trak || !trak->tkhd )
+        return 0;
+    return trak->tkhd->track_ID;
+}
+
+void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
+{
+    memset( param, 0, sizeof(lsmash_track_parameters_t) );
+    param->audio_volume = 0x0100;
+    param->matrix[0] = 0x00010000;
+    param->matrix[4] = 0x00010000;
+    param->matrix[8] = 0x40000000;
+}
+
+int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->hdlr || !root->moov->mvhd )
+        return -1;
+    /* Prepare Track Aperture Modes if required. */
+    if( root->qt_compatible && param->aperture_modes )
+    {
+        if( !trak->tapt && isom_add_tapt( trak ) )
+            return -1;
+        isom_tapt_t *tapt = trak->tapt;
+        if( (!tapt->clef && isom_add_clef( tapt ))
+         || (!tapt->prof && isom_add_prof( tapt ))
+         || (!tapt->enof && isom_add_enof( tapt )) )
+            return -1;
+    }
+    else
+        isom_remove_tapt( trak->tapt );
+    /* Set up Track Header. */
+    uint32_t media_type = trak->mdia->hdlr->componentSubtype;
+    isom_tkhd_t *tkhd = trak->tkhd;
+    tkhd->flags    = param->mode;
+    tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
+    tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
+    /* Template fields
+     * According to 14496-14, these value are all set to defaut values in 14496-12.
+     * And when a file is read as an MPEG-4 file, these values shall be ignored.
+     * If a file complies with other specifications, then those fields may have non-default values
+     * as required by those other specifications. */
+    tkhd->alternate_group = root->qt_compatible || root->itunes_movie || root->max_3gpp_version >= 4 ? param->alternate_group : 0;
+    if( root->qt_compatible || root->itunes_movie )
+    {
+        tkhd->layer  = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer  : 0;
+        tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
+        if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
+            for( int i = 0; i < 9; i++ )
+                tkhd->matrix[i] = param->matrix[i];
+        else
+            for( int i = 0; i < 9; i++ )
+                tkhd->matrix[i] = 0;
+    }
+    else
+    {
+        tkhd->layer     = 0;
+        tkhd->volume    = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
+        tkhd->matrix[0] = 0x00010000;
+        tkhd->matrix[1] = tkhd->matrix[2] = tkhd->matrix[3] = 0;
+        tkhd->matrix[4] = 0x00010000;
+        tkhd->matrix[5] = tkhd->matrix[6] = tkhd->matrix[7] = 0;
+        tkhd->matrix[8] = 0x40000000;
+    }
+    /* visual presentation size */
+    tkhd->width  = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width  : 0;
+    tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
+    /* Update next_track_ID if needed. */
+    if( root->moov->mvhd->next_track_ID <= tkhd->track_ID )
+        root->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
+    return 0;
+}
+
+int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak )
+        return -1;
+    isom_tkhd_t *tkhd = trak->tkhd;
+    param->mode            = tkhd->flags;
+    param->track_ID        = tkhd->track_ID;
+    param->duration        = tkhd->duration;
+    param->video_layer     = tkhd->layer;
+    param->alternate_group = tkhd->alternate_group;
+    param->audio_volume    = tkhd->volume;
+    for( int i = 0; i < 9; i++ )
+        param->matrix[i]   = tkhd->matrix[i];
+    param->display_width   = tkhd->width;
+    param->display_height  = tkhd->height;
+    param->aperture_modes  = !!trak->tapt;
+    return 0;
+}
+
+static int isom_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->hdlr )
+        return -1;
+    isom_hdlr_t *hdlr = trak->mdia->hdlr;
+    uint8_t *name = NULL;
+    uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
+    if( root->qt_compatible )
+        name_length = LSMASH_MIN( name_length, 255 );
+    if( name_length > hdlr->componentName_length && hdlr->componentName )
+        name = realloc( hdlr->componentName, name_length );
+    else if( !hdlr->componentName )
+        name = malloc( name_length );
+    else
+        name = hdlr->componentName;
+    if( !name )
+        return -1;
+    if( root->qt_compatible )
+        name[0] = name_length & 0xff;
+    memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
+    if( root->isom_compatible )
+        name[name_length - 1] = 0;
+    hdlr->componentName = name;
+    hdlr->componentName_length = name_length;
+    return 0;
+}
+
+static int isom_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->hdlr )
+        return -1;
+    isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
+    uint8_t *name = NULL;
+    uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
+    if( root->qt_compatible )
+        name_length = LSMASH_MIN( name_length, 255 );
+    if( name_length > hdlr->componentName_length && hdlr->componentName )
+        name = realloc( hdlr->componentName, name_length );
+    else if( !hdlr->componentName )
+        name = malloc( name_length );
+    else
+        name = hdlr->componentName;
+    if( !name )
+        return -1;
+    if( root->qt_compatible )
+        name[0] = name_length & 0xff;
+    memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
+    if( root->isom_compatible )
+        name[name_length - 1] = 0;
+    hdlr->componentName = name;
+    hdlr->componentName_length = name_length;
+    return 0;
+}
+
+uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd )
+        return 0;
+    return trak->mdia->mdhd->timescale;
+}
+
+uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd )
+        return 0;
+    return trak->mdia->mdhd->duration;
+}
+
+uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->tkhd )
+        return 0;
+    return trak->tkhd->duration;
+}
+
+uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list
+     || !trak->mdia->minf->stbl->stts->list->head || !trak->mdia->minf->stbl->stts->list->head->data )
+        return 0;
+    return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->head->data)->sample_delta;
+}
+
+uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->ctts || !trak->mdia->minf->stbl->ctts->list
+     || !trak->mdia->minf->stbl->ctts->list->head || !trak->mdia->minf->stbl->ctts->list->head->data )
+        return 0;
+    return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
+}
+
+uint16_t lsmash_pack_iso_language( char *iso_language )
+{
+    if( !iso_language || strlen( iso_language ) != 3 )
+        return 0;
+    return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
+}
+
+static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
+{
+    if( !MAC_language )
+        return -1;
+    int i = 0;
+    for( ; isom_languages[i].iso_name; i++ )
+        if( ISO_language == isom_languages[i].iso_name )
+            break;
+    if( !isom_languages[i].iso_name )
+        return -1;
+    *MAC_language = isom_languages[i].mac_value;
+    return 0;
+}
+
+static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
+{
+    if( !ISO_language )
+        return -1;
+    int i = 0;
+    for( ; isom_languages[i].iso_name; i++ )
+        if( MAC_language == isom_languages[i].mac_value )
+            break;
+    *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
+    return 0;
+}
+
+static int isom_set_media_language( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd )
+        return -1;
+    uint16_t language = 0;
+    if( root->isom_compatible )
+    {
+        if( ISO_language )
+            language = ISO_language;
+        else if( MAC_language )
+        {
+            if( isom_mac2iso_language( MAC_language, &language ) )
+                return -1;
+        }
+        else
+            language = ISOM_LANGUAGE_CODE_UNDEFINED;
+    }
+    else if( root->qt_compatible )
+    {
+        if( ISO_language )
+        {
+            if( isom_iso2mac_language( ISO_language, &language ) )
+                return -1;
+        }
+        else
+            language = MAC_language;
+    }
+    else
+        return -1;
+    trak->mdia->mdhd->language = language;
+    return 0;
+}
+
+static int isom_create_grouping( isom_trak_entry_t *trak, isom_grouping_type grouping_type )
+{
+    lsmash_root_t *root = trak->root;
+    switch( grouping_type )
+    {
+        case ISOM_GROUP_TYPE_RAP :
+            assert( root->max_isom_version >= 6 );
+            break;
+        case ISOM_GROUP_TYPE_ROLL :
+            assert( root->avc_extensions || root->qt_compatible );
+            break;
+        default :
+            assert( 0 );
+            break;
+    }
+    if( !isom_add_sgpd( trak->mdia->minf->stbl, grouping_type )
+     || !isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) )
+        return -1;
+    return 0;
+}
+
+void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
+{
+    memset( param, 0, sizeof(lsmash_media_parameters_t) );
+    param->timescale = 1;
+}
+
+int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
+        return -1;
+    trak->mdia->mdhd->timescale = param->timescale;
+    if( isom_set_media_language( root, track_ID, param->ISO_language, param->MAC_language ) )
+        return -1;
+    if( param->media_handler_name
+     && isom_set_media_handler_name( root, track_ID, param->media_handler_name ) )
+        return -1;
+    if( root->qt_compatible && param->data_handler_name
+     && isom_set_data_handler_name( root, track_ID, param->data_handler_name ) )
+        return -1;
+    if( (root->avc_extensions || root->qt_compatible) && param->roll_grouping
+     && isom_create_grouping( trak, ISOM_GROUP_TYPE_ROLL ) )
+        return -1;
+    if( (root->max_isom_version >= 6) && param->rap_grouping
+     && isom_create_grouping( trak, ISOM_GROUP_TYPE_RAP ) )
+        return -1;
+    return 0;
+}
+
+int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr
+     || !trak->mdia->minf || !trak->mdia->minf->stbl )
+        return -1;
+    isom_mdhd_t *mdhd = trak->mdia->mdhd;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_sbgp_entry_t *sbgp;
+    isom_sgpd_entry_t *sgpd;
+    param->timescale    = mdhd->timescale;
+    param->handler_type = trak->mdia->hdlr->componentSubtype;
+    param->duration     = mdhd->duration;
+    /* Whether sample grouping present. */
+    sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
+    sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
+    param->roll_grouping = sbgp && sgpd;
+    sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
+    sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
+    param->rap_grouping = sbgp && sgpd;
+    /* Get media language. */
+    if( mdhd->language >= 0x800 )
+    {
+        param->MAC_language = 0;
+        param->ISO_language = mdhd->language;
+    }
+    else
+    {
+        param->MAC_language = mdhd->language;
+        param->ISO_language = 0;
+    }
+    /* Get handler name(s). */
+    isom_hdlr_t *hdlr = trak->mdia->hdlr;
+    int length = LSMASH_MIN( 255, hdlr->componentName_length );
+    if( length )
+    {
+        memcpy( param->media_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
+        param->media_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
+        param->media_handler_name = param->media_handler_name_shadow;
+    }
+    else
+    {
+        param->media_handler_name = NULL;
+        memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
+    }
+    if( trak->mdia->minf->hdlr )
+    {
+        hdlr = trak->mdia->minf->hdlr;
+        length = LSMASH_MIN( 255, hdlr->componentName_length );
+        if( length )
+        {
+            memcpy( param->data_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
+            param->data_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
+            param->data_handler_name = param->data_handler_name_shadow;
+        }
+        else
+        {
+            param->data_handler_name = NULL;
+            memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
+        }
+    }
+    else
+    {
+        param->data_handler_name = NULL;
+        memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
+    }
+    return 0;
+}
+
+/*---- movie manipulators ----*/
+
+lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode )
+{
+    if( !filename )
+        return NULL;
+    char open_mode[4] = { 0 };
+    if( mode & LSMASH_FILE_MODE_WRITE )
+        memcpy( open_mode, "w+b", 4 );
+#ifdef LSMASH_DEMUXER_ENABLED
+    else if( mode & LSMASH_FILE_MODE_READ )
+        memcpy( open_mode, "rb", 3 );
+#endif
+    if( !open_mode[0] )
+        return NULL;
+    lsmash_root_t *root = lsmash_malloc_zero( sizeof(lsmash_root_t) );
+    if( !root )
+        return NULL;
+    root->root = root;
+    root->bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) );
+    if( !root->bs )
+        goto fail;
+    if( !strcmp( filename, "-" ) )
+    {
+        if( mode & LSMASH_FILE_MODE_READ )
+            root->bs->stream = stdin;
+        else if( (mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_FRAGMENTED) )
+            root->bs->stream = stdout;
+    }
+    else
+        root->bs->stream = fopen( filename, open_mode );
+    if( !root->bs->stream )
+        goto fail;
+    root->flags = mode;
+    if( mode & LSMASH_FILE_MODE_WRITE )
+    {
+        if( isom_add_moov( root ) || isom_add_mvhd( root->moov ) )
+            goto fail;
+        root->qt_compatible = 1;    /* QTFF is default file format. */
+    }
+#ifdef LSMASH_DEMUXER_ENABLED
+    if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) )
+    {
+        if( isom_read_root( root ) )
+            goto fail;
+        root->max_read_size = 4 * 1024 * 1024;
+    }
+#endif
+    if( mode & LSMASH_FILE_MODE_FRAGMENTED )
+    {
+        root->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) );
+        if( !root->fragment )
+            goto fail;
+        root->fragment->pool = lsmash_create_entry_list();
+        if( !root->fragment->pool )
+            goto fail;
+    }
+    return root;
+fail:
+    lsmash_destroy_root( root );
+    return NULL;
+}
+
+static int isom_finish_fragment_movie( lsmash_root_t *root );
+
+/* A movie fragment cannot switch a sample description to another.
+ * So you must call this function before switching sample descriptions. */
+int lsmash_create_fragment_movie( lsmash_root_t *root )
+{
+    if( !root || !root->bs || !root->fragment || !root->moov || !root->moov->trak_list )
+        return -1;
+    /* Finish the previous movie fragment before starting a new one. */
+    if( isom_finish_fragment_movie( root ) )
+        return -1;
+    /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
+    if( root->fragment->movie && root->moof_list->entry_count != 1 )
+        return -1;
+    isom_moof_entry_t *moof = isom_add_moof( root );
+    if( isom_add_mfhd( moof ) )
+        return -1;
+    root->fragment->movie = moof;
+    moof->mfhd->sequence_number = ++ root->fragment->fragment_count;
+    if( root->moof_list->entry_count == 1 )
+        return 0;
+    /* Remove the previous movie fragment. */
+    return lsmash_remove_entry( root->moof_list, 1, isom_remove_moof );
+}
+
+static int isom_set_brands( lsmash_root_t *root, lsmash_brand_type major_brand, uint32_t minor_version, lsmash_brand_type *brands, uint32_t brand_count )
+{
+    if( brand_count > 50 )
+        return -1;      /* We support setting brands up to 50. */
+    if( !brand_count )
+    {
+        /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
+        if( root->ftyp )
+        {
+            if( root->ftyp->compatible_brands )
+                free( root->ftyp->compatible_brands );
+            free( root->ftyp );
+            root->ftyp = NULL;
+        }
+        return 0;
+    }
+    if( !root->ftyp && isom_add_ftyp( root ) )
+        return -1;
+    isom_ftyp_t *ftyp = root->ftyp;
+    ftyp->major_brand = major_brand;
+    ftyp->minor_version = minor_version;
+    lsmash_brand_type *compatible_brands;
+    if( !ftyp->compatible_brands )
+        compatible_brands = malloc( brand_count * sizeof(uint32_t) );
+    else
+        compatible_brands = realloc( ftyp->compatible_brands, brand_count * sizeof(uint32_t) );
+    if( !compatible_brands )
+        return -1;
+    ftyp->compatible_brands = compatible_brands;
+    for( uint32_t i = 0; i < brand_count; i++ )
+    {
+        ftyp->compatible_brands[i] = brands[i];
+        ftyp->size += 4;
+    }
+    ftyp->brand_count = brand_count;
+    return isom_check_compatibility( root );
+}
+
+void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
+{
+    memset( param, 0, sizeof(lsmash_movie_parameters_t) );
+    param->max_chunk_duration  = 0.5;
+    param->max_async_tolerance = 2.0;
+    param->max_chunk_size      = 4 * 1024 * 1024;
+    param->max_read_size       = 4 * 1024 * 1024;
+    param->timescale           = 600;
+    param->playback_rate       = 0x00010000;
+    param->playback_volume     = 0x0100;
+}
+
+int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
+{
+    if( !root || !root->moov || !root->moov->mvhd
+     || isom_set_brands( root, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) )
+        return -1;
+    isom_mvhd_t *mvhd = root->moov->mvhd;
+    root->max_chunk_duration  = param->max_chunk_duration;
+    root->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
+    root->max_chunk_size      = param->max_chunk_size;
+    root->max_read_size       = param->max_read_size;
+    mvhd->timescale           = param->timescale;
+    if( root->qt_compatible || root->itunes_movie )
+    {
+        mvhd->rate            = param->playback_rate;
+        mvhd->volume          = param->playback_volume;
+        mvhd->previewTime     = param->preview_time;
+        mvhd->previewDuration = param->preview_duration;
+        mvhd->posterTime      = param->poster_time;
+    }
+    else
+    {
+        mvhd->rate            = 0x00010000;
+        mvhd->volume          = 0x0100;
+        mvhd->previewTime     = 0;
+        mvhd->previewDuration = 0;
+        mvhd->posterTime      = 0;
+    }
+    return 0;
+}
+
+int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
+{
+    if( !root || !root->moov || !root->moov->mvhd )
+        return -1;
+    isom_mvhd_t *mvhd = root->moov->mvhd;
+    if( root->ftyp )
+    {
+        isom_ftyp_t *ftyp = root->ftyp;
+        uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 );     /* brands up to 50 */
+        for( uint32_t i = 0; i < brand_count; i++ )
+            param->brands_shadow[i] = ftyp->compatible_brands[i];
+        param->major_brand      = ftyp->major_brand;
+        param->brands           = param->brands_shadow;
+        param->number_of_brands = brand_count;
+        param->minor_version    = ftyp->minor_version;
+    }
+    param->max_chunk_duration  = root->max_chunk_duration;
+    param->max_async_tolerance = root->max_async_tolerance;
+    param->max_chunk_size      = root->max_chunk_size;
+    param->max_read_size       = root->max_read_size;
+    param->timescale           = mvhd->timescale;
+    param->duration            = mvhd->duration;
+    param->playback_rate       = mvhd->rate;
+    param->playback_volume     = mvhd->volume;
+    param->preview_time        = mvhd->previewTime;
+    param->preview_duration    = mvhd->previewDuration;
+    param->poster_time         = mvhd->posterTime;
+    param->number_of_tracks    = root->moov->trak_list ? root->moov->trak_list->entry_count : 0;
+    return 0;
+}
+
+uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
+{
+    if( !root || !root->moov || !root->moov->mvhd )
+        return 0;
+    return root->moov->mvhd->timescale;
+}
+
+static int isom_write_ftyp( lsmash_root_t *root )
+{
+    isom_ftyp_t *ftyp = root->ftyp;
+    if( !ftyp || !ftyp->brand_count )
+        return 0;
+    lsmash_bs_t *bs = root->bs;
+    isom_bs_put_box_common( bs, ftyp );
+    lsmash_bs_put_be32( bs, ftyp->major_brand );
+    lsmash_bs_put_be32( bs, ftyp->minor_version );
+    for( uint32_t i = 0; i < ftyp->brand_count; i++ )
+        lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    root->size += ftyp->size;
+    root->file_type_written = 1;
+    return 0;
+}
+
+static int isom_write_moov( lsmash_root_t *root )
+{
+    if( !root || !root->moov )
+        return -1;
+    lsmash_bs_t *bs = root->bs;
+    isom_moov_t *moov = root->moov;
+    isom_bs_put_box_common( bs, moov );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( isom_write_mvhd( root )
+     || isom_write_iods( root ) )
+        return -1;
+    if( moov->trak_list )
+        for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+            if( isom_write_trak( bs, (isom_trak_entry_t *)entry->data ) )
+                return -1;
+    if( isom_write_udta( bs, moov, NULL )
+     || isom_write_meta( bs, moov->meta ) )
+        return -1;
+    return isom_write_mvex( bs, moov->mvex );
+}
+
+int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
+{
+    if( !root || !root->free || !data || !data_length )
+        return -1;
+    isom_free_t *skip = root->free;
+    uint8_t *tmp = NULL;
+    if( !skip->data )
+        tmp = malloc( data_length );
+    else if( skip->length < data_length )
+        tmp = realloc( skip->data, data_length );
+    if( !tmp )
+        return -1;
+    memcpy( tmp, data, data_length );
+    skip->data = tmp;
+    skip->length = data_length;
+    return 0;
+}
+
+int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
+{
+    if( !root )
+        return -1;
+    if( !root->free )
+    {
+        isom_create_box( skip, root, ISOM_BOX_TYPE_FREE );
+        root->free = skip;
+    }
+    if( data && data_length )
+        return lsmash_set_free( root, data, data_length );
+    return 0;
+}
+
+int lsmash_write_free( lsmash_root_t *root )
+{
+    if( !root || !root->bs || !root->free )
+        return -1;
+    isom_free_t *skip = root->free;
+    lsmash_bs_t *bs = root->bs;
+    skip->size = 8 + skip->length;
+    isom_bs_put_box_common( bs, skip );
+    if( skip->data && skip->length )
+        lsmash_bs_put_bytes( bs, skip->data, skip->length );
+    return lsmash_bs_write_data( bs );
+}
+
+int lsmash_create_object_descriptor( lsmash_root_t *root )
+{
+    if( !root )
+        return -1;
+    /* Return error if this file is not compatible with MP4 file format. */
+    if( !root->mp4_version1 && !root->mp4_version2 )
+        return -1;
+    return isom_add_iods( root->moov );
+}
+
+/*---- finishing functions ----*/
+
+static int isom_set_fragment_overall_duration( lsmash_root_t *root )
+{
+    if( root->bs->stream == stdout )
+        return 0;
+    isom_mvex_t *mvex = root->moov->mvex;
+    if( isom_add_mehd( mvex ) )
+        return -1;
+    /* Get the longest duration of the tracks. */
+    uint64_t longest_duration = 0;
+    for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak || !trak->cache || !trak->cache->fragment || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale )
+            return -1;
+        uint64_t duration;
+        if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
+        {
+            duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration;
+            duration = (uint64_t)(((double)duration / trak->mdia->mdhd->timescale) * root->moov->mvhd->timescale);
+        }
+        else
+        {
+            duration = 0;
+            for( lsmash_entry_t *elst_entry = trak->edts->elst->list->head; elst_entry; elst_entry = elst_entry->next )
+            {
+                isom_elst_entry_t *data = (isom_elst_entry_t *)elst_entry->data;
+                if( !data )
+                    return -1;
+                duration += data->segment_duration;
+            }
+        }
+        longest_duration = LSMASH_MAX( duration, longest_duration );
+    }
+    mvex->mehd->fragment_duration = longest_duration;
+    mvex->mehd->version = 1;
+    isom_update_mehd_size( mvex->mehd );
+    /* Write Movie Extends Header Box here. */
+    lsmash_bs_t *bs = root->bs;
+    FILE *stream = bs->stream;
+    uint64_t current_pos = lsmash_ftell( stream );
+    lsmash_fseek( stream, mvex->placeholder_pos, SEEK_SET );
+    int ret = isom_write_mehd( bs, mvex->mehd );
+    if( !ret )
+        ret = lsmash_bs_write_data( bs );
+    lsmash_fseek( stream, current_pos, SEEK_SET );
+    return ret;
+}
+
+static int isom_write_fragment_random_access_info( lsmash_root_t *root )
+{
+    if( root->bs->stream == stdout )
+        return 0;
+    if( isom_update_mfra_size( root->mfra ) )
+        return -1;
+    return isom_write_mfra( root->bs, root->mfra );
+}
+
+int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux )
+{
+    if( !root || !root->bs || !root->moov || !root->moov->trak_list )
+        return -1;
+    if( root->fragment )
+    {
+        /* Output the final movie fragment. */
+        if( isom_finish_fragment_movie( root ) )
+            return -1;
+        /* Write the overall random access information at the tail of the movie. */
+        if( isom_write_fragment_random_access_info( root ) )
+            return -1;
+        /* Set overall duration of the movie. */
+        return isom_set_fragment_overall_duration( root );
+    }
+    isom_moov_t *moov = root->moov;
+    for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
+            return -1;
+        uint32_t track_ID = trak->tkhd->track_ID;
+        uint32_t related_track_ID = trak->related_track_ID;
+        /* Disable the track if the track is a track reference chapter. */
+        if( trak->is_chapter )
+            trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
+        if( trak->is_chapter && related_track_ID )
+        {
+            /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
+            uint64_t track_duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
+            if( lsmash_create_explicit_timeline_map( root, track_ID, track_duration, 0, ISOM_EDIT_MODE_NORMAL ) )
+                return -1;
+        }
+        /* Add stss box if any samples aren't sync sample. */
+        isom_stbl_t *stbl = trak->mdia->minf->stbl;
+        if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
+            return -1;
+        if( isom_update_bitrate_info( trak->mdia ) )
+            return -1;
+    }
+    if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
+        return -1;
+    if( isom_check_mandatory_boxes( root )
+     || isom_set_movie_creation_time( root )
+     || isom_update_moov_size( moov )
+     || isom_write_mdat_size( root ) )
+        return -1;
+
+    lsmash_bs_t *bs = root->bs;
+    uint64_t meta_size = root->meta ? root->meta->size : 0;
+    if( !remux )
+    {
+        if( isom_write_moov( root )
+         || isom_write_meta( bs, root->meta ) )
+            return -1;
+        root->size += moov->size + meta_size;
+        return 0;
+    }
+
+    /* stco->co64 conversion, depending on last chunk's offset */
+    for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
+    {
+        isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
+        isom_stco_t* stco = trak->mdia->minf->stbl->stco;
+        if( !stco->list->tail )
+            return -1;
+        if( stco->large_presentation
+         || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
+        {
+            entry = entry->next;
+            continue;   /* no need to convert stco into co64 */
+        }
+        /* stco->co64 conversion */
+        if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
+            || isom_update_moov_size( moov ) )
+            return -1;
+        entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
+    }
+
+    /* now the amount of offset is fixed. */
+    uint64_t mtf_size = moov->size + meta_size;     /* sum of size of boxes moved to front */
+
+    /* buffer size must be at least mtf_size * 2 */
+    remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
+
+    uint8_t* buf[2];
+    if( (buf[0] = (uint8_t*)malloc( remux->buffer_size )) == NULL )
+        return -1; /* NOTE: i think we still can fallback to "return isom_write_moov( root );" here. */
+    uint64_t size = remux->buffer_size / 2;
+    buf[1] = buf[0] + size; /* split to 2 buffers */
+
+    /* now the amount of offset is fixed. apply that to stco/co64 */
+    for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
+        if( stco->large_presentation )
+            for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
+                ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += mtf_size;
+        else
+            for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
+                ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += mtf_size;
+    }
+
+    FILE *stream = bs->stream;
+    isom_mdat_t *mdat = root->mdat;
+    uint64_t total = root->size + mtf_size;
+    uint64_t readnum;
+    /* backup starting area of mdat and write moov + meta there instead */
+    if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) )
+        goto fail;
+    readnum = fread( buf[0], 1, size, stream );
+    uint64_t read_pos = lsmash_ftell( stream );
+
+    /* write moov + meta there instead */
+    if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET )
+     || isom_write_moov( root )
+     || isom_write_meta( bs, root->meta ) )
+        goto fail;
+    uint64_t write_pos = lsmash_ftell( stream );
+
+    mdat->placeholder_pos += mtf_size; /* update placeholder */
+
+    /* copy-pastan */
+    int buf_switch = 1;
+    while( readnum == size )
+    {
+        if( lsmash_fseek( stream, read_pos, SEEK_SET ) )
+            goto fail;
+        readnum = fread( buf[buf_switch], 1, size, stream );
+        read_pos = lsmash_ftell( stream );
+
+        buf_switch ^= 0x1;
+
+        if( lsmash_fseek( stream, write_pos, SEEK_SET )
+         || fwrite( buf[buf_switch], 1, size, stream ) != size )
+            goto fail;
+        write_pos = lsmash_ftell( stream );
+        if( remux->func ) remux->func( remux->param, write_pos, total ); // FIXME:
+    }
+    if( fwrite( buf[buf_switch^0x1], 1, readnum, stream ) != readnum )
+        goto fail;
+    if( remux->func ) remux->func( remux->param, total, total ); // FIXME:
+
+    root->size += mtf_size;
+    free( buf[0] );
+    return 0;
+
+fail:
+    free( buf[0] );
+    return -1;
+}
+
+#define GET_MOST_USED( box_name, index, flag_name ) \
+    if( most_used[index] < stats.flag_name[i] ) \
+    { \
+        most_used[index] = stats.flag_name[i]; \
+        box_name->default_sample_flags.flag_name = i; \
+    }
+
+static int isom_create_fragment_overall_default_settings( lsmash_root_t *root )
+{
+    if( isom_add_mvex( root->moov ) )
+        return -1;
+    for( lsmash_entry_t *trak_entry = root->moov->trak_list->head; trak_entry; trak_entry = trak_entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)trak_entry->data;
+        if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
+            return -1;
+        isom_stbl_t *stbl = trak->mdia->minf->stbl;
+        if( !stbl->stts || !stbl->stts->list || !stbl->stsz
+         || (stbl->stts->list->tail && !stbl->stts->list->tail->data)
+         || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) )
+            return -1;
+        isom_trex_entry_t *trex = isom_add_trex( root->moov->mvex );
+        if( !trex )
+            return -1;
+        trex->track_ID = trak->tkhd->track_ID;
+        /* Set up defaults. */
+        trex->default_sample_description_index = trak->cache->chunk.sample_description_index ? trak->cache->chunk.sample_description_index : 1;
+        trex->default_sample_duration = stbl->stts->list->tail ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta : 1;
+        trex->default_sample_size = !stbl->stsz->list
+                                  ? stbl->stsz->sample_size : stbl->stsz->list->head
+                                  ? ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size : 0;
+        if( stbl->sdtp && stbl->sdtp->list )
+        {
+            struct sample_flags_stats_t
+            {
+                uint32_t is_leading           [4];
+                uint32_t sample_depends_on    [4];
+                uint32_t sample_is_depended_on[4];
+                uint32_t sample_has_redundancy[4];
+            } stats = { { 0 }, { 0 }, { 0 }, { 0 } };
+            for( lsmash_entry_t *sdtp_entry = stbl->sdtp->list->head; sdtp_entry; sdtp_entry = sdtp_entry->next )
+            {
+                isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)sdtp_entry->data;
+                if( !data )
+                    return -1;
+                ++ stats.is_leading           [ data->is_leading            ];
+                ++ stats.sample_depends_on    [ data->sample_depends_on     ];
+                ++ stats.sample_is_depended_on[ data->sample_is_depended_on ];
+                ++ stats.sample_has_redundancy[ data->sample_has_redundancy ];
+            }
+            uint32_t most_used[4] = { 0, 0, 0, 0 };
+            for( int i = 0; i < 4; i++ )
+            {
+                GET_MOST_USED( trex, 0, is_leading            );
+                GET_MOST_USED( trex, 1, sample_depends_on     );
+                GET_MOST_USED( trex, 2, sample_is_depended_on );
+                GET_MOST_USED( trex, 3, sample_has_redundancy );
+            }
+        }
+        trex->default_sample_flags.sample_is_non_sync_sample = !trak->cache->all_sync;
+    }
+    return 0;
+}
+
+static int isom_prepare_random_access_info( lsmash_root_t *root )
+{
+    if( root->bs->stream == stdout )
+        return 0;
+    if( isom_add_mfra( root )
+     || isom_add_mfro( root->mfra ) )
+        return -1;
+    return 0;
+}
+
+static int isom_output_fragment_media_data( lsmash_root_t *root )
+{
+    isom_fragment_manager_t *fragment = root->fragment;
+    if( !fragment->pool->entry_count )
+    {
+        /* no need to write media data */
+        lsmash_remove_entries( fragment->pool, lsmash_delete_sample );
+        fragment->pool_size = 0;
+        return 0;
+    }
+    /* If there is no available Media Data Box to write samples, add and write a new one. */
+    if( isom_new_mdat( root, fragment->pool_size ) )
+        return -1;
+    /* Write samples in the current movie fragment. */
+    for( lsmash_entry_t* entry = fragment->pool->head; entry; entry = entry->next )
+    {
+        isom_sample_pool_t *pool = (isom_sample_pool_t *)entry->data;
+        if( !pool )
+            return -1;
+        lsmash_bs_put_bytes( root->bs, pool->data, pool->size );
+    }
+    if( lsmash_bs_write_data( root->bs ) )
+        return -1;
+    root->size += root->mdat->size;
+    lsmash_remove_entries( fragment->pool, isom_remove_sample_pool );
+    fragment->pool_size = 0;
+    return 0;
+}
+
+static int isom_finish_fragment_initial_movie( lsmash_root_t *root )
+{
+    if( !root->moov || !root->moov->trak_list )
+        return -1;
+    isom_moov_t *moov = root->moov;
+    for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
+        if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
+            return -1;
+        if( isom_get_sample_count( trak ) )
+        {
+            /* Add stss box if any samples aren't sync sample. */
+            isom_stbl_t *stbl = trak->mdia->minf->stbl;
+            if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
+                return -1;
+        }
+        else
+            trak->tkhd->duration = 0;
+        if( isom_update_bitrate_info( trak->mdia ) )
+            return -1;
+    }
+    if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
+        return -1;
+    if( isom_create_fragment_overall_default_settings( root )
+     || isom_prepare_random_access_info( root )
+     || isom_check_mandatory_boxes( root )
+     || isom_set_movie_creation_time( root )
+     || isom_update_moov_size( moov ) )
+        return -1;
+    /* stco->co64 conversion, depending on last chunk's offset */
+    uint64_t meta_size = root->meta ? root->meta->size : 0;
+    for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
+    {
+        isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
+        isom_stco_t* stco = trak->mdia->minf->stbl->stco;
+        if( !stco->list->tail   /* no samples */
+         || stco->large_presentation
+         || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
+        {
+            entry = entry->next;
+            continue;   /* no need to convert stco into co64 */
+        }
+        /* stco->co64 conversion */
+        if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
+            || isom_update_moov_size( moov ) )
+            return -1;
+        entry = moov->trak_list->head;  /* whenever any conversion, re-check all traks */
+    }
+    /* Now, the amount of offset is fixed. Apply that to stco/co64. */
+    uint64_t preceding_size = moov->size + meta_size;
+    for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
+        if( stco->large_presentation )
+            for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
+                ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += preceding_size;
+        else
+            for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
+                ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += preceding_size;
+    }
+    /* Write File Type Box here if it was not written yet. */
+    if( !root->file_type_written && isom_write_ftyp( root ) )
+        return -1;
+    /* Write Movie Box. */
+    if( isom_write_moov( root )
+     || isom_write_meta( root->bs, root->meta ) )
+        return -1;
+    root->size += preceding_size;
+    /* Output samples. */
+    return isom_output_fragment_media_data( root );
+}
+
+static int isom_finish_fragment_movie( lsmash_root_t *root )
+{
+    if( !root->moov || !root->moov->trak_list || !root->fragment || !root->fragment->pool )
+        return -1;
+    isom_moof_entry_t *moof = root->fragment->movie;
+    if( !moof )
+        return isom_finish_fragment_initial_movie( root );
+    /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
+     * And check whether that default_sample_flags is useful or not. */
+    for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+    {
+        isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
+        if( !traf || !traf->tfhd || !traf->root || !traf->root->moov || !traf->root->moov->mvex )
+            return -1;
+        isom_tfhd_t *tfhd = traf->tfhd;
+        isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, tfhd->track_ID );
+        if( !trex )
+            return -1;
+        struct sample_flags_stats_t
+        {
+            uint32_t is_leading               [4];
+            uint32_t sample_depends_on        [4];
+            uint32_t sample_is_depended_on    [4];
+            uint32_t sample_has_redundancy    [4];
+            uint32_t sample_is_non_sync_sample[2];
+        } stats = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
+        for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
+        {
+            isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
+            if( !trun || !trun->sample_count )
+                return -1;
+            isom_sample_flags_t *sample_flags;
+            if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
+            {
+                if( !trun->optional )
+                    return -1;
+                for( lsmash_entry_t *optional_entry = trun->optional->head; optional_entry; optional_entry = optional_entry->next )
+                {
+                    isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
+                    if( !row )
+                        return -1;
+                    sample_flags = &row->sample_flags;
+                    ++ stats.is_leading               [ sample_flags->is_leading                ];
+                    ++ stats.sample_depends_on        [ sample_flags->sample_depends_on         ];
+                    ++ stats.sample_is_depended_on    [ sample_flags->sample_is_depended_on     ];
+                    ++ stats.sample_has_redundancy    [ sample_flags->sample_has_redundancy     ];
+                    ++ stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ];
+                }
+            }
+            else
+            {
+                sample_flags = &tfhd->default_sample_flags;
+                stats.is_leading               [ sample_flags->is_leading                ] += trun->sample_count;
+                stats.sample_depends_on        [ sample_flags->sample_depends_on         ] += trun->sample_count;
+                stats.sample_is_depended_on    [ sample_flags->sample_is_depended_on     ] += trun->sample_count;
+                stats.sample_has_redundancy    [ sample_flags->sample_has_redundancy     ] += trun->sample_count;
+                stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ] += trun->sample_count;
+            }
+        }
+        uint32_t most_used[5] = { 0, 0, 0, 0, 0 };
+        for( int i = 0; i < 4; i++ )
+        {
+            GET_MOST_USED( tfhd, 0, is_leading            );
+            GET_MOST_USED( tfhd, 1, sample_depends_on     );
+            GET_MOST_USED( tfhd, 2, sample_is_depended_on );
+            GET_MOST_USED( tfhd, 3, sample_has_redundancy );
+            if( i < 2 )
+                GET_MOST_USED( tfhd, 4, sample_is_non_sync_sample );
+        }
+        int useful_default_sample_duration = 0;
+        int useful_default_sample_size = 0;
+        for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
+        {
+            isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
+            if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
+                useful_default_sample_duration = 1;
+            if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
+                useful_default_sample_size = 1;
+            int useful_first_sample_flags = 1;
+            int useful_default_sample_flags = 1;
+            if( trun->sample_count == 1 )
+            {
+                /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
+                 * If it is equal, just use default_sample_flags.
+                 * If not, just use first_sample_flags of this run. */
+                if( !memcmp( &trun->first_sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
+                    useful_first_sample_flags = 0;
+            }
+            else if( trun->optional && trun->optional->head )
+            {
+                lsmash_entry_t *optional_entry = trun->optional->head->next;
+                isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
+                isom_sample_flags_t representative_sample_flags = row->sample_flags;
+                if( memcmp( &tfhd->default_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
+                    useful_default_sample_flags = 0;
+                if( !memcmp( &trun->first_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
+                    useful_first_sample_flags = 0;
+                if( useful_default_sample_flags )
+                    for( optional_entry = optional_entry->next; optional_entry; optional_entry = optional_entry->next )
+                    {
+                        row = (isom_trun_optional_row_t *)optional_entry->data;
+                        if( memcmp( &representative_sample_flags, &row->sample_flags, sizeof(isom_sample_flags_t) ) )
+                        {
+                            useful_default_sample_flags = 0;
+                            break;
+                        }
+                    }
+            }
+            if( useful_default_sample_flags )
+            {
+                tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
+                trun->flags &= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
+            }
+            else
+            {
+                useful_first_sample_flags = 0;
+                trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
+            }
+            if( useful_first_sample_flags )
+                trun->flags |= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT;
+        }
+        if( useful_default_sample_duration && tfhd->default_sample_duration != trex->default_sample_duration )
+            tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
+        else
+            tfhd->default_sample_duration = trex->default_sample_duration;      /* This might be redundant, but is to be more natural. */
+        if( useful_default_sample_size && tfhd->default_sample_size != trex->default_sample_size )
+            tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT;
+        else
+            tfhd->default_sample_size = trex->default_sample_size;              /* This might be redundant, but is to be more natural. */
+        if( !(tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) )
+            tfhd->default_sample_flags = trex->default_sample_flags;            /* This might be redundant, but is to be more natural. */
+        else if( !memcmp( &tfhd->default_sample_flags, &trex->default_sample_flags, sizeof(isom_sample_flags_t) ) )
+            tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
+    }
+    /* When using for live streaming, setting explicit base_data_offset is not preferable.
+     * However, it's OK because we haven't supported this yet.
+     * Implicit base_data_offsets that originate in the first byte of each Movie Fragment Box will be implemented
+     * by the feature of ISO Base Media File Format version 5 or later. 
+     * Media Data Box starts immediately after Movie Fragment Box. */
+    for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+    {
+        isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
+        traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT;
+    }
+    /* Consider the update of tf_flags here. */
+    if( isom_update_moof_entry_size( moof ) )
+        return -1;
+    /* Now, we can calculate offsets in the current movie fragment, so do it. */
+    for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
+    {
+        isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
+        traf->tfhd->base_data_offset = root->size + moof->size + ISOM_BASEBOX_COMMON_SIZE;
+    }
+    if( isom_write_moof( root->bs, moof ) )
+        return -1;
+    root->size += moof->size;
+    /* Output samples. */
+    return isom_output_fragment_media_data( root );
+}
+
+#undef GET_MOST_USED
+
+static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_entry_t *trun, isom_tfhd_t *tfhd, uint32_t sample_number )
+{
+    isom_trun_optional_row_t *row = NULL;
+    if( !trun->optional )
+    {
+        trun->optional = lsmash_create_entry_list();
+        if( !trun->optional )
+            return NULL;
+    }
+    if( trun->optional->entry_count < sample_number )
+    {
+        while( trun->optional->entry_count < sample_number )
+        {
+            row = malloc( sizeof(isom_trun_optional_row_t) );
+            if( !row )
+                return NULL;
+            /* Copy from default. */
+            row->sample_duration                = tfhd->default_sample_duration;
+            row->sample_size                    = tfhd->default_sample_size;
+            row->sample_flags                   = tfhd->default_sample_flags;
+            row->sample_composition_time_offset = 0;
+            if( lsmash_add_entry( trun->optional, row ) )
+            {
+                free( row );
+                return NULL;
+            }
+        }
+        return row;
+    }
+    uint32_t i = 0;
+    for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
+    {
+        row = (isom_trun_optional_row_t *)entry->data;
+        if( !row )
+            return NULL;
+        if( ++i == sample_number )
+            return row;
+    }
+    return NULL;
+}
+
+int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration )
+{
+    if( !root || !root->fragment || !root->fragment->movie || !root->moov )
+        return -1;
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->tkhd )
+        return -1;
+    isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, track_ID );
+    if( !trex )
+        return -1;
+    isom_moof_entry_t *moof = root->fragment->movie;
+    isom_traf_entry_t *traf = isom_get_traf( moof, track_ID );
+    if( traf )
+        return -1;
+    traf = isom_add_traf( root, moof );
+    if( isom_add_tfhd( traf ) )
+        return -1;
+    isom_tfhd_t *tfhd = traf->tfhd;
+    tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY;          /* no samples for this track fragment yet */
+    tfhd->track_ID = trak->tkhd->track_ID;
+    tfhd->default_sample_duration = duration;
+    if( duration != trex->default_sample_duration )
+        tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
+    traf->cache = trak->cache;
+    traf->cache->fragment->traf_number = moof->traf_list->entry_count;
+    traf->cache->fragment->last_duration += duration;       /* The duration of the last sample includes this empty-duration. */
+    return 0;
+}
+
+static int isom_set_fragment_last_duration( isom_traf_entry_t *traf, uint32_t last_duration )
+{
+    isom_tfhd_t *tfhd = traf->tfhd;
+    if( !traf->trun_list || !traf->trun_list->tail || !traf->trun_list->tail->data )
+    {
+        /* There are no track runs in this track fragment, so it is a empty-duration. */
+        isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
+        if( !trex )
+            return -1;
+        tfhd->flags |= ISOM_TF_FLAGS_DURATION_IS_EMPTY;
+        if( last_duration != trex->default_sample_duration )
+            tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
+        tfhd->default_sample_duration = last_duration;
+        traf->cache->fragment->last_duration = last_duration;
+        return 0;
+    }
+    /* Update the last sample_duration if needed. */
+    isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
+    if( trun->sample_count == 1 && traf->trun_list->entry_count == 1 )
+    {
+        isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
+        if( !trex )
+            return -1;
+        if( last_duration != trex->default_sample_duration )
+            tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
+        tfhd->default_sample_duration = last_duration;
+    }
+    else if( last_duration != tfhd->default_sample_duration )
+        trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
+    if( trun->flags )
+    {
+        isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
+        if( !row )
+            return -1;
+        row->sample_duration = last_duration;
+    }
+    traf->cache->fragment->last_duration = last_duration;
+    return 0;
+}
+
+int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
+{
+    if( !root || !track_ID )
+        return -1;
+    if( root->fragment && root->fragment->movie )
+    {
+        isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
+        if( !traf || !traf->cache || !traf->tfhd || !traf->trun_list )
+            return -1;
+        return isom_set_fragment_last_duration( traf, sample_delta );
+    }
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->stsz || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
+        return -1;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_stts_t *stts = stbl->stts;
+    uint32_t sample_count = isom_get_sample_count( trak );
+    if( !stts->list->tail )
+    {
+        if( !sample_count )
+            return 0;       /* no samples */
+        if( sample_count > 1 )
+            return -1;      /* irregular sample_count */
+        if( isom_add_stts_entry( stbl, sample_delta ) )
+            return -1;
+        return lsmash_update_track_duration( root, track_ID, 0 );
+    }
+    uint32_t i = 0;
+    for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
+        i += ((isom_stts_entry_t *)entry->data)->sample_count;
+    if( sample_count < i )
+        return -1;
+    isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
+    if( !last_stts_data )
+        return -1;
+    if( sample_count > i )
+    {
+        if( sample_count - i > 1 )
+            return -1;
+        /* Add a sample_delta. */
+        if( sample_delta == last_stts_data->sample_delta )
+            ++ last_stts_data->sample_count;
+        else if( isom_add_stts_entry( stbl, sample_delta ) )
+            return -1;
+    }
+    else if( sample_count == i && isom_replace_last_sample_delta( stbl, sample_delta ) )
+        return -1;
+    return lsmash_update_track_duration( root, track_ID, sample_delta );
+}
+
+void lsmash_discard_boxes( lsmash_root_t *root )
+{
+    if( !root )
+        return;
+    isom_remove_ftyp( root->ftyp );
+    isom_remove_moov( root );
+    lsmash_remove_list( root->moof_list, isom_remove_moof );
+    isom_remove_mdat( root->mdat );
+    isom_remove_free( root->free );
+    isom_remove_meta( root->meta );
+    isom_remove_mfra( root->mfra );
+    root->ftyp = NULL;
+    root->moov = NULL;
+    root->moof_list = NULL;
+    root->mdat = NULL;
+    root->free = NULL;
+    root->mfra = NULL;
+}
+
+void lsmash_destroy_root( lsmash_root_t *root )
+{
+    if( !root )
+        return;
+#ifdef LSMASH_DEMUXER_ENABLED
+    isom_remove_print_funcs( root );
+    isom_remove_timelines( root );
+#endif
+    lsmash_discard_boxes( root );
+    if( root->bs )
+    {
+        if( root->bs->stream )
+            fclose( root->bs->stream );
+        if( root->bs->data )
+            free( root->bs->data );
+        free( root->bs );
+    }
+    if( root->fragment )
+    {
+        lsmash_remove_list( root->fragment->pool, lsmash_delete_sample );
+        free( root->fragment );
+    }
+    free( root );
+}
+
+/*---- timeline manipulator ----*/
+
+int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
+{
+    if( !segment_duration || media_time < -1 )
+        return -1;
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
+        return -1;
+    isom_elst_t *elst = trak->edts->elst;
+    isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, entry_number );
+    if( !data )
+        return -1;
+    data->segment_duration = segment_duration;
+    data->media_time = media_time;
+    data->media_rate = media_rate;
+    if( !elst->pos || !root->fragment || root->bs->stream == stdout )
+        return isom_update_tkhd_duration( trak );
+    /* Rewrite the specified entry.
+     * Note: we don't update the version of the Edit List Box. */
+    lsmash_bs_t *bs = root->bs;
+    FILE *stream = bs->stream;
+    uint64_t current_pos = lsmash_ftell( stream );
+    uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)entry_number - 1) * (elst->version == 1 ? 20 : 12);
+    lsmash_fseek( stream, entry_pos, SEEK_SET );
+    if( elst->version )
+    {
+        lsmash_bs_put_be64( bs, data->segment_duration );
+        lsmash_bs_put_be64( bs, data->media_time );
+    }
+    else
+    {
+        lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
+        lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
+    }
+    lsmash_bs_put_be32( bs, data->media_rate );
+    int ret = lsmash_bs_write_data( bs );
+    lsmash_fseek( stream, current_pos, SEEK_SET );
+    return ret;
+}
+
+int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
+{
+    if( media_time < -1 )
+        return -1;
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->tkhd )
+        return -1;
+    segment_duration = (segment_duration || root->fragment) ? segment_duration
+                     : trak->tkhd->duration ? trak->tkhd->duration
+                     : isom_update_tkhd_duration( trak ) ? 0
+                     : trak->tkhd->duration;
+    if( isom_add_edts( trak )
+     || isom_add_elst( trak->edts )
+     || isom_add_elst_entry( trak->edts->elst, segment_duration, media_time, media_rate ) )
+        return -1;
+    return isom_update_tkhd_duration( trak );
+}
+
+/*---- create / modification time fields manipulators ----*/
+
+int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->mdia || !trak->mdia->mdhd )
+        return -1;
+    isom_mdhd_t *mdhd = trak->mdia->mdhd;
+    mdhd->modification_time = isom_get_current_mp4time();
+    /* overwrite strange creation_time */
+    if( mdhd->creation_time > mdhd->modification_time )
+        mdhd->creation_time = mdhd->modification_time;
+    return 0;
+}
+
+int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->tkhd )
+        return -1;
+    isom_tkhd_t *tkhd = trak->tkhd;
+    tkhd->modification_time = isom_get_current_mp4time();
+    /* overwrite strange creation_time */
+    if( tkhd->creation_time > tkhd->modification_time )
+        tkhd->creation_time = tkhd->modification_time;
+    return 0;
+}
+
+int lsmash_update_movie_modification_time( lsmash_root_t *root )
+{
+    if( !root || !root->moov || !root->moov->mvhd )
+        return -1;
+    isom_mvhd_t *mvhd = root->moov->mvhd;
+    mvhd->modification_time = isom_get_current_mp4time();
+    /* overwrite strange creation_time */
+    if( mvhd->creation_time > mvhd->modification_time )
+        mvhd->creation_time = mvhd->modification_time;
+    return 0;
+}
+
+/*---- sample manipulators ----*/
+lsmash_sample_t *lsmash_create_sample( uint32_t size )
+{
+    lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
+    if( !sample )
+        return NULL;
+    if( !size )
+        return sample;
+    sample->data = malloc( size );
+    if( !sample->data )
+    {
+        free( sample );
+        return NULL;
+    }
+    sample->length = size;
+    return sample;
+}
+
+int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
+{
+    if( !sample )
+        return -1;
+    if( !size )
+    {
+        if( sample->data )
+            free( sample->data );
+        sample->data = NULL;
+        sample->length = 0;
+        return 0;
+    }
+    if( size == sample->length )
+        return 0;
+    uint8_t *data;
+    if( !sample->data )
+        data = malloc( size );
+    else
+        data = realloc( sample->data, size );
+    if( !data )
+        return -1;
+    sample->data = data;
+    sample->length = size;
+    return 0;
+}
+
+void lsmash_delete_sample( lsmash_sample_t *sample )
+{
+    if( !sample )
+        return;
+    if( sample->data )
+        free( sample->data );
+    free( sample );
+}
+
+isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
+{
+    isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
+    if( !pool )
+        return NULL;
+    if( !size )
+        return pool;
+    pool->data = malloc( size );
+    if( !pool->data )
+    {
+        free( pool );
+        return NULL;
+    }
+    pool->alloc = size;
+    return pool;
+}
+
+static void isom_remove_sample_pool( isom_sample_pool_t *pool )
+{
+    if( !pool )
+        return;
+    if( pool->data )
+        free( pool->data );
+    free( pool );
+}
+
+static uint32_t isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size )
+{
+    if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) )
+        return 0;
+    return isom_get_sample_count( trak );
+}
+
+static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
+{
+    isom_stts_t *stts = stbl->stts;
+    if( !stts->list->entry_count )
+    {
+        if( isom_add_stts_entry( stbl, dts ) )
+            return 0;
+        cache->dts = dts;
+        return dts;
+    }
+    if( dts <= cache->dts )
+        return 0;
+    uint32_t sample_delta = dts - cache->dts;
+    isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
+    if( data->sample_delta == sample_delta )
+        ++ data->sample_count;
+    else if( isom_add_stts_entry( stbl, sample_delta ) )
+        return 0;
+    cache->dts = dts;
+    return sample_delta;
+}
+
+static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
+{
+    isom_ctts_t *ctts = stbl->ctts;
+    if( !ctts )
+    {
+        if( cts == cache->dts )
+        {
+            cache->cts = cts;
+            return 0;
+        }
+        /* Add ctts box and the first ctts entry. */
+        if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) )
+            return -1;
+        ctts = stbl->ctts;
+        isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
+        uint32_t sample_count = stbl->stsz->sample_count;
+        if( sample_count != 1 )
+        {
+            data->sample_count = sample_count - 1;
+            if( isom_add_ctts_entry( stbl, cts - cache->dts ) )
+                return -1;
+        }
+        else
+            data->sample_offset = cts;
+        cache->cts = cts;
+        return 0;
+    }
+    if( !ctts->list )
+        return -1;
+    isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
+    uint32_t sample_offset = cts - cache->dts;
+    if( data->sample_offset == sample_offset )
+        ++ data->sample_count;
+    else if( isom_add_ctts_entry( stbl, sample_offset ) )
+        return -1;
+    cache->cts = cts;
+    return 0;
+}
+
+static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts )
+{
+    if( !trak->cache || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
+        return -1;
+    lsmash_root_t *root = trak->root;
+    if( root->isom_compatible && root->qt_compatible && (cts - dts) > INT32_MAX )
+        return -1;      /* sample_offset is not compatible. */
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_timestamp_t *ts_cache = &trak->cache->timestamp;
+    uint32_t sample_count = isom_get_sample_count( trak );
+    uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
+    if( sample_count > 1 && !sample_delta )
+        return -1;
+    if( isom_add_cts( stbl, ts_cache, cts ) )
+        return -1;
+    if( (cts + ts_cache->ctd_shift) < dts )
+    {
+        if( (root->max_isom_version < 4 && !root->qt_compatible)        /* Negative sample offset is not supported. */
+         || (root->max_isom_version >= 4 && trak->root->qt_compatible)  /* ctts version 1 is not defined in QTFF. */
+         || root->fragment                                              /* Composition time offset is positive. */
+         || ((dts - cts) > INT32_MAX) )                                 /* Overflow */
+            return -1;
+        ts_cache->ctd_shift = dts - cts;
+        if( !stbl->ctts->version && !trak->root->qt_compatible )
+            stbl->ctts->version = 1;
+    }
+    if( trak->cache->fragment )
+    {
+        isom_fragment_t *fragment_cache = trak->cache->fragment;
+        fragment_cache->last_duration = sample_delta;
+        fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
+    }
+    return 0;
+}
+
+static int isom_add_sync_point( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
+{
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_cache_t *cache = trak->cache;
+    if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC )       /* no null check for prop */
+    {
+        if( !cache->all_sync )
+            return 0;
+        if( !stbl->stss && isom_add_stss( stbl ) )
+            return -1;
+        if( isom_add_stss_entry( stbl, 1 ) )    /* Declare here the first sample is a sync sample. */
+            return -1;
+        cache->all_sync = 0;
+        return 0;
+    }
+    if( cache->all_sync )     /* We don't need stss box if all samples are sync sample. */
+        return 0;
+    if( !stbl->stss )
+    {
+        if( isom_get_sample_count( trak ) == 1 )
+        {
+            cache->all_sync = 1;    /* Also the first sample is a sync sample. */
+            return 0;
+        }
+        if( isom_add_stss( stbl ) )
+            return -1;
+    }
+    return isom_add_stss_entry( stbl, sample_number );
+}
+
+static int isom_add_partial_sync( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
+{
+    if( !trak->root->qt_compatible )
+        return 0;
+    if( prop->random_access_type != QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC
+     && !(prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->post_roll.identifier == prop->post_roll.complete) )
+        return 0;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    if( !stbl->stps && isom_add_stps( stbl ) )
+        return -1;
+    return isom_add_stps_entry( stbl, sample_number );
+}
+
+static int isom_add_dependency_type( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
+{
+    if( !trak->root->qt_compatible && !trak->root->avc_extensions )
+        return 0;
+    uint8_t avc_extensions = trak->root->avc_extensions;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    if( stbl->sdtp )
+        return isom_add_sdtp_entry( stbl, prop, avc_extensions );
+    if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant )  /* no null check for prop */
+        return 0;
+    if( isom_add_sdtp( stbl ) )
+        return -1;
+    uint32_t count = isom_get_sample_count( trak );
+    /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
+    lsmash_sample_property_t null_prop = { 0 };
+    for( uint32_t i = 1; i < count; i++ )
+        if( isom_add_sdtp_entry( stbl, &null_prop, avc_extensions ) )
+            return -1;
+    return isom_add_sdtp_entry( stbl, prop, avc_extensions );
+}
+
+static int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_entry_t *sgpd )
+{
+    isom_rap_entry_t *rap = group->random_access;
+    if( !rap )
+        return 0;
+    assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
+    rap->num_leading_samples_known = num_leading_samples_known;
+    /* Avoid duplication of sample group descriptions. */
+    uint32_t group_description_index = 1;
+    for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
+    {
+        isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        if( rap->num_leading_samples_known == data->num_leading_samples_known
+         && rap->num_leading_samples       == data->num_leading_samples )
+        {
+            /* The same description already exists.
+             * Remove the latest random access entry. */
+            lsmash_remove_entry_direct( sgpd->list, sgpd->list->tail, NULL );
+            /* Replace assigned group_description_index with the one corresponding the same description. */
+            if( group->assignment->group_description_index == 0 )
+            {
+                if( group->prev_assignment )
+                    group->prev_assignment->group_description_index = group_description_index;
+            }
+            else
+                group->assignment->group_description_index = group_description_index;
+            break;
+        }
+        ++group_description_index;
+    }
+    group->random_access = NULL;
+    return 0;
+}
+
+static int isom_group_random_access( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
+{
+    if( trak->root->max_isom_version < 6 )
+        return 0;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
+    isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
+    if( !sbgp || !sgpd )
+        return 0;
+    uint8_t is_rap = prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP
+                  || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP
+                  || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP
+                  || (prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->post_roll.identifier == prop->post_roll.complete);
+    isom_rap_group_t *group = trak->cache->rap;
+    if( !group )
+    {
+        /* This sample is the first sample, create a grouping cache. */
+        assert( isom_get_sample_count( trak ) == 1 );
+        group = malloc( sizeof(isom_rap_group_t) );
+        if( !group )
+            return -1;
+        if( is_rap )
+        {
+            group->random_access = isom_add_rap_group_entry( sgpd );
+            group->assignment    = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
+        }
+        else
+        {
+            /* The first sample is not always random access point. */
+            group->random_access = NULL;
+            group->assignment    = isom_add_group_assignment_entry( sbgp, 1, 0 );
+        }
+        if( !group->assignment )
+        {
+            free( group );
+            return -1;
+        }
+        group->prev_assignment = NULL;
+        group->is_prev_rap     = is_rap;
+        trak->cache->rap       = group;
+        return 0;
+    }
+    if( group->is_prev_rap )
+    {
+        /* OK. here, the previous sample is a menber of 'rap '. */
+        if( !is_rap )
+        {
+            /* This sample isn't a member of 'rap ' and the previous sample is.
+             * So we create a new group and set 0 on its group_description_index. */
+            group->prev_assignment = group->assignment;
+            group->assignment      = isom_add_group_assignment_entry( sbgp, 1, 0 );
+            if( !group->assignment )
+            {
+                free( group );
+                return -1;
+            }
+        }
+        else if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
+        {
+            /* Create a new group since there is the possibility the next sample is a leading sample.
+             * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
+            if( isom_rap_grouping_established( group, 1, sgpd ) )
+                return -1;
+            group->random_access   = isom_add_rap_group_entry( sgpd );
+            group->prev_assignment = group->assignment;
+            group->assignment      = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
+            if( !group->assignment )
+            {
+                free( group );
+                return -1;
+            }
+        }
+        else    /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
+            ++ group->assignment->sample_count;
+    }
+    else if( is_rap )
+    {
+        /* This sample is a member of 'rap ' and the previous sample isn't.
+         * So we create a new group and set appropriate value on its group_description_index. */
+        if( isom_rap_grouping_established( group, 1, sgpd ) )
+            return -1;
+        group->random_access   = isom_add_rap_group_entry( sgpd );
+        group->prev_assignment = group->assignment;
+        group->assignment      = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
+        if( !group->assignment )
+        {
+            free( group );
+            return -1;
+        }
+    }
+    else    /* The previous and current sample aren't a member of 'rap '. */
+        ++ group->assignment->sample_count;
+    /* Obtain the property of the latest random access point group. */
+    if( !is_rap && group->random_access )
+    {
+        if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
+        {
+            /* We can no longer know num_leading_samples in this group. */
+            if( isom_rap_grouping_established( group, 0, sgpd ) )
+                return -1;
+        }
+        else
+        {
+            if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
+                ++ group->random_access->num_leading_samples;
+            /* no more consecutive leading samples in this group */
+            else if( isom_rap_grouping_established( group, 1, sgpd ) )
+                return -1;
+        }
+    }
+    group->is_prev_rap = is_rap;
+    return 0;
+}
+
+static int isom_roll_grouping_established( isom_roll_group_t *group, int16_t roll_distance, isom_sgpd_entry_t *sgpd )
+{
+    /* Avoid duplication of sample group descriptions. */
+    uint32_t group_description_index = 1;
+    for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
+    {
+        isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
+        if( !data )
+            return -1;
+        if( roll_distance == data->roll_distance )
+        {
+            /* The same description already exists.
+             * Set the group_description_index corresponding the same description. */
+            group->assignment->group_description_index = group_description_index;
+            return 0;
+        }
+        ++group_description_index;
+    }
+    /* Add a new roll recovery description. */
+    if( !isom_add_roll_group_entry( sgpd, roll_distance ) )
+        return -1;
+    group->assignment->group_description_index = sgpd->list->entry_count;
+    return 0;
+}
+
+/* Remove pooled caches that has become unnecessary. */
+static int isom_clean_roll_pool( lsmash_entry_list_t *pool )
+{
+    for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
+    {
+        isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
+        if( !group )
+            return -1;
+        if( !group->delimited || !group->described )
+            break;
+        if( lsmash_remove_entry_direct( pool, entry, NULL ) )
+            return -1;
+    }
+    return 0;
+}
+
+static int isom_all_recovery_described( lsmash_entry_list_t *pool )
+{
+    for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
+    {
+        isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
+        if( !group )
+            return -1;
+        group->described = 1;
+    }
+    return isom_clean_roll_pool( pool );
+}
+
+static int isom_group_roll_recovery( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
+{
+    if( !trak->root->avc_extensions && !trak->root->qt_compatible )
+        return 0;
+    if( prop->pre_roll.distance > -INT16_MIN )
+        return -1;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
+    isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
+    if( !sbgp || !sgpd )
+        return 0;
+    lsmash_entry_list_t *pool = trak->cache->roll.pool;
+    if( !pool )
+    {
+        pool = lsmash_create_entry_list();
+        if( !pool )
+            return -1;
+        trak->cache->roll.pool = pool;
+    }
+    isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
+    uint32_t sample_count = isom_get_sample_count( trak );
+    if( !group || (prop->pre_roll.distance == 0 && prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY) )
+    {
+        if( group )
+            group->delimited = 1;
+        else
+            assert( sample_count == 1 );
+        /* Create a new group. This group is not 'roll' yet, so we set 0 on its group_description_index. */
+        group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
+        if( !group )
+            return -1;
+        if( prop->pre_roll.distance )
+            group->described = 1;
+        else
+        {
+            group->first_sample   = sample_count;
+            group->recovery_point = prop->post_roll.complete;
+        }
+        group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
+        if( !group->assignment || lsmash_add_entry( pool, group ) )
+        {
+            free( group );
+            return -1;
+        }
+        if( prop->pre_roll.distance )
+            return isom_roll_grouping_established( group, -prop->pre_roll.distance, sgpd );
+    }
+    else
+    {
+        /* Check pre-roll distance. */
+        isom_roll_entry_t *roll = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, sgpd->list->entry_count );
+        int new_group = 0;
+        if( prop->pre_roll.distance )
+        {
+            if( !roll )
+                new_group = 1;      /* Create the first pre-roll group. */
+            else if( prop->pre_roll.distance != -roll->roll_distance )
+            {
+                /* Pre-roll distance is different from the previous. */
+                group->delimited = 1;
+                group->described = 1;
+                new_group = 1;
+            }
+        }
+        if( new_group )
+        {
+            /* Create a new pre-roll group. */
+            group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
+            if( !group )
+                return -1;
+            group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
+            if( !group->assignment || lsmash_add_entry( pool, group ) )
+            {
+                free( group );
+                return -1;
+            }
+            if( prop->pre_roll.distance && isom_roll_grouping_established( group, -prop->pre_roll.distance, sgpd ) )
+                return -1;
+            return isom_all_recovery_described( pool );
+        }
+        ++ group->assignment->sample_count;
+    }
+    /* If encountered a sync sample, all recovery is completed here. */
+    if( prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
+        return isom_all_recovery_described( pool );
+    for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
+    {
+        group = (isom_roll_group_t *)entry->data;
+        if( !group )
+            return -1;
+        if( group->described )
+            continue;
+        if( prop->post_roll.identifier == group->recovery_point )
+        {
+            group->described = 1;
+            int16_t distance = sample_count - group->first_sample;
+            /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
+            if( distance )
+            {
+                /* Now, this group is a 'roll'. */
+                if( isom_roll_grouping_established( group, distance, sgpd ) )
+                    return -1;
+                /* All groups before the current group are described. */
+                lsmash_entry_t *current = entry;
+                for( entry = pool->head; entry != current; entry = entry->next )
+                {
+                    group = (isom_roll_group_t *)entry->data;
+                    if( !group )
+                        return -1;
+                    group->described = 1;
+                }
+            }
+            break;      /* Avoid evaluating groups, in the pool, having the same identifier for recovery point again. */
+        }
+    }
+    return isom_clean_roll_pool( pool );
+}
+
+/* returns 1 if pooled samples must be flushed. */
+/* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
+   see lsmash_append_sample for detail. */
+static int isom_add_chunk( isom_trak_entry_t *trak, lsmash_sample_t *sample )
+{
+    if( !trak->root || !trak->cache || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
+     || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
+        return -1;
+    lsmash_root_t *root = trak->root;
+    isom_chunk_t *current = &trak->cache->chunk;
+    if( !current->pool )
+    {
+        /* Very initial settings, just once per track */
+        current->pool = isom_create_sample_pool( 0 );
+        if( !current->pool )
+            return -1;
+    }
+    if( !current->pool->sample_count )
+    {
+        /* Cannot decide whether we should flush the current sample or not here yet. */
+        ++ current->chunk_number;
+        current->sample_description_index = sample->index;
+        current->first_dts = sample->dts;
+        return 0;
+    }
+    if( sample->dts < current->first_dts )
+        return -1;  /* easy error check. */
+    if( (root->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
+     && (root->max_chunk_size >= current->pool->size + sample->length)
+     && (current->sample_description_index == sample->index) )
+        return 0;   /* No need to flush current cached chunk, the current sample must be put into that. */
+    /* NOTE: chunk relative stuff must be pushed into root after a chunk is fully determined with its contents. */
+    /* now current cached chunk is fixed, actually add chunk relative properties to root accordingly. */
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
+    /* Create a new chunk sequence in this track if needed. */
+    if( (!last_stsc_entry || current->pool->sample_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
+     && isom_add_stsc_entry( stbl, current->chunk_number, current->pool->sample_count, current->sample_description_index ) )
+        return -1;
+    /* Add a new chunk offset in this track. */
+    uint64_t offset = root->size;
+    if( root->fragment )
+        offset += ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size;
+    if( isom_add_stco_entry( stbl, offset ) )
+        return -1;
+    /* update cache information */
+    ++ current->chunk_number;
+    /* re-initialize cache, using the current sample */
+    current->sample_description_index = sample->index;
+    current->first_dts = sample->dts;
+    /* current->pool must be flushed in isom_append_sample_internal() */
+    return 1;
+}
+
+static int isom_write_pooled_samples( lsmash_root_t *root, isom_sample_pool_t *pool )
+{
+    if( !root || !root->mdat || !root->bs || !root->bs->stream )
+        return -1;
+    lsmash_bs_put_bytes( root->bs, pool->data, pool->size );
+    if( lsmash_bs_write_data( root->bs ) )
+        return -1;
+    root->mdat->size  += pool->size;
+    root->size        += pool->size;
+    pool->sample_count = 0;
+    pool->size         = 0;
+    return 0;
+}
+
+static int isom_update_sample_tables( isom_trak_entry_t *trak, lsmash_sample_t *sample )
+{
+    /* Add a sample_size and increment sample_count. */
+    uint32_t sample_count = isom_add_size( trak, sample->length );
+    if( !sample_count )
+        return -1;
+    /* Add a decoding timestamp and a composition timestamp. */
+    if( isom_add_timestamp( trak, sample->dts, sample->cts ) )
+        return -1;
+    /* Add a sync point if needed. */
+    if( isom_add_sync_point( trak, sample_count, &sample->prop ) )
+        return -1;
+    /* Add a partial sync point if needed. */
+    if( isom_add_partial_sync( trak, sample_count, &sample->prop ) )
+        return -1;
+    /* Add leading, independent, disposable and redundant information if needed. */
+    if( isom_add_dependency_type( trak, &sample->prop ) )
+        return -1;
+    /* Group samples into random access point type if needed. */
+    if( isom_group_random_access( trak, &sample->prop ) )
+        return -1;
+    /* Group samples into random access recovery point type if needed. */
+    if( isom_group_roll_recovery( trak, &sample->prop ) )
+        return -1;
+    /* Add a chunk if needed. */
+    return isom_add_chunk( trak, sample );
+}
+
+static int isom_append_fragment_track_run( lsmash_root_t *root, isom_chunk_t *chunk )
+{
+    if( !chunk->pool || !chunk->pool->size )
+        return 0;
+    isom_fragment_manager_t *fragment = root->fragment;
+    /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
+     * Empty the pool of current track. We don't delete data of samples here. */
+    if( lsmash_add_entry( fragment->pool, chunk->pool ) )
+        return -1;
+    fragment->pool->entry_count += chunk->pool->sample_count;
+    fragment->pool_size         += chunk->pool->size;
+    chunk->pool = isom_create_sample_pool( chunk->pool->size );
+    return chunk->pool ? 0 : -1;
+}
+
+static int isom_output_cached_chunk( isom_trak_entry_t *trak )
+{
+    lsmash_root_t *root = trak->root;
+    isom_chunk_t *chunk = &trak->cache->chunk;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
+    /* Create a new chunk sequence in this track if needed. */
+    if( (!last_stsc_entry || chunk->pool->sample_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
+     && isom_add_stsc_entry( stbl, chunk->chunk_number, chunk->pool->sample_count, chunk->sample_description_index ) )
+        return -1;
+    if( root->fragment )
+    {
+        /* Add a new chunk offset in this track. */
+        if( isom_add_stco_entry( stbl, root->size + ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size ) )
+            return -1;
+        return isom_append_fragment_track_run( root, chunk );
+    }
+    /* Add a new chunk offset in this track. */
+    if( isom_add_stco_entry( stbl, root->size ) )
+        return -1;
+    /* Output pooled samples in this track. */
+    return isom_write_pooled_samples( root, chunk->pool );
+}
+
+static int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample )
+{
+    uint64_t pool_size = pool->size + sample->length;
+    if( pool->alloc < pool_size )
+    {
+        uint8_t *data;
+        uint64_t alloc = pool_size + (1<<16);
+        if( !pool->data )
+            data = malloc( alloc );
+        else
+            data = realloc( pool->data, alloc );
+        if( !data )
+            return -1;
+        pool->data = data;
+        pool->alloc = alloc;
+    }
+    memcpy( pool->data + pool->size, sample->data, sample->length );
+    pool->size = pool_size;
+    pool->sample_count += 1;
+    lsmash_delete_sample( sample );
+    return 0;
+}
+
+static int isom_append_sample_internal( isom_trak_entry_t *trak, lsmash_sample_t *sample )
+{
+    int flush = isom_update_sample_tables( trak, sample );
+    if( flush < 0 )
+        return -1;
+    /* flush == 1 means pooled samples must be flushed. */
+    lsmash_root_t *root = trak->root;
+    isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
+    if( flush == 1 && isom_write_pooled_samples( root, current_pool ) )
+        return -1;
+    /* Arbitration system between tracks with extremely scattering dts.
+     * Here, we check whether asynchronization between the tracks exceeds the tolerance.
+     * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
+     * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
+     * Note: you needn't read this loop until you grasp the basic handling of chunks. */
+    double tolerance = root->max_async_tolerance;
+    for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
+    {
+        isom_trak_entry_t *other = (isom_trak_entry_t *)entry->data;
+        if( trak == other )
+            continue;
+        if( !other || !other->cache || !other->mdia || !other->mdia->mdhd || !other->mdia->mdhd->timescale
+         || !other->mdia->minf || !other->mdia->minf->stbl || !other->mdia->minf->stbl->stsc || !other->mdia->minf->stbl->stsc->list )
+            return -1;
+        isom_chunk_t *chunk = &other->cache->chunk;
+        if( !chunk->pool || !chunk->pool->sample_count )
+            continue;
+        double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
+                    - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
+        if( diff > tolerance && isom_output_cached_chunk( other ) )
+            return -1;
+        /* Note: we don't flush the cached chunk in the current track and the current sample here
+         * even if the conditional expression of '-diff > tolerance' meets.
+         * That's useless because appending a sample to another track would be a good equivalent.
+         * It's even harmful because it causes excess chunk division by calling
+         * isom_output_cached_chunk() which always generates a new chunk. 
+         * Anyway some excess chunk division will be there, but rather less without it.
+         * To completely avoid this, we need to observe at least whether the current sample will be placed
+         * right next to the previous chunk of the same track or not. */
+    }
+    /* anyway the current sample must be pooled. */
+    return isom_pool_sample( current_pool, sample );
+}
+
+static int isom_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->root || !trak->cache || !trak->mdia
+     || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
+     || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
+     || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
+        return -1;
+    /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
+    if( !root->mdat )
+    {
+        if( isom_new_mdat( root, 0 ) )
+            return -1;
+        /* Add the size of the Media Data Box and the placeholder. */
+        root->size += 2 * ISOM_BASEBOX_COMMON_SIZE;
+    }
+    isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
+    if( !sample_entry )
+        return -1;
+    if( isom_is_lpcm_audio( sample_entry->type ) )
+    {
+        uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
+        if( sample->length == frame_size )
+            return isom_append_sample_internal( trak, sample );
+        else if( sample->length < frame_size )
+            return -1;
+        /* Append samples splitted into each LPCMFrame. */
+        uint64_t dts = sample->dts;
+        uint64_t cts = sample->cts;
+        for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
+        {
+            lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
+            if( !lpcm_sample )
+                return -1;
+            memcpy( lpcm_sample->data, sample->data + offset, frame_size );
+            lpcm_sample->dts = dts++;
+            lpcm_sample->cts = cts++;
+            lpcm_sample->prop = sample->prop;
+            lpcm_sample->index = sample->index;
+            if( isom_append_sample_internal( trak, lpcm_sample ) )
+            {
+                lsmash_delete_sample( lpcm_sample );
+                return -1;
+            }
+        }
+        lsmash_delete_sample( sample );
+        return 0;
+    }
+    return isom_append_sample_internal( trak, sample );
+}
+
+static int isom_output_cache( isom_trak_entry_t *trak )
+{
+    if( trak->cache->chunk.pool && trak->cache->chunk.pool->sample_count
+     && isom_output_cached_chunk( trak ) )
+        return -1;
+    isom_stbl_t *stbl = trak->mdia->minf->stbl;
+    if( !stbl->sgpd_list )
+        return 0;
+    for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
+    {
+        isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
+        if( !sgpd )
+            return -1;
+        switch( sgpd->grouping_type )
+        {
+            case ISOM_GROUP_TYPE_RAP :
+            {
+                isom_rap_group_t *group = trak->cache->rap;
+                if( !group )
+                {
+                    if( trak->root->fragment )
+                        continue;
+                    else
+                        return -1;
+                }
+                if( !group->random_access )
+                    continue;
+                group->random_access->num_leading_samples_known = 1;
+                break;
+            }
+            case ISOM_GROUP_TYPE_ROLL :
+                if( !trak->cache->roll.pool )
+                {
+                    if( trak->root->fragment )
+                        continue;
+                    else
+                        return -1;
+                }
+                for( lsmash_entry_t *roll_entry = trak->cache->roll.pool->head; roll_entry; roll_entry = roll_entry->next )
+                {
+                    isom_roll_group_t *group = (isom_roll_group_t *)roll_entry->data;
+                    if( !group )
+                        return -1;
+                    group->described = 1;
+                }
+                break;
+            default :
+                break;
+        }
+    }
+    return 0;
+}
+
+static int isom_flush_fragment_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_duration )
+{
+    isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
+    if( !traf )
+        return 0;   /* no samples */
+    if( !traf->cache || !traf->cache->fragment )
+        return -1;
+    if( traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
+    {
+        /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
+         * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
+        isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
+        if( root->fragment->pool_size )
+            trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
+        trun->data_offset = root->fragment->pool_size;
+    }
+    if( isom_append_fragment_track_run( root, &traf->cache->chunk ) )
+        return -1;
+    return isom_set_fragment_last_duration( traf, last_sample_duration );
+}
+
+int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
+{
+    if( !root )
+        return -1;
+    if( root->fragment && root->fragment->movie )
+        return isom_flush_fragment_pooled_samples( root, track_ID, last_sample_delta );
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
+        return -1;
+    if( isom_output_cache( trak ) )
+        return -1;
+    return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
+}
+
+/* This function doesn't update sample_duration of the last sample in the previous movie fragment.
+ * Instead of this, isom_finish_movie_fragment undertakes this task. */
+static int isom_update_fragment_previous_sample_duration( isom_traf_entry_t *traf, isom_trex_entry_t *trex, uint32_t duration )
+{
+    isom_tfhd_t *tfhd = traf->tfhd;
+    isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
+    int previous_run_has_previous_sample = 0;
+    if( trun->sample_count == 1 )
+    {
+        if( traf->trun_list->entry_count == 1 )
+            return 0;       /* The previous track run belongs to the previous movie fragment if it exists. */
+        if( !traf->trun_list->tail->prev || !traf->trun_list->tail->prev->data )
+            return -1;
+        /* OK. The previous sample exists in the previous track run in the same track fragment. */
+        trun = (isom_trun_entry_t *)traf->trun_list->tail->prev->data;
+        previous_run_has_previous_sample = 1;
+    }
+    /* Update default_sample_duration of the Track Fragment Header Box
+     * if this duration is what the first sample in the current track fragment owns. */
+    if( (trun->sample_count == 2 && traf->trun_list->entry_count == 1)
+     || (trun->sample_count == 1 && traf->trun_list->entry_count == 2) )
+    {
+        if( duration != trex->default_sample_duration )
+            tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
+        tfhd->default_sample_duration = duration;
+    }
+    /* Update the previous sample_duration if needed. */
+    if( duration != tfhd->default_sample_duration )
+        trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
+    if( trun->flags )
+    {
+        uint32_t sample_number = trun->sample_count - !previous_run_has_previous_sample;
+        isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, sample_number );
+        if( !row )
+            return -1;
+        row->sample_duration = duration;
+    }
+    traf->cache->fragment->last_duration = duration;
+    return 0;
+}
+
+static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample )
+{
+    isom_sample_flags_t flags;
+    flags.reserved                    = 0;
+    flags.is_leading                  = sample->prop.leading     & 0x3;
+    flags.sample_depends_on           = sample->prop.independent & 0x3;
+    flags.sample_is_depended_on       = sample->prop.disposable  & 0x3;
+    flags.sample_has_redundancy       = sample->prop.redundant   & 0x3;
+    flags.sample_padding_value        = 0;
+    flags.sample_is_non_sync_sample   = sample->prop.random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+    flags.sample_degradation_priority = 0;
+    return flags;
+}
+
+static int isom_update_fragment_sample_tables( isom_traf_entry_t *traf, lsmash_sample_t *sample )
+{
+    isom_tfhd_t *tfhd = traf->tfhd;
+    isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
+    if( !trex )
+        return -1;
+    lsmash_root_t *root   = traf->root;
+    isom_cache_t *cache   = traf->cache;
+    isom_chunk_t *current = &cache->chunk;
+    /* Create a new track run if the duration exceeds max_chunk_duration.
+     * Old one will be appended to the pool of this movie fragment. */
+    int delimit = (root->max_chunk_duration < ((double)(sample->dts - current->first_dts) / lsmash_get_media_timescale( root, tfhd->track_ID )))
+               || (root->max_chunk_size < (current->pool->size + sample->length));
+    isom_trun_entry_t *trun = NULL;
+    if( !traf->trun_list || !traf->trun_list->entry_count || delimit )
+    {
+        if( delimit && traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
+        {
+            /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
+             * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
+            trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
+            if( root->fragment->pool_size )
+                trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
+            trun->data_offset = root->fragment->pool_size;
+        }
+        trun = isom_add_trun( traf );
+        if( !trun )
+            return -1;
+        if( !current->pool )
+        {
+            /* Very initial settings, just once per track */
+            current->pool = isom_create_sample_pool( 0 );
+            if( !current->pool )
+                return -1;
+        }
+    }
+    else
+    {
+        if( !traf->trun_list->tail || !traf->trun_list->tail->data )
+            return -1;
+        trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
+    }
+    uint32_t sample_composition_time_offset = sample->cts - sample->dts;
+    isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample );
+    if( ++trun->sample_count == 1 )
+    {
+        if( traf->trun_list->entry_count == 1 )
+        {
+            /* This track fragment isn't empty-duration-fragment any more. */
+            tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY;
+            /* Set up sample_description_index in this track fragment. */
+            if( sample->index != trex->default_sample_description_index )
+                tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT;
+            tfhd->sample_description_index = current->sample_description_index = sample->index;
+            /* Set up default_sample_size used in this track fragment. */
+            tfhd->default_sample_size = sample->length;
+            /* Set up default_sample_flags used in this track fragment.
+             * Note: we decide an appropriate default value at the end of this movie fragment. */
+            tfhd->default_sample_flags = sample_flags;
+            /* Set up random access information if this sample is random accessible sample.
+             * We inform only the first sample in each movie fragment. */
+            if( root->bs->stream != stdout && sample->prop.random_access_type )
+            {
+                isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, tfhd->track_ID );
+                if( !tfra )
+                {
+                    tfra = isom_add_tfra( root->mfra );
+                    if( !tfra )
+                        return -1;
+                    tfra->track_ID = tfhd->track_ID;
+                }
+                if( !tfra->list )
+                {
+                    tfra->list = lsmash_create_entry_list();
+                    if( !tfra->list )
+                        return -1;
+                }
+                isom_tfra_location_time_entry_t *rap = malloc( sizeof(isom_tfra_location_time_entry_t) );
+                if( !rap )
+                    return -1;
+                rap->time          = sample->cts;   /* If this is wrong, blame vague descriptions of 'presentation time' in the spec. */
+                rap->moof_offset   = root->size;    /* We place Movie Fragment Box in the head of each movie fragment. */
+                rap->traf_number   = cache->fragment->traf_number;
+                rap->trun_number   = traf->trun_list->entry_count;
+                rap->sample_number = trun->sample_count;
+                if( lsmash_add_entry( tfra->list, rap ) )
+                    return -1;
+                tfra->number_of_entry = tfra->list->entry_count;
+                int length;
+                for( length = 1; rap->traf_number >> (length * 8); length++ );
+                tfra->length_size_of_traf_num = LSMASH_MAX( length - 1, tfra->length_size_of_traf_num );
+                for( length = 1; rap->traf_number >> (length * 8); length++ );
+                tfra->length_size_of_trun_num = LSMASH_MAX( length - 1, tfra->length_size_of_trun_num );
+                for( length = 1; rap->sample_number >> (length * 8); length++ );
+                tfra->length_size_of_sample_num = LSMASH_MAX( length - 1, tfra->length_size_of_sample_num );
+            }
+        }
+        trun->first_sample_flags = sample_flags;
+        current->first_dts = sample->dts;
+    }
+    /* Update the optional rows in the current track run except for sample_duration if needed. */
+    if( sample->length != tfhd->default_sample_size )
+        trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT;
+    if( memcmp( &sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
+        trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
+    if( sample_composition_time_offset )
+        trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT;
+    if( trun->flags )
+    {
+        isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
+        if( !row )
+            return -1;
+        row->sample_size                    = sample->length;
+        row->sample_flags                   = sample_flags;
+        row->sample_composition_time_offset = sample_composition_time_offset;
+    }
+    /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
+    if( cache->fragment->has_samples )
+    {
+        /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
+         * since that's trivial for such semi-permanent presentation. */
+        uint64_t prev_dts = cache->timestamp.dts;
+        if( sample->dts <= prev_dts || sample->dts > prev_dts + UINT32_MAX )
+            return -1;
+        uint32_t sample_duration = sample->dts - prev_dts;
+        if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) )
+            return -1;
+    }
+    cache->timestamp.dts = sample->dts;
+    cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts );
+    return delimit;
+}
+
+static int isom_append_fragment_sample_internal_initial( isom_trak_entry_t *trak, lsmash_sample_t *sample )
+{
+    int delimit = 0;
+    /* Update the sample tables of this track fragment.
+     * If a new chunk was created, append the previous one to the pool of this movie fragment. */
+    delimit = isom_update_sample_tables( trak, sample );
+    if( delimit < 0 )
+        return -1;
+    else if( delimit == 1 )
+        isom_append_fragment_track_run( trak->root, &trak->cache->chunk );
+    /* Add a new sample into the pool of this track fragment. */
+    if( isom_pool_sample( trak->cache->chunk.pool, sample ) )
+        return -1;
+    trak->cache->fragment->has_samples = 1;
+    return 0;
+}
+
+static int isom_append_fragment_sample_internal( isom_traf_entry_t *traf, lsmash_sample_t *sample )
+{
+    int delimit = 0;
+    /* Update the sample tables of this track fragment.
+     * If a new track run was created, append the previous one to the pool of this movie fragment. */
+    delimit = isom_update_fragment_sample_tables( traf, sample );
+    if( delimit < 0 )
+        return -1;
+    else if( delimit == 1 )
+        isom_append_fragment_track_run( traf->root, &traf->cache->chunk );
+    /* Add a new sample into the pool of this track fragment. */
+    if( isom_pool_sample( traf->cache->chunk.pool, sample ) )
+        return -1;
+    traf->cache->fragment->has_samples = 1;
+    return 0;
+}
+
+static int isom_append_fragment_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
+{
+    isom_fragment_manager_t *fragment = root->fragment;
+    if( !fragment || !fragment->pool )
+        return -1;
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || !trak->root || !trak->cache || !trak->cache->fragment || !trak->tkhd || !trak->mdia
+     || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
+     || !trak->mdia->minf || !trak->mdia->minf->stbl
+     || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
+     || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
+        return -1;
+    int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL;
+    void *track_fragment = NULL;
+    if( !fragment->movie )
+    {
+        append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial;
+        track_fragment = trak;
+    }
+    else
+    {
+        isom_traf_entry_t *traf = isom_get_traf( fragment->movie, track_ID );
+        if( !traf )
+        {
+            traf = isom_add_traf( root, fragment->movie );
+            if( isom_add_tfhd( traf ) )
+                return -1;
+            traf->tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY;        /* no samples for this track fragment yet */
+            traf->tfhd->track_ID = trak->tkhd->track_ID;
+            traf->cache = trak->cache;
+            traf->cache->fragment->traf_number = fragment->movie->traf_list->entry_count;
+        }
+        else if( !traf->root || !traf->root->moov || !traf->root->moov->mvex || !traf->cache || !traf->tfhd )
+            return -1;
+        append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal;
+        track_fragment = traf;
+    }
+    isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
+    if( !sample_entry )
+        return -1;
+    if( isom_is_lpcm_audio( sample_entry->type ) )
+    {
+        uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
+        if( sample->length == frame_size )
+            return append_sample_func( track_fragment, sample );
+        else if( sample->length < frame_size )
+            return -1;
+        /* Append samples splitted into each LPCMFrame. */
+        uint64_t dts = sample->dts;
+        uint64_t cts = sample->cts;
+        for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
+        {
+            lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
+            if( !lpcm_sample )
+                return -1;
+            memcpy( lpcm_sample->data, sample->data + offset, frame_size );
+            lpcm_sample->dts = dts++;
+            lpcm_sample->cts = cts++;
+            lpcm_sample->prop = sample->prop;
+            lpcm_sample->index = sample->index;
+            if( append_sample_func( track_fragment, lpcm_sample ) )
+            {
+                lsmash_delete_sample( lpcm_sample );
+                return -1;
+            }
+        }
+        lsmash_delete_sample( sample );
+        return 0;
+    }
+    return append_sample_func( track_fragment, sample );
+}
+
+int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
+{
+    /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
+     * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
+    if( !root || !root->bs || !sample || !sample->data || !track_ID
+     || root->max_chunk_duration == 0 || root->max_async_tolerance == 0 )
+        return -1;
+    /* Write File Type Box here if it was not written yet. */
+    if( !root->file_type_written && isom_write_ftyp( root ) )
+        return -1;
+    if( root->fragment && root->fragment->pool )
+        return isom_append_fragment_sample( root, track_ID, sample );
+    return isom_append_sample( root, track_ID, sample );
+}
+
+/*---- misc functions ----*/
+
+#define CHAPTER_BUFSIZE 512 /* for chapter handling */
+
+static int isom_get_start_time( char *chap_time, isom_chapter_entry_t *data )
+{
+    uint64_t hh, mm;
+    double ss;
+    if( sscanf( chap_time, "%"SCNu64":%2"SCNu64":%lf", &hh, &mm, &ss ) != 3 )
+        return -1;
+    /* check overflow */
+    if( hh >= 5124095 || mm >= 60 || ss >= 60 )
+        return -1;
+    /* 1ns timescale */
+    data->start_time = (hh * 3600 + mm * 60 + ss) * 1e9;
+    return 0;
+}
+
+static int isom_lumber_line( char *buff, int bufsize, FILE *chapter  )
+{
+    char *tail;
+    /* remove newline codes and skip empty line */
+    do{
+        if( fgets( buff, bufsize, chapter ) == NULL )
+            return -1;
+        tail = &buff[ strlen( buff ) - 1 ];
+        while( tail >= buff && ( *tail == '\n' || *tail == '\r' ) )
+            *tail-- = '\0';
+    }while( tail < buff );
+    return 0;
+}
+
+static int isom_read_simple_chapter( FILE *chapter, isom_chapter_entry_t *data )
+{
+    char buff[CHAPTER_BUFSIZE];
+    int len;
+
+    /* get start_time */
+    if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) )
+        return -1;
+    char *chapter_time = strchr( buff, '=' );   /* find separator */
+    if( !chapter_time++
+        || isom_get_start_time( chapter_time, data )
+        || isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) /* get chapter_name */
+        return -1;
+    char *chapter_name = strchr( buff, '=' );   /* find separator */
+    if( !chapter_name++ )
+        return -1;
+    len = LSMASH_MIN( 255, strlen( chapter_name ) );  /* We support length of chapter_name up to 255 */
+    data->chapter_name = (char *)malloc( len + 1 );
+    if( !data->chapter_name )
+        return -1;
+    memcpy( data->chapter_name, chapter_name, len );
+    data->chapter_name[len] = '\0';
+    return 0;
+}
+
+static int isom_read_minimum_chapter( FILE *chapter, isom_chapter_entry_t *data )
+{
+    char buff[CHAPTER_BUFSIZE];
+    int len;
+
+    if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) /* read newline */
+        || isom_get_start_time( buff, data ) ) /* get start_time */
+        return -1;
+    /* get chapter_name */
+    char *chapter_name = strchr( buff, ' ' );   /* find separator */
+    if( !chapter_name++ )
+        return -1;
+    len = LSMASH_MIN( 255, strlen( chapter_name ) );  /* We support length of chapter_name up to 255 */
+    data->chapter_name = (char *)malloc( len + 1 );
+    if( !data->chapter_name )
+        return -1;
+    memcpy( data->chapter_name, chapter_name, len );
+    data->chapter_name[len] = '\0';
+    return 0;
+}
+
+typedef int (*fn_get_chapter_data)( FILE *, isom_chapter_entry_t * );
+
+static fn_get_chapter_data isom_check_chap_line( char *file_name )
+{
+    char buff[CHAPTER_BUFSIZE];
+    FILE *fp = fopen( file_name, "rb" );
+    if( !fp )
+        return NULL;
+    fn_get_chapter_data fnc = NULL;
+    if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL )
+    {
+        if( strncmp( buff, "CHAPTER", 7 ) == 0 )
+            fnc = isom_read_simple_chapter;
+        else if( isdigit( buff[0] ) && isdigit( buff[1] ) && buff[2] == ':'
+             && isdigit( buff[3] ) && isdigit( buff[4] ) && buff[5] == ':' )
+            fnc = isom_read_minimum_chapter;
+    }
+    fclose( fp );
+    return fnc;
+}
+
+int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name )
+{
+    /* This function should be called after updating of the latest movie duration. */
+    if( !root || !root->moov || !root->moov->mvhd || !root->moov->mvhd->timescale || !root->moov->mvhd->duration )
+        return -1;
+    /* check each line format */
+    fn_get_chapter_data fnc = isom_check_chap_line( file_name );
+    if( !fnc )
+        return -1;
+    FILE *chapter = fopen( file_name, "rb" );
+    if( !chapter )
+        return -1;
+    if( isom_add_udta( root, 0 ) || isom_add_chpl( root->moov ) )
+        goto fail;
+    isom_chapter_entry_t data;
+    while( !fnc( chapter, &data ) )
+    {
+        data.start_time = (data.start_time + 50) / 100;    /* convert to 100ns unit */
+        if( data.start_time / 1e7 > (double)root->moov->mvhd->duration / root->moov->mvhd->timescale
+            || isom_add_chpl_entry( root->moov->udta->chpl, &data ) )
+            goto fail;
+        free( data.chapter_name );
+        data.chapter_name = NULL;
+    }
+    fclose( chapter );
+    return 0;
+fail:
+    if( data.chapter_name )
+        free( data.chapter_name );
+    fclose( chapter );
+    return -1;
+}
+
+int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name )
+{
+    if( !root || (!root->qt_compatible && !root->itunes_movie) || !root->moov || !root->moov->mvhd )
+        return -1;
+    FILE *chapter = NULL;       /* shut up 'uninitialized' warning */
+    /* Create a Track Reference Box. */
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak || isom_add_tref( trak ) )
+        return -1;
+    /* Create a track_ID for a new chapter track. */
+    uint32_t *id = (uint32_t *)malloc( sizeof(uint32_t) );
+    if( !id )
+        return -1;
+    uint32_t chapter_track_ID = *id = root->moov->mvhd->next_track_ID;
+    /* Create a Track Reference Type Box. */
+    isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP, 1, id );
+    if( !chap )
+        return -1;      /* no need to free id */
+    /* Create a reference chapter track. */
+    if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) )
+        return -1;
+    /* Set track parameters. */
+    lsmash_track_parameters_t track_param;
+    lsmash_initialize_track_parameters( &track_param );
+    track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
+    if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) )
+        goto fail;
+    /* Set media parameters. */
+    uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID );
+    if( !media_timescale )
+        goto fail;
+    lsmash_media_parameters_t media_param;
+    lsmash_initialize_media_parameters( &media_param );
+    media_param.timescale = media_timescale;
+    media_param.ISO_language = root->max_3gpp_version >= 6 || root->itunes_movie ? ISOM_LANGUAGE_CODE_UNDEFINED : 0;
+    media_param.MAC_language = 0;
+    if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) )
+        goto fail;
+    /* Create a sample description. */
+    uint32_t sample_type = root->max_3gpp_version >= 6 || root->itunes_movie ? ISOM_CODEC_TYPE_TX3G_TEXT : QT_CODEC_TYPE_TEXT_TEXT;
+    uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, sample_type, NULL );
+    if( !sample_entry )
+        goto fail;
+    /* Check each line format. */
+    fn_get_chapter_data fnc = isom_check_chap_line( file_name );
+    if( !fnc )
+        goto fail;
+    /* Open chapter format file. */
+    chapter = fopen( file_name, "rb" );
+    if( !chapter )
+        goto fail;
+    /* Parse the file and write text samples. */
+    isom_chapter_entry_t data;
+    while( !fnc( chapter, &data ) )
+    {
+        /* set start_time */
+        data.start_time = data.start_time * 1e-9 * media_timescale + 0.5;
+        /* write a text sample here */
+        uint16_t name_length = strlen( data.chapter_name );
+        lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * (sample_type == QT_CODEC_TYPE_TEXT_TEXT) );
+        if( !sample )
+        {
+            free( data.chapter_name );
+            goto fail;
+        }
+        sample->data[0] = (name_length >> 8) & 0xff;
+        sample->data[1] =  name_length       & 0xff;
+        memcpy( sample->data + 2, data.chapter_name, name_length );
+        if( sample_type == QT_CODEC_TYPE_TEXT_TEXT )
+        {
+            /* QuickTime Player requires Text Encoding Attribute Box ('encd') if media language is ISO language codes : undefined.
+             * Also this box can avoid garbling if the QuickTime text sample is encoded by Unicode characters.
+             * Note: 3GPP Timed Text supports only UTF-8 or UTF-16, so this box isn't needed. */
+            static const uint8_t encd[12] =
+                {
+                    0x00, 0x00, 0x00, 0x0C,     /* size: 12 */
+                    0x65, 0x6E, 0x63, 0x64,     /* type: 'encd' */
+                    0x00, 0x00, 0x01, 0x00      /* Unicode Encoding */
+                };
+            memcpy( sample->data + 2 + name_length, encd, 12 );
+        }
+        sample->dts = sample->cts = data.start_time;
+        sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
+        sample->index = sample_entry;
+        if( lsmash_append_sample( root, chapter_track_ID, sample ) )
+        {
+            free( data.chapter_name );
+            goto fail;
+        }
+        free( data.chapter_name );
+        data.chapter_name = NULL;
+    }
+    if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) )
+        goto fail;
+    trak = isom_get_trak( root, chapter_track_ID );
+    if( !trak )
+        goto fail;
+    fclose( chapter );
+    trak->is_chapter = 1;
+    trak->related_track_ID = track_ID;
+    return 0;
+fail:
+    if( chapter )
+        fclose( chapter );
+    /* Remove chapter track reference. */
+    lsmash_remove_entry_direct( trak->tref->ref_list, trak->tref->ref_list->tail, isom_remove_track_reference_type );
+    if( trak->tref->ref_list->entry_count == 0 )
+        isom_remove_tref( trak->tref );
+    /* Remove the reference chapter track attached at tail of the list. */
+    lsmash_remove_entry_direct( root->moov->trak_list, root->moov->trak_list->tail, isom_remove_trak );
+    return -1;
+}
+
+int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
+{
+    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+    if( !trak )
+        return -1;
+    isom_remove_edts( trak->edts );
+    trak->edts = NULL;
+    return isom_update_tkhd_duration( trak );
+}
+
+void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
+{
+    if( !root || !root->moov || !root->moov->udta )
+        return;
+    isom_remove_chpl( root->moov->udta->chpl );
+    root->moov->udta->chpl = NULL;
+}
+
+int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
+{
+    if( !root || !root->moov || !root->isom_compatible || ISO_language < 0x800 || !notice )
+        return -1;
+    isom_udta_t *udta;
+    if( track_ID )
+    {
+        isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
+        if( !trak || (!trak->udta && isom_add_udta( root, track_ID )) )
+            return -1;
+        udta = trak->udta;
+    }
+    else
+    {
+        if( !root->moov->udta && isom_add_udta( root, 0 ) )
+            return -1;
+        udta = root->moov->udta;
+    }
+    assert( udta );
+    if( udta->cprt_list )
+        for( lsmash_entry_t *entry = udta->cprt_list->head; entry; entry = entry->next )
+        {
+            isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
+            if( !cprt || cprt->language == ISO_language )
+                return -1;
+        }
+    if( isom_add_cprt( udta ) )
+        return -1;
+    isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list->tail->data;
+    cprt->language = ISO_language;
+    cprt->notice_length = strlen( notice ) + 1;
+    cprt->notice = lsmash_memdup( notice, cprt->notice_length );
+    return 0;
+}
+
+static isom_data_t *isom_add_metadata( lsmash_root_t *root, uint32_t type, char *meaning_string, char *name_string )
+{
+    assert( root && root->moov );
+    if( ((type == ITUNES_METADATA_TYPE_CUSTOM) && (!meaning_string || !meaning_string[0]) )
+     || (!root->moov->udta             && isom_add_udta( root, 0 ))
+     || (!root->moov->udta->meta       && isom_add_meta( (isom_box_t *)root->moov->udta ))
+     || (!root->moov->udta->meta->hdlr && isom_add_hdlr( NULL, root->moov->udta->meta, NULL, ISOM_META_HANDLER_TYPE_ITUNES_METADATA ))
+     || (!root->moov->udta->meta->ilst && isom_add_ilst( root->moov )) )
+        return NULL;
+    isom_ilst_t *ilst = root->moov->udta->meta->ilst;
+    if( isom_add_metaitem( ilst, type ) )
+        return NULL;
+    isom_metaitem_t *metaitem = (isom_metaitem_t *)ilst->item_list->tail->data;
+    if( type == ITUNES_METADATA_TYPE_CUSTOM )
+    {
+        if( isom_add_mean( metaitem ) )
+            goto fail;
+        isom_mean_t *mean = metaitem->mean;
+        mean->meaning_string_length = strlen( meaning_string );    /* No null terminator */
+        mean->meaning_string = lsmash_memdup( meaning_string, mean->meaning_string_length );
+        if( !mean->meaning_string )
+            goto fail;
+        if( name_string && name_string[0] )
+        {
+            if( isom_add_name( metaitem ) )
+                goto fail;
+            isom_name_t *name = metaitem->name;
+            name->name_length = strlen( name_string );    /* No null terminator */
+            name->name = lsmash_memdup( name_string, name->name_length );
+            if( !name->name )
+                goto fail;
+        }
+    }
+    if( isom_add_data( metaitem ) )
+        goto fail;
+    return metaitem->data;
+fail:
+    lsmash_remove_entry_direct( ilst->item_list, ilst->item_list->tail, isom_remove_metaitem );
+    return NULL;
+}
+
+int lsmash_set_itunes_metadata_string( lsmash_root_t *root, lsmash_itunes_metadata_type type, char *value, char *meaning, char *name )
+{
+    static const lsmash_itunes_metadata_type metadata_type_table[] =
+        {
+            ITUNES_METADATA_TYPE_ALBUM_NAME,
+            ITUNES_METADATA_TYPE_ARTIST,
+            ITUNES_METADATA_TYPE_USER_COMMENT,
+            ITUNES_METADATA_TYPE_RELEASE_DATE,
+            ITUNES_METADATA_TYPE_ENCODED_BY,
+            ITUNES_METADATA_TYPE_USER_GENRE,
+            ITUNES_METADATA_TYPE_0XA9_GROUPING,
+            ITUNES_METADATA_TYPE_LYRICS,
+            ITUNES_METADATA_TYPE_TITLE,
+            ITUNES_METADATA_TYPE_TRACK_SUBTITLE,
+            ITUNES_METADATA_TYPE_ENCODING_TOOL,
+            ITUNES_METADATA_TYPE_COMPOSER,
+            ITUNES_METADATA_TYPE_ALBUM_ARTIST,
+            ITUNES_METADATA_TYPE_PODCAST_CATEGORY,
+            ITUNES_METADATA_TYPE_COPYRIGHT,
+            ITUNES_METADATA_TYPE_DESCRIPTION,
+            ITUNES_METADATA_TYPE_GROUPING,
+            ITUNES_METADATA_TYPE_PODCAST_KEYWORD,
+            ITUNES_METADATA_TYPE_LONG_DESCRIPTION,
+            ITUNES_METADATA_TYPE_PURCHASE_DATE,
+            ITUNES_METADATA_TYPE_TV_EPISODE_ID,
+            ITUNES_METADATA_TYPE_TV_NETWORK,
+            ITUNES_METADATA_TYPE_TV_SHOW_NAME,
+            ITUNES_METADATA_TYPE_ITUNES_PURCHASE_ACCOUNT_ID,
+            ITUNES_METADATA_TYPE_CUSTOM,
+            0
+        };
+    int i;
+    for( i = 0; metadata_type_table[i]; i++ )
+        if( type == metadata_type_table[i] )
+            break;
+    if( !metadata_type_table[i] )
+        return -1;
+    uint32_t value_length = strlen( value );
+    if( type == ITUNES_METADATA_TYPE_DESCRIPTION && value_length > 255 )
+        type = ITUNES_METADATA_TYPE_LONG_DESCRIPTION;
+    isom_data_t *data = isom_add_metadata( root, type, meaning, name );
+    if( !data )
+        return -1;
+    data->type_code = 1;
+    data->value_length = value_length;      /* No null terminator */
+    data->value = lsmash_memdup( value, data->value_length );
+    if( !data->value )
+    {
+        isom_ilst_t *ilst = root->moov->udta->meta->ilst;
+        lsmash_remove_entry_direct( ilst->item_list, ilst->item_list->tail, isom_remove_metaitem );
+        return -1;
+    }
+    return 0;
+}
+
+int lsmash_set_itunes_metadata_integer( lsmash_root_t *root, lsmash_itunes_metadata_type type, uint64_t value, char *meaning, char *name )
+{
+    static const struct
+    {
+        lsmash_itunes_metadata_type type;
+        int                         length;
+    } metadata_code_type_table[] =
+        {
+            { ITUNES_METADATA_TYPE_EPISODE_GLOBAL_ID,          1 },
+            { ITUNES_METADATA_TYPE_PREDEFINED_GENRE,           4 },
+            { ITUNES_METADATA_TYPE_CONTENT_RATING,             1 },
+            { ITUNES_METADATA_TYPE_MEDIA_TYPE,                 1 },
+            { ITUNES_METADATA_TYPE_BEATS_PER_MINUTE,           2 },
+            { ITUNES_METADATA_TYPE_TV_EPISODE,                 4 },
+            { ITUNES_METADATA_TYPE_TV_SEASON,                  4 },
+            { ITUNES_METADATA_TYPE_ITUNES_ACCOUNT_TYPE,        1 },
+            { ITUNES_METADATA_TYPE_ITUNES_ARTIST_ID,           4 },
+            { ITUNES_METADATA_TYPE_ITUNES_COMPOSER_ID,         4 },
+            { ITUNES_METADATA_TYPE_ITUNES_CATALOG_ID,          4 },
+            { ITUNES_METADATA_TYPE_ITUNES_TV_GENRE_ID,         4 },
+            { ITUNES_METADATA_TYPE_ITUNES_PLAYLIST_ID,         8 },
+            { ITUNES_METADATA_TYPE_ITUNES_COUNTRY_CODE,        4 },
+            { ITUNES_METADATA_TYPE_CUSTOM,                     8 },
+            { 0 }
+        };
+    int i;
+    for( i = 0; metadata_code_type_table[i].type; i++ )
+        if( type == metadata_code_type_table[i].type )
+            break;
+    if( !metadata_code_type_table[i].type )
+        return -1;
+    isom_data_t *data = isom_add_metadata( root, type, meaning, name );
+    if( !data )
+        return -1;
+    data->type_code = 21;
+    data->value_length = metadata_code_type_table[i].length;
+    uint8_t temp[8];
+    for( i = 0; i < data->value_length; i++ )
+    {
+        int shift = (data->value_length - i - 1) * 8;
+        temp[i] = (value >> shift) & 0xff;
+    }
+    data->value = lsmash_memdup( temp, data->value_length );
+    if( !data->value )
+    {
+        isom_ilst_t *ilst = root->moov->udta->meta->ilst;
+        lsmash_remove_entry_direct( ilst->item_list, ilst->item_list->tail, isom_remove_metaitem );
+        return -1;
+    }
+    return 0;
+}
+
+int lsmash_set_itunes_metadata_boolean( lsmash_root_t *root, lsmash_itunes_metadata_type type, lsmash_boolean_t value, char *meaning, char *name )
+{
+    static const lsmash_itunes_metadata_type metadata_type_table[] =
+        {
+            ITUNES_METADATA_TYPE_DISC_COMPILATION,
+            ITUNES_METADATA_TYPE_HIGH_DEFINITION_VIDEO,
+            ITUNES_METADATA_TYPE_PODCAST,
+            ITUNES_METADATA_TYPE_GAPLESS_PLAYBACK,
+            ITUNES_METADATA_TYPE_CUSTOM,
+            0
+        };
+    int i;
+    for( i = 0; metadata_type_table[i]; i++ )
+        if( type == metadata_type_table[i] )
+            break;
+    if( !metadata_type_table[i] )
+        return -1;
+    isom_data_t *data = isom_add_metadata( root, type, meaning, name );
+    if( !data )
+        return -1;
+    data->type_code = 21;
+    data->value_length = 1;
+    uint8_t temp = (uint8_t)value;
+    data->value = lsmash_memdup( &temp, 1 );
+    if( !data->value )
+    {
+        isom_ilst_t *ilst = root->moov->udta->meta->ilst;
+        lsmash_remove_entry_direct( ilst->item_list, ilst->item_list->tail, isom_remove_metaitem );
+        return -1;
+    }
+    return 0;
+}
+
+#ifdef LSMASH_DEMUXER_ENABLED
+/*---- remuxer functions ----*/
+
+static int isom_copy_mean( isom_metaitem_t *dst, isom_metaitem_t *src )
+{
+    if( !dst )
+        return 0;
+    isom_remove_mean( dst->mean );
+    if( !src || !src->mean )
+        return 0;
+    if( isom_add_mean( dst ) )
+        return -1;
+    if( src->mean->meaning_string )
+    {
+        dst->mean->meaning_string = lsmash_memdup( src->mean->meaning_string, src->mean->meaning_string_length );
+        if( !dst->mean->meaning_string )
+            return -1;
+        dst->mean->meaning_string_length = src->mean->meaning_string_length;
+    }
+    return 0;
+}
+
+static int isom_copy_name( isom_metaitem_t *dst, isom_metaitem_t *src )
+{
+    if( !dst )
+        return 0;
+    isom_remove_name( dst->name );
+    if( !src || !src->name )
+        return 0;
+    if( isom_add_name( dst ) )
+        return -1;
+    if( src->name->name )
+    {
+        dst->name->name = lsmash_memdup( src->name->name, src->name->name_length );
+        if( !dst->name->name )
+            return -1;
+        dst->name->name_length = src->name->name_length;
+    }
+    return 0;
+}
+
+static int isom_copy_data( isom_metaitem_t *dst, isom_metaitem_t *src )
+{
+    if( !dst )
+        return 0;
+    isom_remove_data( dst->data );
+    if( !src || !src->data )
+        return 0;
+    if( isom_add_data( dst ) )
+        return -1;
+    isom_copy_fields( dst, src, data );
+    if( src->data->value )
+    {
+        dst->data->value = lsmash_memdup( src->data->value, src->data->value_length );
+        if( !dst->data->value )
+            return -1;
+        dst->data->value_length = src->data->value_length;
+    }
+    return 0;
+}
+
+static isom_metaitem_t *isom_duplicate_metaitem( isom_metaitem_t *src )
+{
+    isom_metaitem_t *dst = lsmash_memdup( src, sizeof(isom_metaitem_t) );
+    if( !dst )
+        return NULL;
+    dst->mean = NULL;
+    dst->name = NULL;
+    dst->data = NULL;
+    /* Copy children. */
+    if( isom_copy_mean( dst, src )
+     || isom_copy_name( dst, src )
+     || isom_copy_data( dst, src ) )
+    {
+        isom_remove_metaitem( dst );
+        return NULL;
+    }
+    return dst;
+}
+
+lsmash_itunes_metadata_list_t *lsmash_export_itunes_metadata( lsmash_root_t *root )
+{
+    if( !root || !root->moov )
+        return NULL;
+    if( !root->moov->udta || !root->moov->udta->meta || !root->moov->udta->meta->ilst )
+    {
+        isom_ilst_t *dst = lsmash_malloc_zero( sizeof(isom_ilst_t) );
+        if( !dst )
+            return NULL;
+        return (lsmash_itunes_metadata_list_t *)dst;
+    }
+    isom_ilst_t *src = root->moov->udta->meta->ilst;
+    isom_ilst_t *dst = lsmash_memdup( src, sizeof(isom_ilst_t) );
+    if( !dst )
+        return NULL;
+    dst->root      = NULL;
+    dst->parent    = NULL;
+    dst->item_list = NULL;
+    if( src->item_list )
+    {
+        dst->item_list = lsmash_create_entry_list();
+        if( !dst->item_list )
+        {
+            free( dst );
+            return NULL;
+        }
+        for( lsmash_entry_t *entry = src->item_list->head; entry; entry = entry->next )
+        {
+            isom_metaitem_t *dst_metaitem = isom_duplicate_metaitem( (isom_metaitem_t *)entry->data );
+            if( !dst_metaitem )
+            {
+                isom_remove_ilst( dst );
+                return NULL;
+            }
+            if( lsmash_add_entry( dst->item_list, dst_metaitem ) )
+            {
+                isom_remove_metaitem( dst_metaitem );
+                isom_remove_ilst( dst );
+                return NULL;
+            }
+        }
+    }
+    return (lsmash_itunes_metadata_list_t *)dst;
+}
+
+int lsmash_import_itunes_metadata( lsmash_root_t *root, lsmash_itunes_metadata_list_t *list )
+{
+    if( !root || !list )
+        return -1;
+    if( !root->itunes_movie )
+        return 0;
+    isom_ilst_t *src = (isom_ilst_t *)list;
+    if( !src->item_list || !src->item_list->entry_count )
+        return 0;
+    if( (!root->moov->udta             && isom_add_udta( root, 0 ))
+     || (!root->moov->udta->meta       && isom_add_meta( (isom_box_t *)root->moov->udta ))
+     || (!root->moov->udta->meta->hdlr && isom_add_hdlr( NULL, root->moov->udta->meta, NULL, ISOM_META_HANDLER_TYPE_ITUNES_METADATA ))
+     || (!root->moov->udta->meta->ilst && isom_add_ilst( root->moov )) )
+        return -1;
+    isom_ilst_t *dst = root->moov->udta->meta->ilst;
+    for( lsmash_entry_t *entry = src->item_list->head; entry; entry = entry->next )
+    {
+        isom_metaitem_t *dst_metaitem = isom_duplicate_metaitem( (isom_metaitem_t *)entry->data );
+        if( !dst_metaitem )
+            return -1;
+        if( lsmash_add_entry( dst->item_list, dst_metaitem ) )
+        {
+            isom_remove_metaitem( dst_metaitem );
+            return -1;
+        }
+    }
+    return 0;
+}
+
+void lsmash_destroy_itunes_metadata( lsmash_itunes_metadata_list_t *list )
+{
+    isom_remove_ilst( (isom_ilst_t *)list );
+}
+#endif /* LSMASH_DEMUXER_ENABLED */
diff --git a/output/mp4/lsmash.h b/output/mp4/lsmash.h
new file mode 100644
index 0000000..69e6636
--- /dev/null
+++ b/output/mp4/lsmash.h
@@ -0,0 +1,1292 @@
+/*****************************************************************************
+ * lsmash.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef LSMASH_H
+#define LSMASH_H
+
+#include <stdint.h>
+
+#define PRIVATE     /* If this declaration is placed at a variable, any user shouldn't use it. */
+
+#define LSMASH_4CC( a, b, c, d ) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d))
+#define LSMASH_PACK_ISO_LANGUAGE( a, b, c ) ((((a-0x60)&0x1f)<<10) | (((b-0x60)&0x1f)<<5) | ((c-0x60)&0x1f))
+
+/* public constants */
+typedef enum
+{
+    LSMASH_FILE_MODE_WRITE             = 1,
+    LSMASH_FILE_MODE_READ              = 1<<1,
+    LSMASH_FILE_MODE_DUMP              = 1<<2,
+    LSMASH_FILE_MODE_FRAGMENTED        = 1<<16,
+    LSMASH_FILE_MODE_WRITE_FRAGMENTED  = LSMASH_FILE_MODE_WRITE | LSMASH_FILE_MODE_FRAGMENTED,
+    LSMASH_FILE_MODE_READ_FRAGMENTED   = LSMASH_FILE_MODE_READ  | LSMASH_FILE_MODE_FRAGMENTED,
+} lsmash_file_mode;
+
+typedef enum
+{
+    ISOM_MEDIA_HANDLER_TYPE_3GPP_SCENE_DESCRIPTION              = LSMASH_4CC( '3', 'g', 's', 'd' ),
+    ISOM_MEDIA_HANDLER_TYPE_ID3_VERSION2_METADATA               = LSMASH_4CC( 'I', 'D', '3', '2' ),
+    ISOM_MEDIA_HANDLER_TYPE_AUXILIARY_VIDEO_TRACK               = LSMASH_4CC( 'a', 'u', 'x', 'v' ),
+    ISOM_MEDIA_HANDLER_TYPE_CPCM_AUXILIARY_METADATA             = LSMASH_4CC( 'c', 'p', 'a', 'd' ),
+    ISOM_MEDIA_HANDLER_TYPE_CLOCK_REFERENCE_STREAM              = LSMASH_4CC( 'c', 'r', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_DVB_MANDATORY_BASIC_DESCRIPTION     = LSMASH_4CC( 'd', 'm', 'b', 'd' ),
+    ISOM_MEDIA_HANDLER_TYPE_TV_ANYTIME                          = LSMASH_4CC( 'd', 't', 'v', 'a' ),
+    ISOM_MEDIA_HANDLER_TYPE_BROADBAND_CONTENT_GUIDE             = LSMASH_4CC( 'd', 't', 'v', 'a' ),
+    ISOM_MEDIA_HANDLER_TYPE_FONT_DATA_STREAM                    = LSMASH_4CC( 'f', 'd', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_GENERAL_MPEG4_SYSTEM_STREAM         = LSMASH_4CC( 'g', 'e', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK                          = LSMASH_4CC( 'h', 'i', 'n', 't' ),
+    ISOM_MEDIA_HANDLER_TYPE_IPDC_ELECTRONIC_SERVICE_GUIDE       = LSMASH_4CC( 'i', 'p', 'd', 'c' ),
+    ISOM_MEDIA_HANDLER_TYPE_IPMP_STREAM                         = LSMASH_4CC( 'i', 'p', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_MPEG7_STREAM                        = LSMASH_4CC( 'm', '7', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK                = LSMASH_4CC( 'm', 'e', 't', 'a' ),
+    ISOM_MEDIA_HANDLER_TYPE_MPEGJ_STREAM                        = LSMASH_4CC( 'm', 'j', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_MPEG21_DIGITAL_ITEM                 = LSMASH_4CC( 'm', 'p', '2', '1' ),
+    ISOM_MEDIA_HANDLER_TYPE_OBJECT_CONTENT_INFO_STREAM          = LSMASH_4CC( 'o', 'c', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_OBJECT_DESCRIPTOR_STREAM            = LSMASH_4CC( 'o', 'd', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_SCENE_DESCRIPTION_STREAM            = LSMASH_4CC( 's', 'd', 's', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_KEY_MANAGEMENT_MESSAGES             = LSMASH_4CC( 's', 'k', 'm', 'm' ),
+    ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK                         = LSMASH_4CC( 's', 'o', 'u', 'n' ),
+    ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK                          = LSMASH_4CC( 't', 'e', 'x', 't' ),
+    ISOM_MEDIA_HANDLER_TYPE_PROPRIETARY_DESCRIPTIVE_METADATA    = LSMASH_4CC( 'u', 'r', 'i', ' ' ),
+    ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK                         = LSMASH_4CC( 'v', 'i', 'd', 'e' ),
+} lsmash_media_type;
+
+typedef enum
+{
+    ISOM_BRAND_TYPE_3G2A  = LSMASH_4CC( '3', 'g', '2', 'a' ),
+    ISOM_BRAND_TYPE_3GE6  = LSMASH_4CC( '3', 'g', 'e', '6' ),
+    ISOM_BRAND_TYPE_3GG6  = LSMASH_4CC( '3', 'g', 'g', '6' ),
+    ISOM_BRAND_TYPE_3GP4  = LSMASH_4CC( '3', 'g', 'p', '4' ),
+    ISOM_BRAND_TYPE_3GP5  = LSMASH_4CC( '3', 'g', 'p', '5' ),
+    ISOM_BRAND_TYPE_3GP6  = LSMASH_4CC( '3', 'g', 'p', '6' ),
+    ISOM_BRAND_TYPE_3GR6  = LSMASH_4CC( '3', 'g', 'r', '6' ),
+    ISOM_BRAND_TYPE_3GS6  = LSMASH_4CC( '3', 'g', 's', '6' ),
+    ISOM_BRAND_TYPE_CAEP  = LSMASH_4CC( 'C', 'A', 'E', 'P' ),
+    ISOM_BRAND_TYPE_CDES  = LSMASH_4CC( 'C', 'D', 'e', 's' ),
+    ISOM_BRAND_TYPE_M4A   = LSMASH_4CC( 'M', '4', 'A', ' ' ),
+    ISOM_BRAND_TYPE_M4B   = LSMASH_4CC( 'M', '4', 'B', ' ' ),
+    ISOM_BRAND_TYPE_M4P   = LSMASH_4CC( 'M', '4', 'P', ' ' ),
+    ISOM_BRAND_TYPE_M4V   = LSMASH_4CC( 'M', '4', 'V', ' ' ),
+    ISOM_BRAND_TYPE_MPPI  = LSMASH_4CC( 'M', 'P', 'P', 'I' ),
+    ISOM_BRAND_TYPE_ROSS  = LSMASH_4CC( 'R', 'O', 'S', 'S' ),
+    ISOM_BRAND_TYPE_AVC1  = LSMASH_4CC( 'a', 'v', 'c', '1' ),
+    ISOM_BRAND_TYPE_CAQV  = LSMASH_4CC( 'c', 'a', 'q', 'v' ),
+    ISOM_BRAND_TYPE_DA0A  = LSMASH_4CC( 'd', 'a', '0', 'a' ),
+    ISOM_BRAND_TYPE_DA0B  = LSMASH_4CC( 'd', 'a', '0', 'b' ),
+    ISOM_BRAND_TYPE_DA1A  = LSMASH_4CC( 'd', 'a', '1', 'a' ),
+    ISOM_BRAND_TYPE_DA1B  = LSMASH_4CC( 'd', 'a', '1', 'b' ),
+    ISOM_BRAND_TYPE_DA2A  = LSMASH_4CC( 'd', 'a', '2', 'a' ),
+    ISOM_BRAND_TYPE_DA2B  = LSMASH_4CC( 'd', 'a', '2', 'b' ),
+    ISOM_BRAND_TYPE_DA3A  = LSMASH_4CC( 'd', 'a', '3', 'a' ),
+    ISOM_BRAND_TYPE_DA3B  = LSMASH_4CC( 'd', 'a', '3', 'b' ),
+    ISOM_BRAND_TYPE_DMB1  = LSMASH_4CC( 'd', 'm', 'b', '1' ),
+    ISOM_BRAND_TYPE_DV1A  = LSMASH_4CC( 'd', 'v', '1', 'a' ),
+    ISOM_BRAND_TYPE_DV1B  = LSMASH_4CC( 'd', 'v', '1', 'b' ),
+    ISOM_BRAND_TYPE_DV2A  = LSMASH_4CC( 'd', 'v', '2', 'a' ),
+    ISOM_BRAND_TYPE_DV2B  = LSMASH_4CC( 'd', 'v', '2', 'b' ),
+    ISOM_BRAND_TYPE_DV3A  = LSMASH_4CC( 'd', 'v', '3', 'a' ),
+    ISOM_BRAND_TYPE_DV3B  = LSMASH_4CC( 'd', 'v', '3', 'b' ),
+    ISOM_BRAND_TYPE_DVR1  = LSMASH_4CC( 'd', 'v', 'r', '1' ),
+    ISOM_BRAND_TYPE_DVT1  = LSMASH_4CC( 'd', 'v', 't', '1' ),
+    ISOM_BRAND_TYPE_ISC2  = LSMASH_4CC( 'i', 's', 'c', '2' ),
+    ISOM_BRAND_TYPE_ISO2  = LSMASH_4CC( 'i', 's', 'o', '2' ),
+    ISOM_BRAND_TYPE_ISO3  = LSMASH_4CC( 'i', 's', 'o', '3' ),
+    ISOM_BRAND_TYPE_ISO4  = LSMASH_4CC( 'i', 's', 'o', '4' ),
+    ISOM_BRAND_TYPE_ISO5  = LSMASH_4CC( 'i', 's', 'o', '5' ),
+    ISOM_BRAND_TYPE_ISO6  = LSMASH_4CC( 'i', 's', 'o', '6' ),
+    ISOM_BRAND_TYPE_ISOM  = LSMASH_4CC( 'i', 's', 'o', 'm' ),
+    ISOM_BRAND_TYPE_JPSI  = LSMASH_4CC( 'j', 'p', 's', 'i' ),
+    ISOM_BRAND_TYPE_MJ2S  = LSMASH_4CC( 'm', 'j', '2', 'j' ),
+    ISOM_BRAND_TYPE_MJP2  = LSMASH_4CC( 'm', 'j', 'p', '2' ),
+    ISOM_BRAND_TYPE_MP21  = LSMASH_4CC( 'm', 'p', '2', '1' ),
+    ISOM_BRAND_TYPE_MP41  = LSMASH_4CC( 'm', 'p', '4', '1' ),
+    ISOM_BRAND_TYPE_MP42  = LSMASH_4CC( 'm', 'p', '4', '2' ),
+    ISOM_BRAND_TYPE_MP71  = LSMASH_4CC( 'm', 'p', '7', '1' ),
+    ISOM_BRAND_TYPE_NIKO  = LSMASH_4CC( 'n', 'i', 'k', 'o' ),
+    ISOM_BRAND_TYPE_ODCF  = LSMASH_4CC( 'o', 'd', 'c', 'f' ),
+    ISOM_BRAND_TYPE_OPF2  = LSMASH_4CC( 'o', 'p', 'f', '2' ),
+    ISOM_BRAND_TYPE_OPX2  = LSMASH_4CC( 'o', 'p', 'x', '2' ),
+    ISOM_BRAND_TYPE_PANA  = LSMASH_4CC( 'p', 'a', 'n', 'a' ),
+    ISOM_BRAND_TYPE_QT    = LSMASH_4CC( 'q', 't', ' ', ' ' ),
+    ISOM_BRAND_TYPE_SDV   = LSMASH_4CC( 's', 'd', 'v', ' ' ),
+} lsmash_brand_type;
+
+typedef enum
+{
+    /* Audio Type */
+    ISOM_CODEC_TYPE_AC_3_AUDIO  = LSMASH_4CC( 'a', 'c', '-', '3' ),   /* AC-3 audio */
+    ISOM_CODEC_TYPE_ALAC_AUDIO  = LSMASH_4CC( 'a', 'l', 'a', 'c' ),   /* Apple lossless audio codec */
+    ISOM_CODEC_TYPE_DRA1_AUDIO  = LSMASH_4CC( 'd', 'r', 'a', '1' ),   /* DRA Audio */
+    ISOM_CODEC_TYPE_DTSC_AUDIO  = LSMASH_4CC( 'd', 't', 's', 'c' ),   /* DTS Coherent Acoustics audio */
+    ISOM_CODEC_TYPE_DTSH_AUDIO  = LSMASH_4CC( 'd', 't', 's', 'h' ),   /* DTS-HD High Resolution Audio */
+    ISOM_CODEC_TYPE_DTSL_AUDIO  = LSMASH_4CC( 'd', 't', 's', 'l' ),   /* DTS-HD Master Audio */
+    ISOM_CODEC_TYPE_DTSE_AUDIO  = LSMASH_4CC( 'd', 't', 's', 'e' ),   /* DTS Express low bit rate audio, also known as DTS LBR */
+    ISOM_CODEC_TYPE_EC_3_AUDIO  = LSMASH_4CC( 'e', 'c', '-', '3' ),   /* Enhanced AC-3 audio */
+    ISOM_CODEC_TYPE_ENCA_AUDIO  = LSMASH_4CC( 'e', 'n', 'c', 'a' ),   /* Encrypted/Protected audio */
+    ISOM_CODEC_TYPE_G719_AUDIO  = LSMASH_4CC( 'g', '7', '1', '9' ),   /* ITU-T Recommendation G.719 (2008) */
+    ISOM_CODEC_TYPE_G726_AUDIO  = LSMASH_4CC( 'g', '7', '2', '6' ),   /* ITU-T Recommendation G.726 (1990) */
+    ISOM_CODEC_TYPE_M4AE_AUDIO  = LSMASH_4CC( 'm', '4', 'a', 'e' ),   /* MPEG-4 Audio Enhancement */
+    ISOM_CODEC_TYPE_MLPA_AUDIO  = LSMASH_4CC( 'm', 'l', 'p', 'a' ),   /* MLP Audio */
+    ISOM_CODEC_TYPE_MP4A_AUDIO  = LSMASH_4CC( 'm', 'p', '4', 'a' ),   /* MPEG-4 Audio */
+    ISOM_CODEC_TYPE_RAW_AUDIO   = LSMASH_4CC( 'r', 'a', 'w', ' ' ),   /* Uncompressed audio */
+    ISOM_CODEC_TYPE_SAMR_AUDIO  = LSMASH_4CC( 's', 'a', 'm', 'r' ),   /* Narrowband AMR voice */
+    ISOM_CODEC_TYPE_SAWB_AUDIO  = LSMASH_4CC( 's', 'a', 'w', 'b' ),   /* Wideband AMR voice */
+    ISOM_CODEC_TYPE_SAWP_AUDIO  = LSMASH_4CC( 's', 'a', 'w', 'p' ),   /* Extended AMR-WB (AMR-WB+) */
+    ISOM_CODEC_TYPE_SEVC_AUDIO  = LSMASH_4CC( 's', 'e', 'v', 'c' ),   /* EVRC Voice */
+    ISOM_CODEC_TYPE_SQCP_AUDIO  = LSMASH_4CC( 's', 'q', 'c', 'p' ),   /* 13K Voice */
+    ISOM_CODEC_TYPE_SSMV_AUDIO  = LSMASH_4CC( 's', 's', 'm', 'v' ),   /* SMV Voice */
+    ISOM_CODEC_TYPE_TWOS_AUDIO  = LSMASH_4CC( 't', 'w', 'o', 's' ),   /* Uncompressed 16-bit audio */
+
+    QT_CODEC_TYPE_23NI_AUDIO    = LSMASH_4CC( '2', '3', 'n', 'i' ),   /* 32-bit little endian integer uncompressed */
+    QT_CODEC_TYPE_MAC3_AUDIO    = LSMASH_4CC( 'M', 'A', 'C', '3' ),   /* MACE 3:1 */
+    QT_CODEC_TYPE_MAC6_AUDIO    = LSMASH_4CC( 'M', 'A', 'C', '6' ),   /* MACE 6:1 */
+    QT_CODEC_TYPE_NONE_AUDIO    = LSMASH_4CC( 'N', 'O', 'N', 'E' ),   /* either 'raw ' or 'twos' */
+    QT_CODEC_TYPE_QDM2_AUDIO    = LSMASH_4CC( 'Q', 'D', 'M', '2' ),   /* Qdesign music 2 */
+    QT_CODEC_TYPE_QDMC_AUDIO    = LSMASH_4CC( 'Q', 'D', 'M', 'C' ),   /* Qdesign music 1 */
+    QT_CODEC_TYPE_QCLP_AUDIO    = LSMASH_4CC( 'Q', 'c', 'l', 'p' ),   /* Qualcomm PureVoice */
+    QT_CODEC_TYPE_AC_3_AUDIO    = LSMASH_4CC( 'a', 'c', '-', '3' ),   /* Digital Audio Compression Standard (AC-3, Enhanced AC-3) */
+    QT_CODEC_TYPE_AGSM_AUDIO    = LSMASH_4CC( 'a', 'g', 's', 'm' ),   /* GSM */
+    QT_CODEC_TYPE_ALAC_AUDIO    = LSMASH_4CC( 'a', 'l', 'a', 'c' ),   /* Apple lossless audio codec */
+    QT_CODEC_TYPE_ALAW_AUDIO    = LSMASH_4CC( 'a', 'l', 'a', 'w' ),   /* a-Law 2:1 */
+    QT_CODEC_TYPE_CDX2_AUDIO    = LSMASH_4CC( 'c', 'd', 'x', '2' ),   /* CD/XA 2:1 */
+    QT_CODEC_TYPE_CDX4_AUDIO    = LSMASH_4CC( 'c', 'd', 'x', '4' ),   /* CD/XA 4:1 */
+    QT_CODEC_TYPE_DVCA_AUDIO    = LSMASH_4CC( 'd', 'v', 'c', 'a' ),   /* DV Audio */
+    QT_CODEC_TYPE_DVI_AUDIO     = LSMASH_4CC( 'd', 'v', 'i', ' ' ),   /* DVI (as used in RTP, 4:1 compression) */
+    QT_CODEC_TYPE_FL32_AUDIO    = LSMASH_4CC( 'f', 'l', '3', '2' ),   /* 32-bit float */
+    QT_CODEC_TYPE_FL64_AUDIO    = LSMASH_4CC( 'f', 'l', '6', '4' ),   /* 64-bit float */
+    QT_CODEC_TYPE_IMA4_AUDIO    = LSMASH_4CC( 'i', 'm', 'a', '4' ),   /* IMA (International Multimedia Assocation, defunct, 4:1) */
+    QT_CODEC_TYPE_IN24_AUDIO    = LSMASH_4CC( 'i', 'n', '2', '4' ),   /* 24-bit integer uncompressed */
+    QT_CODEC_TYPE_IN32_AUDIO    = LSMASH_4CC( 'i', 'n', '3', '2' ),   /* 32-bit integer uncompressed */
+    QT_CODEC_TYPE_LPCM_AUDIO    = LSMASH_4CC( 'l', 'p', 'c', 'm' ),   /* Uncompressed audio (various integer and float formats) */
+    QT_CODEC_TYPE_MP4A_AUDIO    = LSMASH_4CC( 'm', 'p', '4', 'a' ),   /* MPEG-4 Audio */
+    QT_CODEC_TYPE_RAW_AUDIO     = LSMASH_4CC( 'r', 'a', 'w', ' ' ),   /* 8-bit offset-binary uncompressed */
+    QT_CODEC_TYPE_SOWT_AUDIO    = LSMASH_4CC( 's', 'o', 'w', 't' ),   /* 16-bit little endian uncompressed */
+    QT_CODEC_TYPE_TWOS_AUDIO    = LSMASH_4CC( 't', 'w', 'o', 's' ),   /* 8-bit or 16-bit big endian uncompressed */
+    QT_CODEC_TYPE_ULAW_AUDIO    = LSMASH_4CC( 'u', 'l', 'a', 'w' ),   /* uLaw 2:1 */
+    QT_CODEC_TYPE_VDVA_AUDIO    = LSMASH_4CC( 'v', 'd', 'v', 'a' ),   /* DV audio (variable duration per video frame) */
+    QT_CODEC_TYPE_FULLMP3_AUDIO = LSMASH_4CC( '.', 'm', 'p', '3' ),   /* MPEG-1 layer 3, CBR & VBR (QT4.1 and later) */
+    QT_CODEC_TYPE_MP3_AUDIO     = 0x6D730055,                         /* MPEG-1 layer 3, CBR only (pre-QT4.1) */
+    QT_CODEC_TYPE_ADPCM2_AUDIO  = 0x6D730002,                         /* Microsoft ADPCM - ACM code 2 */
+    QT_CODEC_TYPE_ADPCM17_AUDIO = 0x6D730011,                         /* DVI/Intel IMA ADPCM - ACM code 17 */
+    QT_CODEC_TYPE_GSM49_AUDIO   = 0x6D730031,                         /* Microsoft GSM 6.10 - ACM code 49 */
+    QT_CODEC_TYPE_NOT_SPECIFIED = 0x00000000,                         /* either 'raw ' or 'twos' */
+
+    /* Video Type */
+    ISOM_CODEC_TYPE_AVC1_VIDEO  = LSMASH_4CC( 'a', 'v', 'c', '1' ),   /* Advanced Video Coding */
+    ISOM_CODEC_TYPE_AVC2_VIDEO  = LSMASH_4CC( 'a', 'v', 'c', '2' ),   /* Advanced Video Coding */
+    ISOM_CODEC_TYPE_AVCP_VIDEO  = LSMASH_4CC( 'a', 'v', 'c', 'p' ),   /* Advanced Video Coding Parameters */
+    ISOM_CODEC_TYPE_DRAC_VIDEO  = LSMASH_4CC( 'd', 'r', 'a', 'c' ),   /* Dirac Video Coder */
+    ISOM_CODEC_TYPE_ENCV_VIDEO  = LSMASH_4CC( 'e', 'n', 'c', 'v' ),   /* Encrypted/protected video */
+    ISOM_CODEC_TYPE_MJP2_VIDEO  = LSMASH_4CC( 'm', 'j', 'p', '2' ),   /* Motion JPEG 2000 */
+    ISOM_CODEC_TYPE_MP4V_VIDEO  = LSMASH_4CC( 'm', 'p', '4', 'v' ),   /* MPEG-4 Visual */
+    ISOM_CODEC_TYPE_MVC1_VIDEO  = LSMASH_4CC( 'm', 'v', 'c', '1' ),   /* Multiview coding */
+    ISOM_CODEC_TYPE_MVC2_VIDEO  = LSMASH_4CC( 'm', 'v', 'c', '2' ),   /* Multiview coding */
+    ISOM_CODEC_TYPE_S263_VIDEO  = LSMASH_4CC( 's', '2', '6', '3' ),   /* ITU H.263 video (3GPP format) */
+    ISOM_CODEC_TYPE_SVC1_VIDEO  = LSMASH_4CC( 's', 'v', 'c', '1' ),   /* Scalable Video Coding */
+    ISOM_CODEC_TYPE_VC_1_VIDEO  = LSMASH_4CC( 'v', 'c', '-', '1' ),   /* SMPTE VC-1 */
+
+    QT_CODEC_TYPE_CFHD_VIDEO    = LSMASH_4CC( 'C', 'F', 'H', 'D' ),   /* CineForm High-Definition (HD) wavelet codec */
+    QT_CODEC_TYPE_DV10_VIDEO    = LSMASH_4CC( 'D', 'V', '1', '0' ),   /* Digital Voodoo 10 bit Uncompressed 4:2:2 codec */
+    QT_CODEC_TYPE_DVOO_VIDEO    = LSMASH_4CC( 'D', 'V', 'O', 'O' ),   /* Digital Voodoo 8 bit Uncompressed 4:2:2 codec */
+    QT_CODEC_TYPE_DVOR_VIDEO    = LSMASH_4CC( 'D', 'V', 'O', 'R' ),   /* Digital Voodoo intermediate raw */
+    QT_CODEC_TYPE_DVTV_VIDEO    = LSMASH_4CC( 'D', 'V', 'T', 'V' ),   /* Digital Voodoo intermediate 2vuy */
+    QT_CODEC_TYPE_DVVT_VIDEO    = LSMASH_4CC( 'D', 'V', 'V', 'T' ),   /* Digital Voodoo intermediate v210 */
+    QT_CODEC_TYPE_HD10_VIDEO    = LSMASH_4CC( 'H', 'D', '1', '0' ),   /* Digital Voodoo 10 bit Uncompressed 4:2:2 HD codec */
+    QT_CODEC_TYPE_M105_VIDEO    = LSMASH_4CC( 'M', '1', '0', '5' ),   /* Internal format of video data supported by Matrox hardware; pixel organization is proprietary*/
+    QT_CODEC_TYPE_PNTG_VIDEO    = LSMASH_4CC( 'P', 'N', 'T', 'G' ),   /* Apple MacPaint image format */
+    QT_CODEC_TYPE_SVQ1_VIDEO    = LSMASH_4CC( 'S', 'V', 'Q', '1' ),   /* Sorenson Video 1 video */
+    QT_CODEC_TYPE_SVQ3_VIDEO    = LSMASH_4CC( 'S', 'V', 'Q', '3' ),   /* Sorenson Video 3 video */
+    QT_CODEC_TYPE_SHR0_VIDEO    = LSMASH_4CC( 'S', 'h', 'r', '0' ),   /* Generic SheerVideo codec */
+    QT_CODEC_TYPE_SHR1_VIDEO    = LSMASH_4CC( 'S', 'h', 'r', '1' ),   /* SheerVideo RGB[A] 8b - at 8 bits/channel */
+    QT_CODEC_TYPE_SHR2_VIDEO    = LSMASH_4CC( 'S', 'h', 'r', '2' ),   /* SheerVideo Y'CbCr[A] 8bv 4:4:4[:4] - at 8 bits/channel, in ITU-R BT.601-4 video range */
+    QT_CODEC_TYPE_SHR3_VIDEO    = LSMASH_4CC( 'S', 'h', 'r', '3' ),   /* SheerVideo Y'CbCr 8bv 4:2:2 - 2:1 chroma subsampling, at 8 bits/channel, in ITU-R BT.601-4 video range */
+    QT_CODEC_TYPE_SHR4_VIDEO    = LSMASH_4CC( 'S', 'h', 'r', '4' ),   /* SheerVideo Y'CbCr 8bw 4:2:2 - 2:1 chroma subsampling, at 8 bits/channel, with full-range luma and wide-range two's-complement chroma */
+    QT_CODEC_TYPE_WRLE_VIDEO    = LSMASH_4CC( 'W', 'R', 'L', 'E' ),   /* Windows BMP image format */
+    QT_CODEC_TYPE_APCH_VIDEO    = LSMASH_4CC( 'a', 'p', 'c', 'h' ),   /* Apple ProRes 422 High Quality */
+    QT_CODEC_TYPE_APCN_VIDEO    = LSMASH_4CC( 'a', 'p', 'c', 'n' ),   /* Apple ProRes 422 Standard Definition */
+    QT_CODEC_TYPE_APCS_VIDEO    = LSMASH_4CC( 'a', 'p', 'c', 's' ),   /* Apple ProRes 422 LT */
+    QT_CODEC_TYPE_APCO_VIDEO    = LSMASH_4CC( 'a', 'p', 'c', 'o' ),   /* Apple ProRes 422 Proxy */
+    QT_CODEC_TYPE_AP4H_VIDEO    = LSMASH_4CC( 'a', 'p', '4', 'h' ),   /* Apple ProRes 4444 */
+    QT_CODEC_TYPE_CIVD_VIDEO    = LSMASH_4CC( 'c', 'i', 'v', 'd' ),   /* Cinepak Video */
+    QT_CODEC_TYPE_DRAC_VIDEO    = LSMASH_4CC( 'd', 'r', 'a', 'c' ),   /* Dirac Video Coder */
+    QT_CODEC_TYPE_DVH5_VIDEO    = LSMASH_4CC( 'd', 'v', 'h', '5' ),   /* DVCPRO-HD 1080/50i */
+    QT_CODEC_TYPE_DVH6_VIDEO    = LSMASH_4CC( 'd', 'v', 'h', '6' ),   /* DVCPRO-HD 1080/60i */
+    QT_CODEC_TYPE_DVHP_VIDEO    = LSMASH_4CC( 'd', 'v', 'h', 'p' ),   /* DVCPRO-HD 720/60p */
+    QT_CODEC_TYPE_FLIC_VIDEO    = LSMASH_4CC( 'f', 'l', 'i', 'c' ),   /* Autodesk FLIC animation format */
+    QT_CODEC_TYPE_GIF_VIDEO     = LSMASH_4CC( 'g', 'i', 'f', ' ' ),   /* GIF image format */
+    QT_CODEC_TYPE_H261_VIDEO    = LSMASH_4CC( 'h', '2', '6', '1' ),   /* ITU H.261 video */
+    QT_CODEC_TYPE_H263_VIDEO    = LSMASH_4CC( 'h', '2', '6', '3' ),   /* ITU H.263 video */
+    QT_CODEC_TYPE_JPEG_VIDEO    = LSMASH_4CC( 'j', 'p', 'e', 'g' ),   /* JPEG image format */
+    QT_CODEC_TYPE_MJPA_VIDEO    = LSMASH_4CC( 'm', 'j', 'p', 'a' ),   /* Motion-JPEG (format A) */
+    QT_CODEC_TYPE_MJPB_VIDEO    = LSMASH_4CC( 'm', 'j', 'p', 'b' ),   /* Motion-JPEG (format B) */
+    QT_CODEC_TYPE_PNG_VIDEO     = LSMASH_4CC( 'p', 'n', 'g', ' ' ),   /* W3C Portable Network Graphics (PNG) */
+    QT_CODEC_TYPE_RLE_VIDEO     = LSMASH_4CC( 'r', 'l', 'e', ' ' ),   /* Apple animation codec */
+    QT_CODEC_TYPE_RPZA_VIDEO    = LSMASH_4CC( 'r', 'p', 'z', 'a' ),   /* Apple simple video 'road pizza' compression */
+    QT_CODEC_TYPE_TGA_VIDEO     = LSMASH_4CC( 't', 'g', 'a', ' ' ),   /* Truvision Targa video format */
+    QT_CODEC_TYPE_TIFF_VIDEO    = LSMASH_4CC( 't', 'i', 'f', 'f' ),   /* Tagged Image File Format (Adobe) */
+
+    /* Text Type */
+    ISOM_CODEC_TYPE_ENCT_TEXT   = LSMASH_4CC( 'e', 'n', 'c', 't' ),   /* Encrypted Text */
+    ISOM_CODEC_TYPE_TX3G_TEXT   = LSMASH_4CC( 't', 'x', '3', 'g' ),   /* Timed Text stream */
+
+    QT_CODEC_TYPE_TEXT_TEXT     = LSMASH_4CC( 't', 'e', 'x', 't' ),   /* QuickTime Text Media */
+
+    /* Hint Type */
+    ISOM_CODEC_TYPE_FDP_HINT    = LSMASH_4CC( 'f', 'd', 'p', ' ' ),   /* File delivery hints */
+    ISOM_CODEC_TYPE_M2TS_HINT   = LSMASH_4CC( 'm', '2', 't', 's' ),   /* MPEG-2 transport stream for DMB */
+    ISOM_CODEC_TYPE_PM2T_HINT   = LSMASH_4CC( 'p', 'm', '2', 't' ),   /* Protected MPEG-2 Transport */
+    ISOM_CODEC_TYPE_PRTP_HINT   = LSMASH_4CC( 'p', 'r', 't', 'p' ),   /* Protected RTP Reception */
+    ISOM_CODEC_TYPE_RM2T_HINT   = LSMASH_4CC( 'r', 'm', '2', 't' ),   /* MPEG-2 Transport Reception */
+    ISOM_CODEC_TYPE_RRTP_HINT   = LSMASH_4CC( 'r', 'r', 't', 'p' ),   /* RTP reception */
+    ISOM_CODEC_TYPE_RSRP_HINT   = LSMASH_4CC( 'r', 's', 'r', 'p' ),   /* SRTP Reception */
+    ISOM_CODEC_TYPE_RTP_HINT    = LSMASH_4CC( 'r', 't', 'p', ' ' ),   /* RTP Hints */
+    ISOM_CODEC_TYPE_SM2T_HINT   = LSMASH_4CC( 's', 'm', '2', 't' ),   /* MPEG-2 Transport Server */
+    ISOM_CODEC_TYPE_SRTP_HINT   = LSMASH_4CC( 's', 'r', 't', 'p' ),   /* SRTP Hints */
+
+    /* Metadata Type */
+    ISOM_CODEC_TYPE_IXSE_META   = LSMASH_4CC( 'i', 'x', 's', 'e' ),   /* DVB Track Level Index Track */
+    ISOM_CODEC_TYPE_METT_META   = LSMASH_4CC( 'm', 'e', 't', 't' ),   /* Text timed metadata */
+    ISOM_CODEC_TYPE_METX_META   = LSMASH_4CC( 'm', 'e', 't', 'x' ),   /* XML timed metadata */
+    ISOM_CODEC_TYPE_MLIX_META   = LSMASH_4CC( 'm', 'l', 'i', 'x' ),   /* DVB Movie level index track */
+    ISOM_CODEC_TYPE_OKSD_META   = LSMASH_4CC( 'o', 'k', 's', 'd' ),   /* OMA Keys */
+    ISOM_CODEC_TYPE_SVCM_META   = LSMASH_4CC( 's', 'v', 'c', 'M' ),   /* SVC metadata */
+    ISOM_CODEC_TYPE_TEXT_META   = LSMASH_4CC( 't', 'e', 'x', 't' ),   /* Textual meta-data with MIME type */
+    ISOM_CODEC_TYPE_URIM_META   = LSMASH_4CC( 'u', 'r', 'i', 'm' ),   /* URI identified timed metadata */
+    ISOM_CODEC_TYPE_XML_META    = LSMASH_4CC( 'x', 'm', 'l', ' ' ),   /* XML-formatted meta-data */
+
+    /* Other Type */
+    ISOM_CODEC_TYPE_ENCS_SYSTEM = LSMASH_4CC( 'e', 'n', 'c', 's' ),   /* Encrypted Systems stream */
+    ISOM_CODEC_TYPE_MP4S_SYSTEM = LSMASH_4CC( 'm', 'p', '4', 's' ),   /* MPEG-4 Systems */
+} lsmash_codec_type;
+
+typedef enum
+{
+    ISOM_LANGUAGE_CODE_ENGLISH          = LSMASH_PACK_ISO_LANGUAGE( 'e', 'n', 'g' ),
+    ISOM_LANGUAGE_CODE_FRENCH           = LSMASH_PACK_ISO_LANGUAGE( 'f', 'r', 'a' ),
+    ISOM_LANGUAGE_CODE_GERMAN           = LSMASH_PACK_ISO_LANGUAGE( 'd', 'e', 'u' ),
+    ISOM_LANGUAGE_CODE_ITALIAN          = LSMASH_PACK_ISO_LANGUAGE( 'i', 't', 'a' ),
+    ISOM_LANGUAGE_CODE_DUTCH_M          = LSMASH_PACK_ISO_LANGUAGE( 'd', 'u', 'm' ),
+    ISOM_LANGUAGE_CODE_SWEDISH          = LSMASH_PACK_ISO_LANGUAGE( 's', 'w', 'e' ),
+    ISOM_LANGUAGE_CODE_SPANISH          = LSMASH_PACK_ISO_LANGUAGE( 's', 'p', 'a' ),
+    ISOM_LANGUAGE_CODE_DANISH           = LSMASH_PACK_ISO_LANGUAGE( 'd', 'a', 'n' ),
+    ISOM_LANGUAGE_CODE_PORTUGUESE       = LSMASH_PACK_ISO_LANGUAGE( 'p', 'o', 'r' ),
+    ISOM_LANGUAGE_CODE_NORWEGIAN        = LSMASH_PACK_ISO_LANGUAGE( 'n', 'o', 'r' ),
+    ISOM_LANGUAGE_CODE_HEBREW           = LSMASH_PACK_ISO_LANGUAGE( 'h', 'e', 'b' ),
+    ISOM_LANGUAGE_CODE_JAPANESE         = LSMASH_PACK_ISO_LANGUAGE( 'j', 'p', 'n' ),
+    ISOM_LANGUAGE_CODE_ARABIC           = LSMASH_PACK_ISO_LANGUAGE( 'a', 'r', 'a' ),
+    ISOM_LANGUAGE_CODE_FINNISH          = LSMASH_PACK_ISO_LANGUAGE( 'f', 'i', 'n' ),
+    ISOM_LANGUAGE_CODE_GREEK            = LSMASH_PACK_ISO_LANGUAGE( 'e', 'l', 'l' ),
+    ISOM_LANGUAGE_CODE_ICELANDIC        = LSMASH_PACK_ISO_LANGUAGE( 'i', 's', 'l' ),
+    ISOM_LANGUAGE_CODE_MALTESE          = LSMASH_PACK_ISO_LANGUAGE( 'm', 'l', 't' ),
+    ISOM_LANGUAGE_CODE_TURKISH          = LSMASH_PACK_ISO_LANGUAGE( 't', 'u', 'r' ),
+    ISOM_LANGUAGE_CODE_CROATIAN         = LSMASH_PACK_ISO_LANGUAGE( 'h', 'r', 'v' ),
+    ISOM_LANGUAGE_CODE_CHINESE          = LSMASH_PACK_ISO_LANGUAGE( 'z', 'h', 'o' ),
+    ISOM_LANGUAGE_CODE_URDU             = LSMASH_PACK_ISO_LANGUAGE( 'u', 'r', 'd' ),
+    ISOM_LANGUAGE_CODE_HINDI            = LSMASH_PACK_ISO_LANGUAGE( 'h', 'i', 'n' ),
+    ISOM_LANGUAGE_CODE_THAI             = LSMASH_PACK_ISO_LANGUAGE( 't', 'h', 'a' ),
+    ISOM_LANGUAGE_CODE_KOREAN           = LSMASH_PACK_ISO_LANGUAGE( 'k', 'o', 'r' ),
+    ISOM_LANGUAGE_CODE_LITHUANIAN       = LSMASH_PACK_ISO_LANGUAGE( 'l', 'i', 't' ),
+    ISOM_LANGUAGE_CODE_POLISH           = LSMASH_PACK_ISO_LANGUAGE( 'p', 'o', 'l' ),
+    ISOM_LANGUAGE_CODE_HUNGARIAN        = LSMASH_PACK_ISO_LANGUAGE( 'h', 'u', 'n' ),
+    ISOM_LANGUAGE_CODE_ESTONIAN         = LSMASH_PACK_ISO_LANGUAGE( 'e', 's', 't' ),
+    ISOM_LANGUAGE_CODE_LATVIAN          = LSMASH_PACK_ISO_LANGUAGE( 'l', 'a', 'v' ),
+    ISOM_LANGUAGE_CODE_SAMI             = LSMASH_PACK_ISO_LANGUAGE( 's', 'm', 'i' ),
+    ISOM_LANGUAGE_CODE_FAROESE          = LSMASH_PACK_ISO_LANGUAGE( 'f', 'a', 'o' ),
+    ISOM_LANGUAGE_CODE_RUSSIAN          = LSMASH_PACK_ISO_LANGUAGE( 'r', 'u', 's' ),
+    ISOM_LANGUAGE_CODE_DUTCH            = LSMASH_PACK_ISO_LANGUAGE( 'n', 'l', 'd' ),
+    ISOM_LANGUAGE_CODE_IRISH            = LSMASH_PACK_ISO_LANGUAGE( 'g', 'l', 'e' ),
+    ISOM_LANGUAGE_CODE_ALBANIAN         = LSMASH_PACK_ISO_LANGUAGE( 's', 'q', 'i' ),
+    ISOM_LANGUAGE_CODE_ROMANIAN         = LSMASH_PACK_ISO_LANGUAGE( 'r', 'o', 'n' ),
+    ISOM_LANGUAGE_CODE_CZECH            = LSMASH_PACK_ISO_LANGUAGE( 'c', 'e', 's' ),
+    ISOM_LANGUAGE_CODE_SLOVAK           = LSMASH_PACK_ISO_LANGUAGE( 's', 'l', 'k' ),
+    ISOM_LANGUAGE_CODE_SLOVENIA         = LSMASH_PACK_ISO_LANGUAGE( 's', 'l', 'v' ),
+    ISOM_LANGUAGE_CODE_YIDDISH          = LSMASH_PACK_ISO_LANGUAGE( 'y', 'i', 'd' ),
+    ISOM_LANGUAGE_CODE_SERBIAN          = LSMASH_PACK_ISO_LANGUAGE( 's', 'r', 'p' ),
+    ISOM_LANGUAGE_CODE_MACEDONIAN       = LSMASH_PACK_ISO_LANGUAGE( 'm', 'k', 'd' ),
+    ISOM_LANGUAGE_CODE_BULGARIAN        = LSMASH_PACK_ISO_LANGUAGE( 'b', 'u', 'l' ),
+    ISOM_LANGUAGE_CODE_UKRAINIAN        = LSMASH_PACK_ISO_LANGUAGE( 'u', 'k', 'r' ),
+    ISOM_LANGUAGE_CODE_BELARUSIAN       = LSMASH_PACK_ISO_LANGUAGE( 'b', 'e', 'l' ),
+    ISOM_LANGUAGE_CODE_UZBEK            = LSMASH_PACK_ISO_LANGUAGE( 'u', 'z', 'b' ),
+    ISOM_LANGUAGE_CODE_KAZAKH           = LSMASH_PACK_ISO_LANGUAGE( 'k', 'a', 'z' ),
+    ISOM_LANGUAGE_CODE_AZERBAIJANI      = LSMASH_PACK_ISO_LANGUAGE( 'a', 'z', 'e' ),
+    ISOM_LANGUAGE_CODE_ARMENIAN         = LSMASH_PACK_ISO_LANGUAGE( 'h', 'y', 'e' ),
+    ISOM_LANGUAGE_CODE_GEORGIAN         = LSMASH_PACK_ISO_LANGUAGE( 'k', 'a', 't' ),
+    ISOM_LANGUAGE_CODE_MOLDAVIAN        = LSMASH_PACK_ISO_LANGUAGE( 'r', 'o', 'n' ),
+    ISOM_LANGUAGE_CODE_KIRGHIZ          = LSMASH_PACK_ISO_LANGUAGE( 'k', 'i', 'r' ),
+    ISOM_LANGUAGE_CODE_TAJIK            = LSMASH_PACK_ISO_LANGUAGE( 't', 'g', 'k' ),
+    ISOM_LANGUAGE_CODE_TURKMEN          = LSMASH_PACK_ISO_LANGUAGE( 't', 'u', 'k' ),
+    ISOM_LANGUAGE_CODE_MONGOLIAN        = LSMASH_PACK_ISO_LANGUAGE( 'm', 'o', 'n' ),
+    ISOM_LANGUAGE_CODE_PASHTO           = LSMASH_PACK_ISO_LANGUAGE( 'p', 'u', 's' ),
+    ISOM_LANGUAGE_CODE_KURDISH          = LSMASH_PACK_ISO_LANGUAGE( 'k', 'u', 'r' ),
+    ISOM_LANGUAGE_CODE_KASHMIRI         = LSMASH_PACK_ISO_LANGUAGE( 'k', 'a', 's' ),
+    ISOM_LANGUAGE_CODE_SINDHI           = LSMASH_PACK_ISO_LANGUAGE( 's', 'n', 'd' ),
+    ISOM_LANGUAGE_CODE_TIBETAN          = LSMASH_PACK_ISO_LANGUAGE( 'b', 'o', 'd' ),
+    ISOM_LANGUAGE_CODE_NEPALI           = LSMASH_PACK_ISO_LANGUAGE( 'n', 'e', 'p' ),
+    ISOM_LANGUAGE_CODE_SANSKRIT         = LSMASH_PACK_ISO_LANGUAGE( 's', 'a', 'n' ),
+    ISOM_LANGUAGE_CODE_MARATHI          = LSMASH_PACK_ISO_LANGUAGE( 'm', 'a', 'r' ),
+    ISOM_LANGUAGE_CODE_BENGALI          = LSMASH_PACK_ISO_LANGUAGE( 'b', 'e', 'n' ),
+    ISOM_LANGUAGE_CODE_ASSAMESE         = LSMASH_PACK_ISO_LANGUAGE( 'a', 's', 'm' ),
+    ISOM_LANGUAGE_CODE_GUJARATI         = LSMASH_PACK_ISO_LANGUAGE( 'g', 'u', 'j' ),
+    ISOM_LANGUAGE_CODE_PUNJABI          = LSMASH_PACK_ISO_LANGUAGE( 'p', 'a', 'n' ),
+    ISOM_LANGUAGE_CODE_ORIYA            = LSMASH_PACK_ISO_LANGUAGE( 'o', 'r', 'i' ),
+    ISOM_LANGUAGE_CODE_MALAYALAM        = LSMASH_PACK_ISO_LANGUAGE( 'm', 'a', 'l' ),
+    ISOM_LANGUAGE_CODE_KANNADA          = LSMASH_PACK_ISO_LANGUAGE( 'k', 'a', 'n' ),
+    ISOM_LANGUAGE_CODE_TAMIL            = LSMASH_PACK_ISO_LANGUAGE( 't', 'a', 'm' ),
+    ISOM_LANGUAGE_CODE_TELUGU           = LSMASH_PACK_ISO_LANGUAGE( 't', 'e', 'l' ),
+    ISOM_LANGUAGE_CODE_SINHALESE        = LSMASH_PACK_ISO_LANGUAGE( 's', 'i', 'n' ),
+    ISOM_LANGUAGE_CODE_BURMESE          = LSMASH_PACK_ISO_LANGUAGE( 'm', 'y', 'a' ),
+    ISOM_LANGUAGE_CODE_KHMER            = LSMASH_PACK_ISO_LANGUAGE( 'k', 'h', 'm' ),
+    ISOM_LANGUAGE_CODE_LAO              = LSMASH_PACK_ISO_LANGUAGE( 'l', 'a', 'o' ),
+    ISOM_LANGUAGE_CODE_VIETNAMESE       = LSMASH_PACK_ISO_LANGUAGE( 'v', 'i', 'e' ),
+    ISOM_LANGUAGE_CODE_INDONESIAN       = LSMASH_PACK_ISO_LANGUAGE( 'i', 'n', 'd' ),
+    ISOM_LANGUAGE_CODE_TAGALOG          = LSMASH_PACK_ISO_LANGUAGE( 't', 'g', 'l' ),
+    ISOM_LANGUAGE_CODE_MALAY_ROMAN      = LSMASH_PACK_ISO_LANGUAGE( 'm', 's', 'a' ),
+    ISOM_LANGUAGE_CODE_MAYAY_ARABIC     = LSMASH_PACK_ISO_LANGUAGE( 'm', 's', 'a' ),
+    ISOM_LANGUAGE_CODE_AMHARIC          = LSMASH_PACK_ISO_LANGUAGE( 'a', 'm', 'h' ),
+    ISOM_LANGUAGE_CODE_OROMO            = LSMASH_PACK_ISO_LANGUAGE( 'o', 'r', 'm' ),
+    ISOM_LANGUAGE_CODE_SOMALI           = LSMASH_PACK_ISO_LANGUAGE( 's', 'o', 'm' ),
+    ISOM_LANGUAGE_CODE_SWAHILI          = LSMASH_PACK_ISO_LANGUAGE( 's', 'w', 'a' ),
+    ISOM_LANGUAGE_CODE_KINYARWANDA      = LSMASH_PACK_ISO_LANGUAGE( 'k', 'i', 'n' ),
+    ISOM_LANGUAGE_CODE_RUNDI            = LSMASH_PACK_ISO_LANGUAGE( 'r', 'u', 'n' ),
+    ISOM_LANGUAGE_CODE_CHEWA            = LSMASH_PACK_ISO_LANGUAGE( 'n', 'y', 'a' ),
+    ISOM_LANGUAGE_CODE_MALAGASY         = LSMASH_PACK_ISO_LANGUAGE( 'm', 'l', 'g' ),
+    ISOM_LANGUAGE_CODE_ESPERANTO        = LSMASH_PACK_ISO_LANGUAGE( 'e', 'p', 'o' ),
+    ISOM_LANGUAGE_CODE_WELSH            = LSMASH_PACK_ISO_LANGUAGE( 'c', 'y', 'm' ),
+    ISOM_LANGUAGE_CODE_BASQUE           = LSMASH_PACK_ISO_LANGUAGE( 'e', 'u', 's' ),
+    ISOM_LANGUAGE_CODE_CATALAN          = LSMASH_PACK_ISO_LANGUAGE( 'c', 'a', 't' ),
+    ISOM_LANGUAGE_CODE_LATIN            = LSMASH_PACK_ISO_LANGUAGE( 'l', 'a', 't' ),
+    ISOM_LANGUAGE_CODE_QUECHUA          = LSMASH_PACK_ISO_LANGUAGE( 'q', 'u', 'e' ),
+    ISOM_LANGUAGE_CODE_GUARANI          = LSMASH_PACK_ISO_LANGUAGE( 'g', 'r', 'n' ),
+    ISOM_LANGUAGE_CODE_AYMARA           = LSMASH_PACK_ISO_LANGUAGE( 'a', 'y', 'm' ),
+    ISOM_LANGUAGE_CODE_TATAR            = LSMASH_PACK_ISO_LANGUAGE( 'c', 'r', 'h' ),
+    ISOM_LANGUAGE_CODE_UIGHUR           = LSMASH_PACK_ISO_LANGUAGE( 'u', 'i', 'g' ),
+    ISOM_LANGUAGE_CODE_DZONGKHA         = LSMASH_PACK_ISO_LANGUAGE( 'd', 'z', 'o' ),
+    ISOM_LANGUAGE_CODE_JAVANESE         = LSMASH_PACK_ISO_LANGUAGE( 'j', 'a', 'v' ),
+    ISOM_LANGUAGE_CODE_UNDEFINED        = LSMASH_PACK_ISO_LANGUAGE( 'u', 'n', 'd' ),
+} lsmash_iso_language_code;
+
+typedef enum
+{
+#define UINT16_MAX_PLUS_ONE 0x10000
+    QT_COLOR_PARAMETER_NOT_SPECIFIED = UINT16_MAX_PLUS_ONE,
+    QT_COLOR_PARAMETER_ITU_R_BT470_M,
+    QT_COLOR_PARAMETER_ITU_R_BT470_BG,
+    QT_COLOR_PARAMETER_ITU_R_BT709,
+    QT_COLOR_PARAMETER_SMPTE_170M,
+    QT_COLOR_PARAMETER_SMPTE_240M,
+    QT_COLOR_PARAMETER_SMPTE_274M,
+    QT_COLOR_PARAMETER_SMPTE_293M,
+    QT_COLOR_PARAMETER_SMPTE_296M,
+    QT_COLOR_PARAMETER_END,
+} lsmash_color_parameter;
+
+typedef enum
+{
+    QT_CHANNEL_LABEL_UNKNOWN                = 0xffffffff,   /* unknown or unspecified other use */
+    QT_CHANNEL_LABEL_UNUSED                 = 0,            /* channel is present, but has no intended use or destination */
+    QT_CHANNEL_LABEL_USE_COORDINATES        = 100,          /* channel is described by the coordinates fields. */
+
+    QT_CHANNEL_LABEL_LEFT                   = 1,
+    QT_CHANNEL_LABEL_RIGHT                  = 2,
+    QT_CHANNEL_LABEL_CENTER                 = 3,
+    QT_CHANNEL_LABEL_LFE_SCREEN             = 4,
+    QT_CHANNEL_LABEL_LEFT_SURROUND          = 5,            /* WAVE: "Back Left" */
+    QT_CHANNEL_LABEL_RIGHT_SUROUND          = 6,            /* WAVE: "Back Right" */
+    QT_CHANNEL_LABEL_LEFT_CENTER            = 7,
+    QT_CHANNEL_LABEL_RIGHT_CENTER           = 8,
+    QT_CHANNEL_LABEL_CENTER_SURROUND        = 9,            /* WAVE: "Back Center" or plain "Rear Surround" */
+    QT_CHANNEL_LABEL_LEFT_SURROUND_DIRECT   = 10,           /* WAVE: "Side Left" */
+    QT_CHANNEL_LABEL_RIGHT_SURROUND_DIRECT  = 11,           /* WAVE: "Side Right" */
+    QT_CHANNEL_LABEL_TOP_CENTER_SURROUND    = 12,
+    QT_CHANNEL_LABEL_VERTICAL_HEIGHT_LEFT   = 13,           /* WAVE: "Top Front Left" */
+    QT_CHANNEL_LABEL_VERTICAL_HEIGHT_CENTER = 14,           /* WAVE: "Top Front Center" */
+    QT_CHANNEL_LABEL_VERTICAL_HEIGHT_RIGHT  = 15,           /* WAVE: "Top Front Right" */
+
+    QT_CHANNEL_LABEL_TOP_BACK_LEFT          = 16,
+    QT_CHANNEL_LABEL_TOP_BACK_CENTER        = 17,
+    QT_CHANNEL_LABEL_TOP_BACK_RIGHT         = 18,
+
+    QT_CHANNEL_LABEL_REAR_SURROUND_LEFT     = 33,
+    QT_CHANNEL_LABEL_REAR_SURROUND_RIGHT    = 34,
+    QT_CHANNEL_LABEL_LEFT_WIDE              = 35,
+    QT_CHANNEL_LABEL_RIGHT_WIDE             = 36,
+    QT_CHANNEL_LABEL_LFE2                   = 37,
+    QT_CHANNEL_LABEL_LEFT_TOTAL             = 38,           /* matrix encoded 4 channels */
+    QT_CHANNEL_LABEL_RIGHT_TOTAL            = 39,           /* matrix encoded 4 channels */
+    QT_CHANNEL_LABEL_HEARING_IMPAIRED       = 40,
+    QT_CHANNEL_LABEL_NARRATION              = 41,
+    QT_CHANNEL_LABEL_MONO                   = 42,
+    QT_CHANNEL_LABEL_DIALOG_CENTRIC_MIX     = 43,
+
+    QT_CHANNEL_LABEL_CENTER_SURROUND_DIRECT = 44,           /* back center, non diffuse */
+
+    QT_CHANNEL_LABEL_HAPTIC                 = 45,
+
+    /* first order ambisonic channels */
+    QT_CHANNEL_LABEL_AMBISONIC_W            = 200,
+    QT_CHANNEL_LABEL_AMBISONIC_X            = 201,
+    QT_CHANNEL_LABEL_AMBISONIC_Y            = 202,
+    QT_CHANNEL_LABEL_AMBISONIC_Z            = 203,
+
+    /* Mid/Side Recording */
+    QT_CHANNEL_LABEL_MS_MID                 = 204,
+    QT_CHANNEL_LABEL_MS_SIDE                = 205,
+
+    /* X-Y Recording */
+    QT_CHANNEL_LABEL_XY_X                   = 206,
+    QT_CHANNEL_LABEL_XY_Y                   = 207,
+
+    /* other */
+    QT_CHANNEL_LABEL_HEADPHONES_LEFT        = 301,
+    QT_CHANNEL_LABEL_HEADPHONES_RIGHT       = 302,
+    QT_CHANNEL_LABEL_CLICK_TRACK            = 304,
+    QT_CHANNEL_LABEL_FOREIGN_LANGUAGE       = 305,
+
+    /* generic discrete channel */
+    QT_CHANNEL_LABEL_DISCRETE               = 400,
+
+    /* numbered discrete channel */
+    QT_CHANNEL_LABEL_DISCRETE_0             = (1<<16),
+    QT_CHANNEL_LABEL_DISCRETE_1             = (1<<16) | 1,
+    QT_CHANNEL_LABEL_DISCRETE_2             = (1<<16) | 2,
+    QT_CHANNEL_LABEL_DISCRETE_3             = (1<<16) | 3,
+    QT_CHANNEL_LABEL_DISCRETE_4             = (1<<16) | 4,
+    QT_CHANNEL_LABEL_DISCRETE_5             = (1<<16) | 5,
+    QT_CHANNEL_LABEL_DISCRETE_6             = (1<<16) | 6,
+    QT_CHANNEL_LABEL_DISCRETE_7             = (1<<16) | 7,
+    QT_CHANNEL_LABEL_DISCRETE_8             = (1<<16) | 8,
+    QT_CHANNEL_LABEL_DISCRETE_9             = (1<<16) | 9,
+    QT_CHANNEL_LABEL_DISCRETE_10            = (1<<16) | 10,
+    QT_CHANNEL_LABEL_DISCRETE_11            = (1<<16) | 11,
+    QT_CHANNEL_LABEL_DISCRETE_12            = (1<<16) | 12,
+    QT_CHANNEL_LABEL_DISCRETE_13            = (1<<16) | 13,
+    QT_CHANNEL_LABEL_DISCRETE_14            = (1<<16) | 14,
+    QT_CHANNEL_LABEL_DISCRETE_15            = (1<<16) | 15,
+    QT_CHANNEL_LABEL_DISCRETE_65535         = (1<<16) | 65535,
+} lsmash_channel_label;
+
+typedef enum
+{
+    QT_CHANNEL_BIT_LEFT                   = 1,
+    QT_CHANNEL_BIT_RIGHT                  = 1<<1,
+    QT_CHANNEL_BIT_CENTER                 = 1<<2,
+    QT_CHANNEL_BIT_LFE_SCREEN             = 1<<3,
+    QT_CHANNEL_BIT_LEFT_SURROUND          = 1<<4,       /* WAVE: "Back Left" */
+    QT_CHANNEL_BIT_RIGHT_SURROUND         = 1<<5,       /* WAVE: "Back Right" */
+    QT_CHANNEL_BIT_LEFT_CENTER            = 1<<6,
+    QT_CHANNEL_BIT_RIGHT_CENTER           = 1<<7,
+    QT_CHANNEL_BIT_CENTER_SURROUND        = 1<<8,       /* WAVE: "Back Center" */
+    QT_CHANNEL_BIT_LEFT_SURROUND_DIRECT   = 1<<9,       /* WAVE: "Side Left" */
+    QT_CHANNEL_BIT_RIGHT_SURROUND_DIRECT  = 1<<10,      /* WAVE: "Side Right" */
+    QT_CHANNEL_BIT_TOP_CENTER_SURROUND    = 1<<11,
+    QT_CHANNEL_BIT_VERTICAL_HEIGHT_LEFT   = 1<<12,      /* WAVE: "Top Front Left" */
+    QT_CHANNEL_BIT_VERTICAL_HEIGHT_CENTER = 1<<13,      /* WAVE: "Top Front Center" */
+    QT_CHANNEL_BIT_VERTICAL_HEIGHT_RIGHT  = 1<<14,      /* WAVE: "Top Front Right" */
+    QT_CHANNEL_BIT_TOP_BACK_LEFT          = 1<<15,
+    QT_CHANNEL_BIT_TOP_BACK_CENTER        = 1<<16,
+    QT_CHANNEL_BIT_TOP_BACK_RIGHT         = 1<<17,
+    QT_CHANNEL_BIT_FULL                   = 0x3ffff,
+} lsmash_channel_bitmap;
+
+typedef enum
+{
+    QT_CHANNEL_FLAGS_ALL_OFF                 = 0,
+    QT_CHANNEL_FLAGS_RECTANGULAR_COORDINATES = 1,
+    QT_CHANNEL_FLAGS_SPHERICAL_COORDINATES   = 1<<1,
+    QT_CHANNEL_FLAGS_METERS                  = 1<<2,
+} lsmash_channel_flags;
+
+typedef enum
+{
+    /* indices for accessing the coordinates array in Channel Descriptions */
+    /* for rectangulare coordinates */
+    QT_CHANNEL_COORDINATES_LEFT_RIGHT = 0,      /* Negative is left and positive is right. */
+    QT_CHANNEL_COORDINATES_BACK_FRONT = 1,      /* Negative is back and positive is front. */
+    QT_CHANNEL_COORDINATES_DOWN_UP    = 2,      /* Negative is below ground level, 0 is ground level, and positive is above ground level. */
+    /* for spherical coordinates */
+    QT_CHANNEL_COORDINATES_AZIMUTH    = 0,      /* 0 is front center, positive is right, negative is left. This is measured in degrees. */
+    QT_CHANNEL_COORDINATES_ELEVATION  = 1,      /* +90 is zenith, 0 is horizontal, -90 is nadir. This is measured in degrees. */
+    QT_CHANNEL_COORDINATES_DISTANCE   = 2,      /* The units are described by flags. */
+} lsmash_channel_coordinates_index;
+
+typedef enum
+{
+    /* channel abbreviations:
+     * L - left
+     * R - right
+     * C - center
+     * Ls - left surround
+     * Rs - right surround
+     * Cs - center surround
+     * Rls - rear left surround
+     * Rrs - rear right surround
+     * Lw - left wide
+     * Rw - right wide
+     * Lsd - left surround direct
+     * Rsd - right surround direct
+     * Lc - left center
+     * Rc - right center
+     * Ts - top surround
+     * Vhl - vertical height left
+     * Vhc - vertical height center
+     * Vhr - vertical height right
+     * Lt - left matrix total. for matrix encoded stereo.
+     * Rt - right matrix total. for matrix encoded stereo. */
+
+    /*  General layouts */
+    QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS = 0,                 /* use the array of Channel Descriptions to define the mapping. */
+    QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP       = 1<<16,             /* use the bitmap to define the mapping. */
+
+    QT_CHANNEL_LAYOUT_MONO                     = (100<<16) | 1,     /* a standard mono stream */
+    QT_CHANNEL_LAYOUT_STEREO                   = (101<<16) | 2,     /* a standard stereo stream (L R) - implied playback */
+    QT_CHANNEL_LAYOUT_STEREO_HEADPHONES        = (102<<16) | 2,     /* a standard stereo stream (L R) - implied headphone playback */
+    QT_CHANNEL_LAYOUT_MATRIX_STEREO            = (103<<16) | 2,     /* a matrix encoded stereo stream (Lt, Rt) */
+    QT_CHANNEL_LAYOUT_MID_SIDE                 = (104<<16) | 2,     /* mid/side recording */
+    QT_CHANNEL_LAYOUT_XY                       = (105<<16) | 2,     /* coincident mic pair (often 2 figure 8's) */
+    QT_CHANNEL_LAYOUT_BINAURAL                 = (106<<16) | 2,     /* binaural stereo (left, right) */
+    QT_CHANNEL_LAYOUT_AMBISONIC_B_FORMAT       = (107<<16) | 4,     /* W, X, Y, Z */
+
+    QT_CHANNEL_LAYOUT_QUADRAPHONIC             = (108<<16) | 4,     /* front left, front right, back left, back right */
+
+    QT_CHANNEL_LAYOUT_PENTAGONAL               = (109<<16) | 5,     /* left, right, rear left, rear right, center */
+
+    QT_CHANNEL_LAYOUT_HEXAGONAL                = (110<<16) | 6,     /* left, right, rear left, rear right, center, rear */
+
+    QT_CHANNEL_LAYOUT_OCTAGONAL                = (111<<16) | 8,     /* front left, front right, rear left, rear right,
+                                                                     * front center, rear center, side left, side right */
+
+    QT_CHANNEL_LAYOUT_CUBE                     = (112<<16) | 8,     /* left, right, rear left, rear right,
+                                                                     * top left, top right, top rear left, top rear right */
+
+    /*  MPEG defined layouts */
+    QT_CHANNEL_LAYOUT_MPEG_1_0                 = QT_CHANNEL_LAYOUT_MONO,            /* C */
+    QT_CHANNEL_LAYOUT_MPEG_2_0                 = QT_CHANNEL_LAYOUT_STEREO,          /* L R */
+    QT_CHANNEL_LAYOUT_MPEG_3_0_A               = (113<<16) | 3,                     /* L R C */
+    QT_CHANNEL_LAYOUT_MPEG_3_0_B               = (114<<16) | 3,                     /* C L R */
+    QT_CHANNEL_LAYOUT_MPEG_4_0_A               = (115<<16) | 4,                     /* L R C Cs */
+    QT_CHANNEL_LAYOUT_MPEG_4_0_B               = (116<<16) | 4,                     /* C L R Cs */
+    QT_CHANNEL_LAYOUT_MPEG_5_0_A               = (117<<16) | 5,                     /* L R C Ls Rs */
+    QT_CHANNEL_LAYOUT_MPEG_5_0_B               = (118<<16) | 5,                     /* L R Ls Rs C */
+    QT_CHANNEL_LAYOUT_MPEG_5_0_C               = (119<<16) | 5,                     /* L C R Ls Rs */
+    QT_CHANNEL_LAYOUT_MPEG_5_0_D               = (120<<16) | 5,                     /* C L R Ls Rs */
+    QT_CHANNEL_LAYOUT_MPEG_5_1_A               = (121<<16) | 6,                     /* L R C LFE Ls Rs */
+    QT_CHANNEL_LAYOUT_MPEG_5_1_B               = (122<<16) | 6,                     /* L R Ls Rs C LFE */
+    QT_CHANNEL_LAYOUT_MPEG_5_1_C               = (123<<16) | 6,                     /* L C R Ls Rs LFE */
+    QT_CHANNEL_LAYOUT_MPEG_5_1_D               = (124<<16) | 6,                     /* C L R Ls Rs LFE */
+    QT_CHANNEL_LAYOUT_MPEG_6_1_A               = (125<<16) | 7,                     /* L R C LFE Ls Rs Cs */
+    QT_CHANNEL_LAYOUT_MPEG_7_1_A               = (126<<16) | 8,                     /* L R C LFE Ls Rs Lc Rc */
+    QT_CHANNEL_LAYOUT_MPEG_7_1_B               = (127<<16) | 8,                     /* C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC Table 3.1) */
+    QT_CHANNEL_LAYOUT_MPEG_7_1_C               = (128<<16) | 8,                     /* L R C LFE Ls Rs Rls Rrs */
+    QT_CHANNEL_LAYOUT_EMAGIC_DEFAULT_7_1       = (129<<16) | 8,                     /* L R Ls Rs C LFE Lc Rc */
+    QT_CHANNEL_LAYOUT_SMPTE_DTV                = (130<<16) | 8,                     /* L R C LFE Ls Rs Lt Rt */
+
+    /*  ITU defined layouts */
+    QT_CHANNEL_LAYOUT_ITU_1_0                  = QT_CHANNEL_LAYOUT_MONO,            /* C */
+    QT_CHANNEL_LAYOUT_ITU_2_0                  = QT_CHANNEL_LAYOUT_STEREO,          /* L R */
+
+    QT_CHANNEL_LAYOUT_ITU_2_1                  = (131<<16) | 3,                     /* L R Cs */
+    QT_CHANNEL_LAYOUT_ITU_2_2                  = (132<<16) | 4,                     /* L R Ls Rs */
+    QT_CHANNEL_LAYOUT_ITU_3_0                  = QT_CHANNEL_LAYOUT_MPEG_3_0_A,      /* L R C */
+    QT_CHANNEL_LAYOUT_ITU_3_1                  = QT_CHANNEL_LAYOUT_MPEG_4_0_A,      /* L R C Cs */
+
+    QT_CHANNEL_LAYOUT_ITU_3_2                  = QT_CHANNEL_LAYOUT_MPEG_5_0_A,      /* L R C Ls Rs */
+    QT_CHANNEL_LAYOUT_ITU_3_2_1                = QT_CHANNEL_LAYOUT_MPEG_5_1_A,      /* L R C LFE Ls Rs */
+    QT_CHANNEL_LAYOUT_ITU_3_4_1                = QT_CHANNEL_LAYOUT_MPEG_7_1_C,      /* L R C LFE Ls Rs Rls Rrs */
+
+    /* DVD defined layouts */
+    QT_CHANNEL_LAYOUT_DVD_0                    = QT_CHANNEL_LAYOUT_MONO,            /* C (mono) */
+    QT_CHANNEL_LAYOUT_DVD_1                    = QT_CHANNEL_LAYOUT_STEREO,          /* L R */
+    QT_CHANNEL_LAYOUT_DVD_2                    = QT_CHANNEL_LAYOUT_ITU_2_1,         /* L R Cs */
+    QT_CHANNEL_LAYOUT_DVD_3                    = QT_CHANNEL_LAYOUT_ITU_2_2,         /* L R Ls Rs */
+    QT_CHANNEL_LAYOUT_DVD_4                    = (133<<16) | 3,                     /* L R LFE */
+    QT_CHANNEL_LAYOUT_DVD_5                    = (134<<16) | 4,                     /* L R LFE Cs */
+    QT_CHANNEL_LAYOUT_DVD_6                    = (135<<16) | 5,                     /* L R LFE Ls Rs */
+    QT_CHANNEL_LAYOUT_DVD_7                    = QT_CHANNEL_LAYOUT_MPEG_3_0_A,      /* L R C */
+    QT_CHANNEL_LAYOUT_DVD_8                    = QT_CHANNEL_LAYOUT_MPEG_4_0_A,      /* L R C Cs */
+    QT_CHANNEL_LAYOUT_DVD_9                    = QT_CHANNEL_LAYOUT_MPEG_5_0_A,      /* L R C Ls Rs */
+    QT_CHANNEL_LAYOUT_DVD_10                   = (136<<16) | 4,                     /* L R C LFE */
+    QT_CHANNEL_LAYOUT_DVD_11                   = (137<<16) | 5,                     /* L R C LFE Cs */
+    QT_CHANNEL_LAYOUT_DVD_12                   = QT_CHANNEL_LAYOUT_MPEG_5_1_A,      /* L R C LFE Ls Rs */
+    /* 13 through 17 are duplicates of 8 through 12. */
+    QT_CHANNEL_LAYOUT_DVD_13                   = QT_CHANNEL_LAYOUT_DVD_8,           /* L R C Cs */
+    QT_CHANNEL_LAYOUT_DVD_14                   = QT_CHANNEL_LAYOUT_DVD_9,           /* L R C Ls Rs */
+    QT_CHANNEL_LAYOUT_DVD_15                   = QT_CHANNEL_LAYOUT_DVD_10,          /* L R C LFE */
+    QT_CHANNEL_LAYOUT_DVD_16                   = QT_CHANNEL_LAYOUT_DVD_11,          /* L R C LFE Cs */
+    QT_CHANNEL_LAYOUT_DVD_17                   = QT_CHANNEL_LAYOUT_DVD_12,          /* L R C LFE Ls Rs */
+    QT_CHANNEL_LAYOUT_DVD_18                   = (138<<16) | 5,                     /* L R Ls Rs LFE */
+    QT_CHANNEL_LAYOUT_DVD_19                   = QT_CHANNEL_LAYOUT_MPEG_5_0_B,      /* L R Ls Rs C */
+    QT_CHANNEL_LAYOUT_DVD_20                   = QT_CHANNEL_LAYOUT_MPEG_5_1_B,      /* L R Ls Rs C LFE */
+
+    /* These are the symmetrical layouts. */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_4             = QT_CHANNEL_LAYOUT_QUADRAPHONIC,
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_5             = QT_CHANNEL_LAYOUT_PENTAGONAL,
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_6             = QT_CHANNEL_LAYOUT_HEXAGONAL,
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_8             = QT_CHANNEL_LAYOUT_OCTAGONAL,
+    /* These are the surround-based layouts. */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_5_0           = QT_CHANNEL_LAYOUT_MPEG_5_0_B,      /* L R Ls Rs C */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_6_0           = (139<<16) | 6,                     /* L R Ls Rs C Cs */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_7_0           = (140<<16) | 7,                     /* L R Ls Rs C Rls Rrs */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_7_0_FRONT     = (148<<16) | 7,                     /* L R Ls Rs C Lc Rc */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_5_1           = QT_CHANNEL_LAYOUT_MPEG_5_1_A,      /* L R C LFE Ls Rs */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_6_1           = QT_CHANNEL_LAYOUT_MPEG_6_1_A,      /* L R C LFE Ls Rs Cs */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_7_1           = QT_CHANNEL_LAYOUT_MPEG_7_1_C,      /* L R C LFE Ls Rs Rls Rrs */
+    QT_CHANNEL_LAYOUT_AUDIO_UNIT_7_1_FRONT     = QT_CHANNEL_LAYOUT_MPEG_7_1_A,      /* L R C LFE Ls Rs Lc Rc */
+
+    QT_CHANNEL_LAYOUT_AAC_3_0                  = QT_CHANNEL_LAYOUT_MPEG_3_0_B,      /* C L R */
+    QT_CHANNEL_LAYOUT_AAC_QUADRAPHONIC         = QT_CHANNEL_LAYOUT_QUADRAPHONIC,    /* L R Ls Rs */
+    QT_CHANNEL_LAYOUT_AAC_4_0                  = QT_CHANNEL_LAYOUT_MPEG_4_0_B,      /* C L R Cs */
+    QT_CHANNEL_LAYOUT_AAC_5_0                  = QT_CHANNEL_LAYOUT_MPEG_5_0_D,      /* C L R Ls Rs */
+    QT_CHANNEL_LAYOUT_AAC_5_1                  = QT_CHANNEL_LAYOUT_MPEG_5_1_D,      /* C L R Ls Rs Lfe */
+    QT_CHANNEL_LAYOUT_AAC_6_0                  = (141<<16) | 6,                     /* C L R Ls Rs Cs */
+    QT_CHANNEL_LAYOUT_AAC_6_1                  = (142<<16) | 7,                     /* C L R Ls Rs Cs Lfe */
+    QT_CHANNEL_LAYOUT_AAC_7_0                  = (143<<16) | 7,                     /* C L R Ls Rs Rls Rrs */
+    QT_CHANNEL_LAYOUT_AAC_7_1                  = QT_CHANNEL_LAYOUT_MPEG_7_1_B,      /* C Lc Rc L R Ls Rs Lfe */
+    QT_CHANNEL_LAYOUT_AAC_OCTAGONAL            = (144<<16) | 8,                     /* C L R Ls Rs Rls Rrs Cs */
+
+    QT_CHANNEL_LAYOUT_TMH_10_2_STD             = (145<<16) | 16,                    /* L R C Vhc Lsd Rsd Ls Rs Vhl Vhr Lw Rw Csd Cs LFE1 LFE2 */
+    QT_CHANNEL_LAYOUT_TMH_10_2_FULL            = (146<<16) | 21,                    /* TMH_10_2_std plus: Lc Rc HI VI Haptic */
+
+    QT_CHANNEL_LAYOUT_AC3_1_0_1                = (149<<16) | 2,                     /* C LFE */
+    QT_CHANNEL_LAYOUT_AC3_3_0                  = (150<<16) | 3,                     /* L C R */
+    QT_CHANNEL_LAYOUT_AC3_3_1                  = (151<<16) | 4,                     /* L C R Cs */
+    QT_CHANNEL_LAYOUT_AC3_3_0_1                = (152<<16) | 4,                     /* L C R LFE */
+    QT_CHANNEL_LAYOUT_AC3_2_1_1                = (153<<16) | 4,                     /* L R Cs LFE */
+    QT_CHANNEL_LAYOUT_AC3_3_1_1                = (154<<16) | 5,                     /* L C R Cs LFE */
+
+    QT_CHANNEL_LAYOUT_EAC_6_0_A                = (155<<16) | 6,                     /* L C R Ls Rs Cs */
+    QT_CHANNEL_LAYOUT_EAC_7_0_A                = (156<<16) | 7,                     /* L C R Ls Rs Rls Rrs */
+
+    QT_CHANNEL_LAYOUT_EAC3_6_1_A               = (157<<16) | 7,                     /* L C R Ls Rs LFE Cs */
+    QT_CHANNEL_LAYOUT_EAC3_6_1_B               = (158<<16) | 7,                     /* L C R Ls Rs LFE Ts */
+    QT_CHANNEL_LAYOUT_EAC3_6_1_C               = (159<<16) | 7,                     /* L C R Ls Rs LFE Vhc */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_A               = (160<<16) | 8,                     /* L C R Ls Rs LFE Rls Rrs */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_B               = (161<<16) | 8,                     /* L C R Ls Rs LFE Lc Rc */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_C               = (162<<16) | 8,                     /* L C R Ls Rs LFE Lsd Rsd */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_D               = (163<<16) | 8,                     /* L C R Ls Rs LFE Lw Rw */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_E               = (164<<16) | 8,                     /* L C R Ls Rs LFE Vhl Vhr */
+
+    QT_CHANNEL_LAYOUT_EAC3_7_1_F               = (165<<16) | 8,                     /* L C R Ls Rs LFE Cs Ts */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_G               = (166<<16) | 8,                     /* L C R Ls Rs LFE Cs Vhc */
+    QT_CHANNEL_LAYOUT_EAC3_7_1_H               = (167<<16) | 8,                     /* L C R Ls Rs LFE Ts Vhc */
+
+    QT_CHANNEL_LAYOUT_DISCRETE_IN_ORDER        = 147<<16,                           /* needs to be ORed with the actual number of channels */  
+    QT_CHANNEL_LAYOUT_UNKNOWN                  = 0xffff0000,                        /* needs to be ORed with the actual number of channels */
+} lsmash_channel_layout_tag;
+
+typedef enum
+{
+    /* In MP4 and/or ISO base media file format, if in a presentation all tracks have neither track_in_movie nor track_in_preview set,
+     * then all tracks shall be treated as if both flags were set on all tracks. */
+    ISOM_TRACK_ENABLED      = 0x000001,     /* Track_enabled: Indicates that the track is enabled.
+                                             * A disabled track is treated as if it were not present. */
+    ISOM_TRACK_IN_MOVIE     = 0x000002,     /* Track_in_movie: Indicates that the track is used in the presentation. */
+    ISOM_TRACK_IN_PREVIEW   = 0x000004,     /* Track_in_preview: Indicates that the track is used when previewing the presentation. */
+
+    QT_TRACK_IN_POSTER      = 0x000008,     /* Track_in_poster: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format) */
+} lsmash_track_mode;
+
+typedef enum
+{
+    ISOM_SCALING_METHOD_FILL    = 1,
+    ISOM_SCALING_METHOD_HIDDEN  = 2,
+    ISOM_SCALING_METHOD_MEET    = 3,
+    ISOM_SCALING_METHOD_SLICE_X = 4,
+    ISOM_SCALING_METHOD_SLICE_Y = 5,
+} lsmash_scaling_method;
+
+typedef enum
+{
+    ISOM_EDIT_MODE_NORMAL   = 1<<16,
+    ISOM_EDIT_MODE_DWELL    = 0,
+    ISOM_EDIT_MODE_EMPTY    = -1,
+} lsmash_edit_mode;
+
+typedef enum
+{
+    /* allow_ealier */
+    QT_SAMPLE_EARLIER_PTS_ALLOWED       = 1,
+    /* leading */
+    ISOM_SAMPLE_LEADING_UNKNOWN         = 0,
+    ISOM_SAMPLE_IS_UNDECODABLE_LEADING  = 1,
+    ISOM_SAMPLE_IS_NOT_LEADING          = 2,
+    ISOM_SAMPLE_IS_DECODABLE_LEADING    = 3,
+    /* independent */
+    ISOM_SAMPLE_INDEPENDENCY_UNKNOWN    = 0,
+    ISOM_SAMPLE_IS_NOT_INDEPENDENT      = 1,
+    ISOM_SAMPLE_IS_INDEPENDENT          = 2,
+    /* disposable */
+    ISOM_SAMPLE_DISPOSABLE_UNKNOWN      = 0,
+    ISOM_SAMPLE_IS_NOT_DISPOSABLE       = 1,
+    ISOM_SAMPLE_IS_DISPOSABLE           = 2,
+    /* redundant */
+    ISOM_SAMPLE_REDUNDANCY_UNKNOWN      = 0,
+    ISOM_SAMPLE_HAS_REDUNDANCY          = 1,
+    ISOM_SAMPLE_HAS_NO_REDUNDANCY       = 2,
+} lsmash_sample_dependency;
+
+/* objectTypeIndication */
+typedef enum {
+    MP4SYS_OBJECT_TYPE_Forbidden                          = 0x00,   /* Forbidden */
+    MP4SYS_OBJECT_TYPE_Systems_ISO_14496_1                = 0x01,   /* Systems ISO/IEC 14496-1 */
+    /* For all 14496-1 streams unless specifically indicated to the contrary.
+       Scene Description scenes, which are identified with StreamType=0x03, using
+       this object type value shall use the BIFSConfig. */
+    MP4SYS_OBJECT_TYPE_Systems_ISO_14496_1_BIFSv2         = 0x02,   /* Systems ISO/IEC 14496-1 */
+    /* This object type shall be used, with StreamType=0x03, for Scene
+       Description streams that use the BIFSv2Config.
+       Its use with other StreamTypes is reserved. */
+
+    MP4SYS_OBJECT_TYPE_Interaction_Stream                 = 0x03,   /* Interaction Stream */
+    MP4SYS_OBJECT_TYPE_Extended_BIFS                      = 0x04,   /* Extended BIFS */
+    /* Used, with StreamType=0x03, for Scene Description streams that use the BIFSConfigEx; its use with
+       other StreamTypes is reserved. (Was previously reserved for MUCommandStream but not used for that purpose.) */
+    MP4SYS_OBJECT_TYPE_AFX_Stream                         = 0x05,   /* AFX Stream */
+    /* Used, with StreamType=0x03, for Scene Description streams that use the AFXConfig;
+       its use with other StreamTypes is reserved. */
+    MP4SYS_OBJECT_TYPE_Font_Data_Stream                   = 0x06,   /* Font Data Stream */
+    MP4SYS_OBJECT_TYPE_Synthetised_Texture                = 0x07,   /* Synthetised Texture */
+    MP4SYS_OBJECT_TYPE_Text_Stream                        = 0x08,   /* Text Stream */
+
+    MP4SYS_OBJECT_TYPE_Visual_ISO_14496_2                 = 0x20,   /* Visual ISO/IEC 14496-2 */
+    MP4SYS_OBJECT_TYPE_Visual_H264_ISO_14496_10           = 0x21,   /* Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 */
+    /* The actual object types are within the DecoderSpecificInfo and defined in H.264 | 14496-10. */
+    MP4SYS_OBJECT_TYPE_Parameter_Sets_H_264_ISO_14496_10  = 0x22,   /* Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 */
+    /* The actual object types are within the DecoderSpecificInfo and defined in 14496-2. */
+
+    MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3                  = 0x40,   /* Audio ISO/IEC 14496-3 (MPEG-4 Audio) */
+    //MP4SYS_OBJECT_TYPE_MP4A_AUDIO = 0x40,
+    /* The actual object types are defined in 14496-3 and are in the DecoderSpecificInfo as specified in 14496-3. */
+
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Simple_Profile  = 0x60,   /* Visual ISO/IEC 13818-2 Simple Profile (MPEG-2 Video) */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Main_Profile    = 0x61,   /* Visual ISO/IEC 13818-2 Main Profile */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_SNR_Profile     = 0x62,   /* Visual ISO/IEC 13818-2 SNR Profile */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Spatial_Profile = 0x63,   /* Visual ISO/IEC 13818-2 Spatial Profile */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_High_Profile    = 0x64,   /* Visual ISO/IEC 13818-2 High Profile */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_422_Profile     = 0x65,   /* Visual ISO/IEC 13818-2 422 Profile */
+    MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile     = 0x66,   /* Audio ISO/IEC 13818-7 Main Profile (MPEG-2 Audio)(AAC) */
+    MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile       = 0x67,   /* Audio ISO/IEC 13818-7 LowComplexity Profile */
+    MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile      = 0x68,   /* Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile */
+    /* For streams kinda 13818-7 the decoder specific information consists of the ADIF header if present
+       (or none if not present) and an access unit is a "raw_data_block()" as defined in 13818-7. */
+    MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3                  = 0x69,   /* Audio ISO/IEC 13818-3 (MPEG-2 BC-Audio)(redefined MPEG-1 Audio in MPEG-2) */
+    /* For streams kinda 13818-3 the decoder specific information is empty since all necessary data is in the bitstream frames itself.
+       The access units in this case are the "frame()" bitstream element as is defined in 11172-3. */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_11172_2                 = 0x6A,   /* Visual ISO/IEC 11172-2 (MPEG-1 Video) */
+    MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3                  = 0x6B,   /* Audio ISO/IEC 11172-3 (MPEG-1 Audio) */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_10918_1                 = 0x6C,   /* Visual ISO/IEC 10918-1 (JPEG) */
+    MP4SYS_OBJECT_TYPE_PNG                                = 0x6D,   /* Portable Network Graphics */
+    MP4SYS_OBJECT_TYPE_Visual_ISO_15444_1_JPEG2000        = 0x6E,   /* Visual ISO/IEC 15444-1 (JPEG 2000) */
+
+    /* FIXME: rename these symbols to be explaining, rather than based on four cc */
+    MP4SYS_OBJECT_TYPE_EVRC_AUDIO                         = 0xA0,   /* EVRC Voice */
+    MP4SYS_OBJECT_TYPE_SSMV_AUDIO                         = 0xA1,   /* SMV Voice */
+    MP4SYS_OBJECT_TYPE_3GPP2_CMF                          = 0xA2,   /* 3GPP2 Compact Multimedia Format (CMF) */
+    MP4SYS_OBJECT_TYPE_VC_1_VIDEO                         = 0xA3,   /* SMPTE VC-1 Video */
+    MP4SYS_OBJECT_TYPE_DRAC_VIDEO                         = 0xA4,   /* Dirac Video Coder */
+    MP4SYS_OBJECT_TYPE_AC_3_AUDIO                         = 0xA5,   /* AC-3 Audio */
+    MP4SYS_OBJECT_TYPE_EC_3_AUDIO                         = 0xA6,   /* Enhanced AC-3 audio */
+    MP4SYS_OBJECT_TYPE_DRA1_AUDIO                         = 0xA7,   /* DRA Audio */
+    MP4SYS_OBJECT_TYPE_G719_AUDIO                         = 0xA8,   /* ITU G.719 Audio */
+    MP4SYS_OBJECT_TYPE_DTSC_AUDIO                         = 0xA9,   /* DTS Coherent Acoustics audio */
+    MP4SYS_OBJECT_TYPE_DTSH_AUDIO                         = 0xAA,   /* DTS-HD High Resolution Audio */
+    MP4SYS_OBJECT_TYPE_DTSL_AUDIO                         = 0xAB,   /* DTS-HD Master Audio */
+    MP4SYS_OBJECT_TYPE_DTSE_AUDIO                         = 0xAC,   /* DTS Express low bit rate audio, also known as DTS LBR */
+
+    MP4SYS_OBJECT_TYPE_SQCP_AUDIO                         = 0xE1,   /* 13K Voice */
+
+    MP4SYS_OBJECT_TYPE_NONE                               = 0xFF,   /* no object type specified */
+    /* Streams with this value with a StreamType indicating a systems stream (values 1,2,3,6,7,8,9)
+       shall be treated as if the ObjectTypeIndication had been set to 0x01. */
+} lsmash_mp4sys_object_type_indication;
+
+/* streamType */
+typedef enum {
+    MP4SYS_STREAM_TYPE_Forbidden               = 0x00,  /* Forbidden */
+    MP4SYS_STREAM_TYPE_ObjectDescriptorStream  = 0x01,  /* ObjectDescriptorStream */
+    MP4SYS_STREAM_TYPE_ClockReferenceStream    = 0x02,  /* ClockReferenceStream */
+    MP4SYS_STREAM_TYPE_SceneDescriptionStream  = 0x03,  /* SceneDescriptionStream */
+    MP4SYS_STREAM_TYPE_VisualStream            = 0x04,  /* VisualStream */
+    MP4SYS_STREAM_TYPE_AudioStream             = 0x05,  /* AudioStream */
+    MP4SYS_STREAM_TYPE_MPEG7Stream             = 0x06,  /* MPEG7Stream */
+    MP4SYS_STREAM_TYPE_IPMPStream              = 0x07,  /* IPMPStream */
+    MP4SYS_STREAM_TYPE_ObjectContentInfoStream = 0x08,  /* ObjectContentInfoStream */
+    MP4SYS_STREAM_TYPE_MPEGJStream             = 0x09,  /* MPEGJStream */
+    MP4SYS_STREAM_TYPE_InteractionStream       = 0x0A,  /* Interaction Stream */
+    MP4SYS_STREAM_TYPE_IPMPToolStream          = 0x0B,  /* IPMPToolStream */
+    MP4SYS_STREAM_TYPE_FontDataStream          = 0x0C,  /* FontDataStream */
+    MP4SYS_STREAM_TYPE_StreamingText           = 0x0D,  /* StreamingText */
+} lsmash_mp4sys_stream_type;
+
+/* Audio Object Types */
+typedef enum {
+    MP4A_AUDIO_OBJECT_TYPE_NULL                           = 0,
+    MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN                       = 1,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_AAC_LC                         = 2,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_AAC_SSR                        = 3,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_AAC_LTP                        = 4,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_SBR                            = 5,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_AAC_scalable                   = 6,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_TwinVQ                         = 7,  /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_CELP                           = 8,  /* ISO/IEC 14496-3 subpart 3 */
+    MP4A_AUDIO_OBJECT_TYPE_HVXC                           = 9,  /* ISO/IEC 14496-3 subpart 2 */
+    MP4A_AUDIO_OBJECT_TYPE_TTSI                           = 12, /* ISO/IEC 14496-3 subpart 6 */
+    MP4A_AUDIO_OBJECT_TYPE_Main_synthetic                 = 13, /* ISO/IEC 14496-3 subpart 5 */
+    MP4A_AUDIO_OBJECT_TYPE_Wavetable_synthesis            = 14, /* ISO/IEC 14496-3 subpart 5 */
+    MP4A_AUDIO_OBJECT_TYPE_General_MIDI                   = 15, /* ISO/IEC 14496-3 subpart 5 */
+    MP4A_AUDIO_OBJECT_TYPE_Algorithmic_Synthesis_Audio_FX = 16, /* ISO/IEC 14496-3 subpart 5 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC                      = 17, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP                     = 19, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable                = 20, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ                     = 21, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_BSAC                        = 22, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD                      = 23, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_CELP                        = 24, /* ISO/IEC 14496-3 subpart 3 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_HVXC                        = 25, /* ISO/IEC 14496-3 subpart 2 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_HILN                        = 26, /* ISO/IEC 14496-3 subpart 7 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_Parametric                  = 27, /* ISO/IEC 14496-3 subpart 2 and 7 */
+    MP4A_AUDIO_OBJECT_TYPE_SSC                            = 28, /* ISO/IEC 14496-3 subpart 8 */
+    MP4A_AUDIO_OBJECT_TYPE_PS                             = 29, /* ISO/IEC 14496-3 subpart 8 */
+    MP4A_AUDIO_OBJECT_TYPE_MPEG_Surround                  = 30, /* ISO/IEC 23003-1 */
+    MP4A_AUDIO_OBJECT_TYPE_ESCAPE                         = 31,
+    MP4A_AUDIO_OBJECT_TYPE_Layer_1                        = 32, /* ISO/IEC 14496-3 subpart 9 */
+    MP4A_AUDIO_OBJECT_TYPE_Layer_2                        = 33, /* ISO/IEC 14496-3 subpart 9 */
+    MP4A_AUDIO_OBJECT_TYPE_Layer_3                        = 34, /* ISO/IEC 14496-3 subpart 9 */
+    MP4A_AUDIO_OBJECT_TYPE_DST                            = 35, /* ISO/IEC 14496-3 subpart 10 */
+    MP4A_AUDIO_OBJECT_TYPE_ALS                            = 36, /* ISO/IEC 14496-3 subpart 11 */
+    MP4A_AUDIO_OBJECT_TYPE_SLS                            = 37, /* ISO/IEC 14496-3 subpart 12 */
+    MP4A_AUDIO_OBJECT_TYPE_SLS_non_core                   = 38, /* ISO/IEC 14496-3 subpart 12 */
+    MP4A_AUDIO_OBJECT_TYPE_ER_AAC_ELD                     = 39, /* ISO/IEC 14496-3 subpart 4 */
+    MP4A_AUDIO_OBJECT_TYPE_SMR_Simple                     = 40, /* ISO/IEC 14496-23 */
+    MP4A_AUDIO_OBJECT_TYPE_SMR_Main                       = 41, /* ISO/IEC 14496-23 */
+    MP4A_AUDIO_OBJECT_TYPE_SAOC                           = 43, /* ISO/IEC 23003-2 */
+} lsmash_mp4a_AudioObjectType;
+
+/* see ISO/IEC 14496-3 Signaling of SBR, SBR Signaling and Corresponding Decoder Behavior */
+typedef enum {
+    MP4A_AAC_SBR_NOT_SPECIFIED = 0x0,   /* not mention to SBR presence. Implicit signaling. */
+    MP4A_AAC_SBR_NONE,                  /* explicitly signals SBR does not present. Useless in general. */
+    MP4A_AAC_SBR_BACKWARD_COMPATIBLE,   /* explicitly signals SBR present. Recommended method to signal SBR. */
+    MP4A_AAC_SBR_HIERARCHICAL           /* SBR exists. SBR dedicated method. */
+} lsmash_mp4a_aac_sbr_mode;
+
+typedef enum
+{
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_NONE         = 0,        /* not random access point */
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC         = 1,        /* sync sample */
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP   = 1,        /* the first sample of a closed GOP */
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP     = 2,        /* the first sample of an open GOP */
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP  = 3,        /* the first sample of an open or closed GOP */
+    ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY     = 4,        /* starting point of gradual decoder refresh */
+
+    QT_SAMPLE_RANDOM_ACCESS_TYPE_NONE           = 0,        /* not random access point */
+    QT_SAMPLE_RANDOM_ACCESS_TYPE_SYNC           = 1,        /* sync sample */
+    QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC   = 2,        /* partial sync sample */
+    QT_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP     = 1,        /* the first sample of a closed GOP */
+    QT_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP       = 2,        /* the first sample of an open GOP */
+} lsmash_random_access_type;
+
+typedef enum
+{
+    /* UTF String type */
+    ITUNES_METADATA_TYPE_ALBUM_NAME                 = LSMASH_4CC( 0xA9, 'a', 'l', 'b' ),    /* Album Name */
+    ITUNES_METADATA_TYPE_ARTIST                     = LSMASH_4CC( 0xA9, 'A', 'R', 'T' ),    /* Artist */
+    ITUNES_METADATA_TYPE_USER_COMMENT               = LSMASH_4CC( 0xA9, 'c', 'm', 't' ),    /* User Comment */
+    ITUNES_METADATA_TYPE_RELEASE_DATE               = LSMASH_4CC( 0xA9, 'd', 'a', 'y' ),    /* YYYY-MM-DD format string (may be incomplete, i.e. only year) */
+    ITUNES_METADATA_TYPE_ENCODED_BY                 = LSMASH_4CC( 0xA9, 'e', 'n', 'c' ),    /* Person or company that encoded the recording */
+    ITUNES_METADATA_TYPE_USER_GENRE                 = LSMASH_4CC( 0xA9, 'g', 'e', 'n' ),    /* User Genre user-specified string */
+    ITUNES_METADATA_TYPE_0XA9_GROUPING              = LSMASH_4CC( 0xA9, 'g', 'r', 'p' ),    /* Grouping */
+    ITUNES_METADATA_TYPE_LYRICS                     = LSMASH_4CC( 0xA9, 'l', 'y', 'r' ),    /* Lyrics */
+    ITUNES_METADATA_TYPE_TITLE                      = LSMASH_4CC( 0xA9, 'n', 'a', 'm' ),    /* Title / Song Name */
+    ITUNES_METADATA_TYPE_TRACK_SUBTITLE             = LSMASH_4CC( 0xA9, 's', 't', '3' ),    /* Track Sub-Title */
+    ITUNES_METADATA_TYPE_ENCODING_TOOL              = LSMASH_4CC( 0xA9, 't', 'o', 'o' ),    /* Software which encoded the recording */
+    ITUNES_METADATA_TYPE_COMPOSER                   = LSMASH_4CC( 0xA9, 'w', 'r', 't' ),    /* Composer */
+    ITUNES_METADATA_TYPE_ALBUM_ARTIST               = LSMASH_4CC( 'a', 'A', 'R', 'T' ),     /* Artist for the whole album (if different than the individual tracks) */
+    ITUNES_METADATA_TYPE_PODCAST_CATEGORY           = LSMASH_4CC( 'c', 'a', 't', 'g' ),     /* Podcast Category */
+    ITUNES_METADATA_TYPE_COPYRIGHT                  = LSMASH_4CC( 'c', 'p', 'r', 't' ),     /* Copyright */
+    ITUNES_METADATA_TYPE_DESCRIPTION                = LSMASH_4CC( 'd', 'e', 's', 'c' ),     /* Description (limited to 255 bytes) */
+    ITUNES_METADATA_TYPE_GROUPING                   = LSMASH_4CC( 'g', 'r', 'u', 'p' ),     /* Grouping */
+    ITUNES_METADATA_TYPE_PODCAST_KEYWORD            = LSMASH_4CC( 'k', 'e', 'y', 'w' ),     /* Podcast Keywords */
+    ITUNES_METADATA_TYPE_LONG_DESCRIPTION           = LSMASH_4CC( 'l', 'd', 'e', 's' ),     /* Long Description */
+    ITUNES_METADATA_TYPE_PURCHASE_DATE              = LSMASH_4CC( 'p', 'u', 'r', 'd' ),     /* Purchase Date */
+    ITUNES_METADATA_TYPE_TV_EPISODE_ID              = LSMASH_4CC( 't', 'v', 'e', 'n' ),     /* TV Episode ID */
+    ITUNES_METADATA_TYPE_TV_NETWORK                 = LSMASH_4CC( 't', 'v', 'n', 'n' ),     /* TV Network Name */
+    ITUNES_METADATA_TYPE_TV_SHOW_NAME               = LSMASH_4CC( 't', 'v', 's', 'h' ),     /* TV Show Name */
+    ITUNES_METADATA_TYPE_ITUNES_PURCHASE_ACCOUNT_ID = LSMASH_4CC( 'a', 'p', 'I', 'D' ),     /* iTunes Account Used for Purchase */
+
+    /* Integer type
+     * (X): X means length of bytes */
+    ITUNES_METADATA_TYPE_EPISODE_GLOBAL_ID          = LSMASH_4CC( 'e', 'g', 'i', 'd' ),     /* (1) Episode Global Unique ID */
+    ITUNES_METADATA_TYPE_PREDEFINED_GENRE           = LSMASH_4CC( 'g', 'n', 'r', 'e' ),     /* (4) Pre-defined Genre / Enumerated value from ID3 tag set, plus 1 */
+    ITUNES_METADATA_TYPE_PODCAST_URL                = LSMASH_4CC( 'p', 'u', 'r', 'l' ),     /* (?) Podcast URL */
+    ITUNES_METADATA_TYPE_CONTENT_RATING             = LSMASH_4CC( 'r', 't', 'n', 'g' ),     /* (1) Content Rating / Does song have explicit content? 0: none, 2: clean, 4: explicit */
+    ITUNES_METADATA_TYPE_MEDIA_TYPE                 = LSMASH_4CC( 's', 't', 'i', 'k' ),     /* (1) Media Type */
+    ITUNES_METADATA_TYPE_BEATS_PER_MINUTE           = LSMASH_4CC( 't', 'm', 'p', 'o' ),     /* (2) Beats Per Minute */
+    ITUNES_METADATA_TYPE_TV_EPISODE                 = LSMASH_4CC( 't', 'v', 'e', 's' ),     /* (4) TV Episode */
+    ITUNES_METADATA_TYPE_TV_SEASON                  = LSMASH_4CC( 't', 'v', 's', 'n' ),     /* (4) TV Season */
+    ITUNES_METADATA_TYPE_ITUNES_ACCOUNT_TYPE        = LSMASH_4CC( 'a', 'k', 'I', 'D' ),     /* (1) iTunes Account Type / 0: iTunes, 1: AOL */
+    ITUNES_METADATA_TYPE_ITUNES_ARTIST_ID           = LSMASH_4CC( 'a', 't', 'I', 'D' ),     /* (4) iTunes Artist ID */
+    ITUNES_METADATA_TYPE_ITUNES_COMPOSER_ID         = LSMASH_4CC( 'c', 'm', 'I', 'D' ),     /* (4) iTunes Composer ID */
+    ITUNES_METADATA_TYPE_ITUNES_CATALOG_ID          = LSMASH_4CC( 'c', 'n', 'I', 'D' ),     /* (4) iTunes Catalog ID */
+    ITUNES_METADATA_TYPE_ITUNES_TV_GENRE_ID         = LSMASH_4CC( 'g', 'e', 'I', 'D' ),     /* (4) iTunes TV Genre ID */
+    ITUNES_METADATA_TYPE_ITUNES_PLAYLIST_ID         = LSMASH_4CC( 'p', 'l', 'I', 'D' ),     /* (8) iTunes Playlist ID */
+    ITUNES_METADATA_TYPE_ITUNES_COUNTRY_CODE        = LSMASH_4CC( 's', 'f', 'I', 'D' ),     /* (4) iTunes Country Code */
+
+    /* Boolean type */
+    ITUNES_METADATA_TYPE_DISC_COMPILATION           = LSMASH_4CC( 'c', 'p', 'i', 'l' ),     /* Disc Compilation / Is disc part of a compilation? 0: No, 1: Yes */
+    ITUNES_METADATA_TYPE_HIGH_DEFINITION_VIDEO      = LSMASH_4CC( 'h', 'd', 'v', 'd' ),     /* High Definition Video / 0: No, 1: Yes */
+    ITUNES_METADATA_TYPE_PODCAST                    = LSMASH_4CC( 'p', 'c', 's', 't' ),     /* Podcast / 0: No, 1: Yes */
+    ITUNES_METADATA_TYPE_GAPLESS_PLAYBACK           = LSMASH_4CC( 'p', 'g', 'a', 'p' ),     /* Gapless Playback / 0: insert gap, 1: no gap */
+
+    /* Binary type */
+    ITUNES_METADATA_TYPE_COVER_ART                  = LSMASH_4CC( 'c', 'o', 'v', 'r' ),     /* One or more cover art images */
+    ITUNES_METADATA_TYPE_DISC_NUMBER                = LSMASH_4CC( 'd', 'i', 's', 'k' ),     /* Disc Number */
+    ITUNES_METADATA_TYPE_TRACH_NUMBER               = LSMASH_4CC( 't', 'r', 'k', 'n' ),     /* Track Number */
+
+    /* Custom type */
+    ITUNES_METADATA_TYPE_CUSTOM                     = LSMASH_4CC( '-', '-', '-', '-' ),     /* Custom */
+} lsmash_itunes_metadata_type;
+
+/* public data types */
+typedef struct
+{
+    uint32_t complete;      /* recovery point: the identifier necessary for the recovery from its starting point to be completed */
+    uint32_t identifier;    /* the identifier for samples
+                             * If this identifier equals a certain identifier of recovery point,
+                             * then this sample is the recovery point of the earliest group in the pool. */
+} lsmash_post_roll_t;
+
+typedef struct
+{
+    uint16_t distance;      /* pre-roll distance for representing audio decoder delay derived from composition
+                             * For example, typical AAC encoding uses a transform over consecutive sets of 2048 audio samples,
+                             * applied every 1024 audio samples (MDCTs are overlapped).
+                             * For correct audio to be decoded, both transforms for any period of 1024 audio samples are needed.
+                             * For this AAC stream, therefore, shall be set to 1 (one AAC access unit).
+                             * Note: the number of priming audio sample i.e. encoder delay shall be represented by media_time in edit. */
+} lsmash_pre_roll_t;
+
+typedef struct
+{
+    uint8_t allow_earlier;
+    uint8_t leading;
+    uint8_t independent;
+    uint8_t disposable;
+    uint8_t redundant;
+    lsmash_random_access_type random_access_type;
+    lsmash_post_roll_t        post_roll;
+    lsmash_pre_roll_t         pre_roll;
+} lsmash_sample_property_t;
+
+typedef struct
+{
+    uint32_t length;
+    uint8_t *data;
+    uint64_t dts;
+    uint64_t cts;
+    uint32_t index;
+    lsmash_sample_property_t prop;
+} lsmash_sample_t;
+
+typedef struct
+{
+    uint64_t dts;
+    uint64_t cts;
+} lsmash_media_ts_t;
+
+typedef struct
+{
+    uint32_t sample_count;
+    lsmash_media_ts_t *timestamp;
+} lsmash_media_ts_list_t;
+
+/* */
+typedef int (*lsmash_adhoc_remux_callback)( void* param, uint64_t done, uint64_t total );
+typedef struct {
+    uint64_t buffer_size;
+    lsmash_adhoc_remux_callback func;
+    void* param;
+} lsmash_adhoc_remux_t;
+
+/* L-SMASH's original structure, summary of audio/video stream configuration */
+/* NOTE: For audio, currently assuming AAC-LC. */
+
+#define LSMASH_BASE_SUMMARY \
+    lsmash_codec_type sample_type;      /* Codec type */ \
+    lsmash_mp4sys_object_type_indication object_type_indication; \
+    lsmash_mp4sys_stream_type stream_type; \
+    void *exdata;               /* typically payload of DecoderSpecificInfo (that's called AudioSpecificConfig in mp4a) */ \
+    uint32_t exdata_length;     /* length of exdata */ \
+    uint32_t max_au_length;     /* buffer length for 1 access unit, typically max size of 1 audio/video frame */
+
+typedef struct
+{
+    LSMASH_BASE_SUMMARY
+} lsmash_summary_t;
+
+typedef struct
+{
+    LSMASH_BASE_SUMMARY
+    // mp4a_audioProfileLevelIndication pli;   /* I wonder we should have this or not. */
+    lsmash_mp4a_AudioObjectType aot;        /* Detailed codec type. If not mp4a, just ignored. */
+    uint32_t frequency;                     /* Even if the stream is HE-AAC v1/SBR, this is base AAC's one. */
+    uint32_t channels;                      /* Even if the stream is HE-AAC v2/SBR+PS, this is base AAC's one. */
+    uint32_t bit_depth;                     /* If AAC, AAC stream itself does not mention to accuracy (bit_depth of decoded PCM data), we assume 16bit. */
+    uint32_t samples_in_frame;              /* Even if the stream is HE-AAC/aacPlus/SBR(+PS), this is base AAC's one, so 1024. */
+    lsmash_mp4a_aac_sbr_mode sbr_mode;      /* SBR treatment. Currently we always set this as mp4a_AAC_SBR_NOT_SPECIFIED(Implicit signaling).
+                                             * User can set this for treatment in other way. */
+    lsmash_channel_layout_tag layout_tag;   /* channel layout */
+    lsmash_channel_bitmap bitmap;           /* Only available when layout_tag is set to QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP. */
+    /* LPCM descriptions */
+    uint8_t sample_format;      /* 0: integer, 1: floating point */
+    uint8_t endianness;         /* 0: big endian, 1: little endian */
+    uint8_t signedness;         /* 0: unsigned, 1: signed / This is only valid when sample format is integer. */
+    uint8_t packed;             /* 0: unpacked, 1: packed i.e. the sample bits occupy the entire available bits for the channel */
+    uint8_t alignment;          /* 0: low bit placement, 1: high bit placement / This is only valid when unpacked. */
+    uint8_t interleaved;        /* 0: non-interleaved, 1: interleaved i.e. the samples for each channels are stored in one stream */
+} lsmash_audio_summary_t;
+
+typedef struct
+{
+    LSMASH_BASE_SUMMARY
+    // mp4sys_visualProfileLevelIndication pli;    /* I wonder we should have this or not. */
+    // lsmash_mp4v_VideoObjectType vot;            /* Detailed codec type. If not mp4v, just ignored. */
+    uint32_t timescale;                         /* media timescale
+                                                 * User can't set this parameter manually. */
+    uint32_t timebase;                          /* increment unit of timestamp
+                                                 * User can't set this parameter manually. */
+    uint8_t vfr;                                /* whether a stream is assumed as variable frame rate
+                                                 * User can't set this parameter manually. */
+    uint8_t full_range;
+    uint32_t width;                             /* pixel counts of width samples have */
+    uint32_t height;                            /* pixel counts of height samples have */
+    uint32_t crop_top;
+    uint32_t crop_left;
+    uint32_t crop_bottom;
+    uint32_t crop_right;
+    uint32_t par_h;                             /* horizontal factor of pixel aspect ratio */
+    uint32_t par_v;                             /* vertical factor of pixel aspect ratio */
+    lsmash_scaling_method scaling_method;       /* If not set, video samples are scaled into the visual presentation region to fill it. */
+    lsmash_color_parameter primaries;
+    lsmash_color_parameter transfer;
+    lsmash_color_parameter matrix;
+} lsmash_video_summary_t;
+
+typedef struct
+{
+    lsmash_media_type handler_type;     /* the nature of the media
+                                         * You can't change handler_type through this parameter manually. */
+    uint32_t timescale;                 /* media timescale: timescale for this media */
+    uint64_t duration;                  /* the duration of this media, expressed in the media timescale
+                                         * You can't set this parameter manually. */
+    uint8_t  roll_grouping;             /* roll recovery grouping present
+                                         * Require 'avc1' brand, or ISO Base Media File Format version 2 or later. */
+    uint8_t  rap_grouping;              /* random access point grouping present
+                                         * Require ISO Base Media File Format version 6 or later. */
+    /* Use either type of language code. */
+    uint16_t MAC_language;              /* Macintosh language code for this media */
+    uint16_t ISO_language;              /* ISO 639-2/T language code for this media */
+    /* human-readable name for the track type (for debugging and inspection purposes) */
+    char *media_handler_name;
+    char *data_handler_name;
+    /* Any user shouldn't use the following parameters. */
+    PRIVATE char media_handler_name_shadow[256];
+    PRIVATE char data_handler_name_shadow[256];
+} lsmash_media_parameters_t;
+
+typedef struct
+{
+    lsmash_track_mode mode;
+    uint32_t track_ID;              /* an integer that uniquely identifies the track
+                                     * Don't set to value already used except for zero value.
+                                     * Zero value don't override established track_ID. */
+    uint64_t duration;              /* the duration of this track expressed in the movie timescale units
+                                     * If there is any edit, your setting is ignored. */
+    int16_t  alternate_group;       /* an integer that specifies a group or collection of tracks
+                                     * If this field is not 0, it should be the same for tracks that contain alternate data for one another
+                                     * and different for tracks belonging to different such groups.
+                                     * Only one track within an alternate group should be played or streamed at any one time.
+                                     * Note: alternate_group is ignored when a file is read as an MPEG-4. */
+    /* The following parameters are ignored when a file is read as an MPEG-4 or 3GPP file format. */
+    int16_t  video_layer;           /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */
+    int16_t  audio_volume;          /* fixed point 8.8 number. 0x0100 is full volume. */
+    int32_t  matrix[9];             /* transformation matrix for the video
+                                     * Each value represents, in order, a, b, u, c, d, v, x, y and w.
+                                     * All the values in a matrix are stored as 16.16 fixed-point values,
+                                     * except for u, v and w, which are stored as 2.30 fixed-point values.
+                                     * Not all derived specifications use matrices.
+                                     * If a matrix is used, the point (p, q) is transformed into (p', q') using the matrix as follows:
+                                     *             | a b u |
+                                     * (p, q, 1) * | c d v | = z * (p', q', 1)
+                                     *             | x y w |
+                                     * p' = (a * p + c * q + x) / z; q' = (b * p + d * q + y) / z; z = u * p + v * q + w
+                                     * Note: transformation matrix is applied after scaling to display size up to display_width and display_height. */
+    /* visual presentation region size */
+    uint32_t display_width;         /* visual presentation region size of horizontal direction as fixed point 16.16 number. */
+    uint32_t display_height;        /* visual presentation region size of vertical direction as fixed point 16.16 number. */
+    /* */
+    uint8_t  aperture_modes;        /* track aperture modes present
+                                     * This feature is only available under QuickTime file format.
+                                     * Automatically disabled if multiple sample description is present or scaling method is specified. */
+} lsmash_track_parameters_t;
+
+typedef struct
+{
+    lsmash_brand_type  major_brand;         /* the best used brand */
+    lsmash_brand_type *brands;              /* the list of compatible brands */
+    uint32_t number_of_brands;              /* the number of compatible brands used in the movie */
+    uint32_t minor_version;                 /* minor version of best used brand */
+    double   max_chunk_duration;            /* max duration per chunk in seconds. 0.5 is default value. */
+    double   max_async_tolerance;           /* max tolerance, in seconds, for amount of interleaving asynchronization between tracks.
+                                             * 2.0 is default value. At least twice of max_chunk_duration is used. */
+    uint64_t max_chunk_size;                /* max size per chunk in bytes. 4*1024*1024 (4MiB) is default value. */
+    uint64_t max_read_size;                 /* max size of reading from a chunk at a time. 4*1024*1024 (4MiB) is default value. */
+    uint32_t timescale;                     /* movie timescale: timescale for the entire presentation */
+    uint64_t duration;                      /* the duration, expressed in movie timescale, of the longest track
+                                             * You can't set this parameter manually. */
+    uint32_t number_of_tracks;              /* the number of tracks in the movie
+                                             * You can't set this parameter manually. */
+    /* The following parameters are recognized only when a file is read as an Apple MPEG-4 or QuickTime file fromat. */
+    int32_t  playback_rate;                 /* fixed point 16.16 number. 0x00010000 is normal forward playback and default value. */
+    int32_t  playback_volume;               /* fixed point 8.8 number. 0x0100 is full volume and default value. */
+    int32_t  preview_time;                  /* the time value in the movie at which the preview begins */
+    int32_t  preview_duration;              /* the duration of the movie preview in movie timescale units */
+    int32_t  poster_time;                   /* the time value of the time of the movie poster */
+    /* Any user shouldn't use the following parameter. */
+    PRIVATE lsmash_brand_type brands_shadow[50];
+} lsmash_movie_parameters_t;
+
+typedef enum
+{
+    LSMASH_BOOLEAN_FALSE = 0,
+    LSMASH_BOOLEAN_TRUE  = 1
+} lsmash_boolean_t;
+
+typedef struct lsmash_root_tag lsmash_root_t;
+typedef void lsmash_itunes_metadata_list_t;
+
+/* public functions */
+int lsmash_add_sps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *sps, uint32_t sps_size );
+int lsmash_add_pps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *pps, uint32_t pps_size );
+int lsmash_add_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size );
+int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_type, void* summary );
+
+int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number );
+int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length );
+
+int lsmash_write_free( lsmash_root_t *root );
+
+uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID );
+uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID );
+uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID );
+uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID );
+uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID );
+uint32_t lsmash_get_movie_timescale( lsmash_root_t *root );
+
+int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number,
+                           uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility,
+                           uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne,
+                           uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 );
+int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta );
+int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length );
+int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name );
+
+int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name );
+int lsmash_create_object_descriptor( lsmash_root_t *root );
+
+int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint64_t segment_duration, int64_t media_time, int32_t media_rate );
+int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint64_t segment_duration, int64_t media_time, int32_t media_rate );
+int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID );
+
+int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID );
+int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID );
+int lsmash_update_movie_modification_time( lsmash_root_t *root );
+int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta );
+
+uint16_t lsmash_pack_iso_language( char *iso_language );
+
+lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode );
+void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param );
+int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param );
+int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param );
+uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type );
+uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number );
+void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param );
+int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param );
+int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param );
+void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param );
+int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param );
+int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param );
+lsmash_sample_t *lsmash_create_sample( uint32_t size );
+int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size );
+void lsmash_delete_sample( lsmash_sample_t *sample );
+int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample );
+int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta );
+int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux );
+void lsmash_discard_boxes( lsmash_root_t *root );
+void lsmash_destroy_root( lsmash_root_t *root );
+
+int lsmash_create_fragment_movie( lsmash_root_t *root );
+int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration );
+
+void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID );
+void lsmash_delete_tyrant_chapter( lsmash_root_t *root );
+
+/* track_ID == 0 means copyright declaration applies to the entire presentation, not an entire track. */
+int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice );
+
+/* When type is specified as ITUNES_METADATA_TYPE_CUSTOM, meaning is mandatory while name is optionally valid.
+ * Otherwise, meaning and name are just ignored. */
+int lsmash_set_itunes_metadata_string( lsmash_root_t *root, lsmash_itunes_metadata_type type, char *value, char *meaning, char *name );
+int lsmash_set_itunes_metadata_integer( lsmash_root_t *root, lsmash_itunes_metadata_type type, uint64_t value, char *meaning, char *name );
+int lsmash_set_itunes_metadata_boolean( lsmash_root_t *root, lsmash_itunes_metadata_type type, lsmash_boolean_t value, char *meaning, char *name );
+
+#ifdef LSMASH_DEMUXER_ENABLED
+int lsmash_print_movie( lsmash_root_t *root );
+
+int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID );
+int lsmash_copy_decoder_specific_info( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID );
+int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID );
+void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID );
+int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta );
+int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts );
+lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number );
+int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number );
+
+lsmash_itunes_metadata_list_t *lsmash_export_itunes_metadata( lsmash_root_t *root );
+int lsmash_import_itunes_metadata( lsmash_root_t *root, lsmash_itunes_metadata_list_t *list );
+void lsmash_destroy_itunes_metadata( lsmash_itunes_metadata_list_t *list );
+
+int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list );
+int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list );
+int lsmash_get_media_timeline_shift( lsmash_root_t *root, uint32_t track_ID, int32_t *timeline_shift );
+#endif
+
+/* to facilitate to make exdata (typically DecoderSpecificInfo or AudioSpecificConfig). */
+int lsmash_setup_AudioSpecificConfig( lsmash_audio_summary_t* summary );
+int lsmash_summary_add_exdata( lsmash_summary_t *summary, void* exdata, uint32_t exdata_length );
+
+lsmash_summary_t *lsmash_create_summary( lsmash_mp4sys_stream_type stream_type );
+void lsmash_cleanup_summary( lsmash_summary_t *summary );
+
+#undef PRIVATE
+
+#endif
diff --git a/output/mp4/mp4a.c b/output/mp4/mp4a.c
new file mode 100644
index 0000000..d440c7d
--- /dev/null
+++ b/output/mp4/mp4a.c
@@ -0,0 +1,901 @@
+/*****************************************************************************
+ * mp4a.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#define MP4A_INTERNAL
+#include "mp4a.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/***************************************************************************
+    implementation of part of ISO/IEC 14496-3 (ISO/IEC 14496-1 relevant)
+***************************************************************************/
+
+/* ISO/IEC 14496-3 samplingFrequencyIndex */
+/* ISO/IEC 14496-3 Sampling frequency mapping */
+const uint32_t mp4a_sampling_frequency_table[13][5] = {
+    /* threshold, exact, idx_for_ga, idx_for_sbr, idx */
+    {      92017, 96000,        0x0,         0xF, 0x0 }, /* SBR is not allowed */
+    {      75132, 88200,        0x1,         0xF, 0x1 }, /* SBR is not allowed */
+    {      55426, 64000,        0x2,         0xF, 0x2 }, /* SBR is not allowed */
+    {      46009, 48000,        0x3,         0x0, 0x3 },
+    {      37566, 44100,        0x4,         0x1, 0x4 },
+    {      27713, 32000,        0x5,         0x2, 0x5 },
+    {      23004, 24000,        0x6,         0x3, 0x6 },
+    {      18783, 22050,        0x7,         0x4, 0x7 },
+    {      13856, 16000,        0x8,         0x5, 0x8 },
+    {      11502, 12000,        0x9,         0x6, 0x9 },
+    {       9391, 11025,        0xA,         0x7, 0xA },
+    {       8000,  8000,        0xB,         0x8, 0xB },
+    {          0,  7350,        0xB,         0xF, 0xC } /* samplingFrequencyIndex for GASpecificConfig is 0xB (same as 8000Hz). */
+};
+
+/* ISO/IEC 14496-3 Interface to ISO/IEC 14496-1 (MPEG-4 Systems), Syntax of AudioSpecificConfig(). */
+/* This structure is represent of regularized AudioSpecificConfig. */
+/* for actual definition, see Syntax of GetAudioObjectType() for audioObjectType and extensionAudioObjectType. */
+typedef struct {
+    lsmash_mp4a_aac_sbr_mode sbr_mode; /* L-SMASH's original, including sbrPresent flag. */
+    lsmash_mp4a_AudioObjectType audioObjectType;
+    uint8_t samplingFrequencyIndex;
+    uint32_t samplingFrequency;
+    uint8_t channelConfiguration;
+    lsmash_mp4a_AudioObjectType extensionAudioObjectType;
+    uint8_t extensionSamplingFrequencyIndex;
+    uint8_t extensionSamplingFrequency;
+    /* if( audioObjectType in
+        #[ 1, 2, 3, 4, 6, 7, *17, *19, *20, *21, *22, *23 ] // GASpecificConfig, AAC relatives and TwinVQ, BSAC
+        [ 8 ]              // CelpSpecificConfig, not supported
+        [ 9 ]              // HvxcSpecificConfig, not supported
+        [ 12 ]             // TTSSpecificConfig, not supported
+        [ 13, 14, 15, 16 ] // StructuredAudioSpecificConfig, notsupported
+        [ 24 ]             // ErrorResilientCelpSpecificConfig, notsupported
+        [ 25 ]             // ErrorResilientHvxcSpecificConfig, notsupported
+        [ 26, 27 ]         // ParametricSpecificConfig, notsupported
+        [ 28 ]             // SSCSpecificConfig, notsupported
+        #[ 32, 33, 34 ]     // MPEG_1_2_SpecificConfig
+        [ 35 ]             // DSTSpecificConfig, notsupported
+    ){ */
+        void* deepAudioSpecificConfig; // L-SMASH's original name, reperesents such as GASpecificConfig. */
+    /* } */
+    /*
+    // error resilient stuff, not supported
+    if( audioObjectType in [17, 19, 20, 21, 22, 23, 24, 25, 26, 27] ){
+        uint8_t epConfig // 2bit
+        if( epConfig == 2 || epConfig == 3 ){
+            ErrorProtectionSpecificConfig();
+        }
+        if( epConfig == 3 ){
+            uint8_t directMapping;  // 1bit, currently always 1.
+            if( !directMapping ){
+                // tbd
+            }
+        }
+    }
+    */
+} mp4a_AudioSpecificConfig_t;
+
+/* ISO/IEC 14496-3 Decoder configuration (GASpecificConfig), Syntax of GASpecificConfig() */
+/* ISO/IEC 14496-3 GASpecificConfig(), Sampling frequency mapping */
+typedef struct {
+    uint8_t frameLengthFlag; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */
+    uint8_t dependsOnCoreCoder; /* FIXME: used if scalable AAC. */
+    /*
+    if( dependsOnCoreCoder ){
+        uint16_t coreCoderDelay; // 14bits
+    }
+    */
+    uint8_t extensionFlag; /* 1bit, 1 if ErrorResilience */
+    /* if( !channelConfiguration ){ */
+        void* program_config_element;  /* currently not supported. */
+    /* } */
+    /*
+    // we do not support AAC_scalable
+    if( (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_AAC_scalable) || (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable) ){
+        uint8_t layerNr; // 3bits
+    }
+    */
+    /*
+    // we do not support special AACs
+    if( extensionFlag ){
+        if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_BSAC ){
+            uint8_t numOfSubFrame; // 5bits
+            uint8_t layer_length;  // 11bits
+        }
+        if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP
+            || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD
+        ){
+            uint8_t aacSectionDataResilienceFlag; // 1bit
+            uint8_t aacScalefactorDataResilienceFlag; // 1bit
+            uint8_t aacSpectralDataResilienceFlag; // 1bit
+        }
+        uint8_t extensionFlag3; // 1bit
+        if( extensionFlag3 ){
+            // tbd in version 3
+        }
+    }
+    */
+} mp4a_GASpecificConfig_t;
+
+/* ISO/IEC 14496-3 MPEG_1_2_SpecificConfig */
+typedef struct {
+    uint8_t extension; /* shall be 0. */
+} mp4a_MPEG_1_2_SpecificConfig_t;
+
+/* ISO/IEC 14496-3 ALSSpecificConfig */
+typedef struct
+{
+    uint32_t size;
+    uint8_t *data;
+    uint32_t samp_freq;
+    uint16_t channels;
+    uint8_t  resolution;
+    uint8_t  floating;
+    uint16_t frame_length;
+    uint16_t max_order;
+    uint8_t  block_switching;
+    uint8_t  bgmc_mode;
+    uint8_t  RLSLMS;
+} mp4a_ALSSpecificConfig_t;
+
+static inline void mp4a_remove_GASpecificConfig( mp4a_GASpecificConfig_t* gasc )
+{
+    debug_if( !gasc )
+        return;
+    if( gasc->program_config_element )
+        free( gasc->program_config_element );
+    free( gasc );
+}
+
+static inline void mp4a_remove_MPEG_1_2_SpecificConfig( mp4a_MPEG_1_2_SpecificConfig_t* mpeg_1_2_sc )
+{
+    debug_if( mpeg_1_2_sc )
+        free( mpeg_1_2_sc );
+}
+
+void mp4a_remove_AudioSpecificConfig( mp4a_AudioSpecificConfig_t* asc )
+{
+    if( !asc )
+        return;
+    switch( asc->audioObjectType ){
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_SBR:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_TwinVQ:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD:
+        mp4a_remove_GASpecificConfig( (mp4a_GASpecificConfig_t*)asc->deepAudioSpecificConfig );
+        break;
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_1:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_2:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_3:
+        mp4a_remove_MPEG_1_2_SpecificConfig( (mp4a_MPEG_1_2_SpecificConfig_t*)asc->deepAudioSpecificConfig );
+        break;
+    default:
+        if( asc->deepAudioSpecificConfig )
+            free( asc->deepAudioSpecificConfig );
+        break;
+    }
+    free( asc );
+}
+
+/* ADIF/PCE(program config element) style GASpecificConfig is not not supported. */
+/* channelConfig/samplingFrequencyIndex will be used when we support ADIF/PCE style GASpecificConfig. */
+static mp4a_GASpecificConfig_t* mp4a_create_GASpecificConfig( uint8_t samplingFrequencyIndex, uint8_t channelConfig, lsmash_mp4a_AudioObjectType aot )
+{
+    debug_if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC
+        && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP
+        && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ )
+        return NULL;
+    if( samplingFrequencyIndex > 0xB || channelConfig == 0 || channelConfig == 7 )
+        return NULL;
+    mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_GASpecificConfig_t) );
+    if( !gasc )
+        return NULL;
+    gasc->frameLengthFlag = 0; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */
+    gasc->dependsOnCoreCoder = 0; /* FIXME: used if scalable AAC. */
+    switch( aot ){
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_TwinVQ:
+        gasc->extensionFlag = 0;
+        break;
+    /* currently never occures. */
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD:
+        gasc->extensionFlag = 1;
+        break;
+    default:
+        gasc->extensionFlag = 0;
+        break;
+    }
+    return gasc;
+}
+
+static mp4a_MPEG_1_2_SpecificConfig_t* mp4a_create_MPEG_1_2_SpecificConfig()
+{
+    mp4a_MPEG_1_2_SpecificConfig_t *mpeg_1_2_sc = (mp4a_MPEG_1_2_SpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_MPEG_1_2_SpecificConfig_t) );
+    if( !mpeg_1_2_sc )
+        return NULL;
+    mpeg_1_2_sc->extension = 0; /* shall be 0. */
+    return mpeg_1_2_sc;
+}
+
+static mp4a_ALSSpecificConfig_t *mp4a_create_ALSSpecificConfig( uint8_t *exdata, uint32_t exdata_length )
+{
+    mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_ALSSpecificConfig_t) );
+    if( !alssc )
+        return NULL;
+    alssc->data = lsmash_memdup( exdata, exdata_length );
+    if( !alssc->data )
+        return NULL;
+    alssc->size = exdata_length;
+    return alssc;
+}
+
+/* Currently, only normal AAC, MPEG_1_2 are supported.
+   For AAC, other than normal AAC, such as AAC_scalable, ER_AAC_xxx, are not supported.
+   ADIF/PCE(program config element) style AudioSpecificConfig is not supported.
+   aot shall not be MP4A_AUDIO_OBJECT_TYPE_SBR even if you wish to signal SBR explicitly, use sbr_mode instead.
+   Frequency/channels shall be base AAC's one, even if SBR/PS.
+   If other than AAC with SBR, sbr_mode shall be MP4A_AAC_SBR_NOT_SPECIFIED. */
+mp4a_AudioSpecificConfig_t *mp4a_create_AudioSpecificConfig(
+    lsmash_mp4a_AudioObjectType aot,
+    uint32_t frequency,
+    uint32_t channels,
+    lsmash_mp4a_aac_sbr_mode sbr_mode,
+    uint8_t *exdata,
+    uint32_t exdata_length
+)
+{
+    if( aot != MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LC
+        && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_SSR && aot != MP4A_AUDIO_OBJECT_TYPE_AAC_LTP
+        && aot != MP4A_AUDIO_OBJECT_TYPE_TwinVQ && aot != MP4A_AUDIO_OBJECT_TYPE_ALS )
+        return NULL;
+    if( frequency == 0 )
+        return NULL;
+
+    uint8_t channelConfig;
+    switch( channels ){
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+        case 6:
+            channelConfig = channels;
+            break;
+        case 8:
+            channelConfig = 7;
+            break;
+        default:
+            return NULL;
+    }
+
+    mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) );
+    if( !asc )
+        return NULL;
+
+    asc->sbr_mode = sbr_mode;
+    asc->audioObjectType = aot;
+    asc->channelConfiguration = channelConfig;
+
+    uint8_t samplingFrequencyIndex = 0xF;
+    uint8_t i = 0x0;
+    if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED
+     || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN
+     || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LC
+     || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_SSR
+     || aot == MP4A_AUDIO_OBJECT_TYPE_AAC_LTP
+     || aot == MP4A_AUDIO_OBJECT_TYPE_SBR )
+    {
+        while( frequency < mp4a_sampling_frequency_table[i][0] )
+            i++;
+        asc->samplingFrequencyIndex = frequency == mp4a_sampling_frequency_table[i][1] ? i : 0xF;
+        asc->samplingFrequency = frequency;
+        samplingFrequencyIndex = mp4a_sampling_frequency_table[i][2];
+        /* SBR settings */
+        if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED )
+        {
+            /* SBR limitation */
+            /* see ISO/IEC 14496-3 Levels within the profiles / Levels for the High Efficiency AAC Profile */
+            if( i < 0x3 )
+            {
+                free( asc );
+                return NULL;
+            }
+            asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_SBR;
+        }
+        else
+            asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_NULL;
+
+        if( sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE || sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE )
+        {
+            asc->extensionSamplingFrequency = frequency * 2;
+            asc->extensionSamplingFrequencyIndex = i == 0xC ? 0xF : mp4a_sampling_frequency_table[i][3];
+        }
+        else
+        {
+            asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex;
+            asc->extensionSamplingFrequency = asc->samplingFrequency;
+        }
+    }
+    else
+    {
+        while( i < 0xD && frequency != mp4a_sampling_frequency_table[i][1] )
+            i++;
+        asc->samplingFrequencyIndex          = i != 0xD ? i : 0xF;
+        asc->samplingFrequency               = frequency;
+        asc->extensionAudioObjectType        = MP4A_AUDIO_OBJECT_TYPE_NULL;
+        asc->extensionSamplingFrequencyIndex = asc->samplingFrequencyIndex;
+        asc->extensionSamplingFrequency      = asc->samplingFrequency;
+    }
+
+    switch( aot )
+    {
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN:
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LC:
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR:
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP:
+        case MP4A_AUDIO_OBJECT_TYPE_SBR:
+#if 0 /* FIXME: here, stop currently unsupported codecs. */
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable:
+        case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC:
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP:
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable:
+        case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ:
+        case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC:
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD:
+#endif
+            asc->deepAudioSpecificConfig = mp4a_create_GASpecificConfig( samplingFrequencyIndex, channelConfig, aot );
+            break;
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_1:
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_2:
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_3:
+            asc->deepAudioSpecificConfig = mp4a_create_MPEG_1_2_SpecificConfig();
+            break;
+        case MP4A_AUDIO_OBJECT_TYPE_ALS:
+            asc->deepAudioSpecificConfig = mp4a_create_ALSSpecificConfig( exdata, exdata_length );
+            break;
+        default:
+            break; /* this case is trapped below. */
+    }
+    if( !asc->deepAudioSpecificConfig ){
+        free( asc );
+        return NULL;
+    }
+    return asc;
+}
+
+/* ADIF/PCE(program config element) style GASpecificConfig is not supported. */
+static void mp4a_put_GASpecificConfig( lsmash_bits_t* bits, mp4a_GASpecificConfig_t* gasc )
+{
+    debug_if( !bits || !gasc )
+        return;
+    lsmash_bits_put( bits, gasc->frameLengthFlag, 1 );
+    lsmash_bits_put( bits, gasc->dependsOnCoreCoder, 1 );
+    lsmash_bits_put( bits, gasc->extensionFlag, 1 );
+}
+
+static void mp4a_put_MPEG_1_2_SpecificConfig( lsmash_bits_t* bits, mp4a_MPEG_1_2_SpecificConfig_t* mpeg_1_2_sc )
+{
+    debug_if( !bits || !mpeg_1_2_sc )
+        return;
+    lsmash_bits_put( bits, mpeg_1_2_sc->extension, 1 ); /* shall be 0 */
+}
+
+static void mp4a_put_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_ALSSpecificConfig_t *alssc )
+{
+    debug_if( !bits || !alssc )
+        return;
+    lsmash_bits_put( bits, 0, 5 );      /* fillBits for byte alignment */
+    lsmash_bits_import_data( bits, alssc->data, alssc->size );
+}
+
+static inline void mp4a_put_AudioObjectType( lsmash_bits_t* bits, lsmash_mp4a_AudioObjectType aot )
+{
+    if( aot > MP4A_AUDIO_OBJECT_TYPE_ESCAPE )
+    {
+        lsmash_bits_put( bits, MP4A_AUDIO_OBJECT_TYPE_ESCAPE, 5 );
+        lsmash_bits_put( bits, aot - MP4A_AUDIO_OBJECT_TYPE_ESCAPE - 1, 6 );
+    }
+    else
+        lsmash_bits_put( bits, aot, 5 );
+}
+
+static inline void mp4a_put_SamplingFrequencyIndex( lsmash_bits_t* bits, uint8_t samplingFrequencyIndex, uint32_t samplingFrequency )
+{
+    lsmash_bits_put( bits, samplingFrequencyIndex, 4 );
+    if( samplingFrequencyIndex == 0xF )
+        lsmash_bits_put( bits, samplingFrequency, 24 );
+}
+
+/* Currently, only normal AAC, MPEG_1_2 are supported.
+   For AAC, other than normal AAC, such as AAC_scalable, ER_AAC_xxx, are not supported.
+   ADIF/PCE(program config element) style AudioSpecificConfig is not supported either. */
+void mp4a_put_AudioSpecificConfig( lsmash_bs_t* bs, mp4a_AudioSpecificConfig_t* asc )
+{
+    debug_if( !bs || !asc )
+        return;
+    lsmash_bits_t bits;
+    lsmash_bits_init( &bits, bs );
+
+    if( asc->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL )
+        mp4a_put_AudioObjectType( &bits, asc->extensionAudioObjectType ); /* puts MP4A_AUDIO_OBJECT_TYPE_SBR */
+    else
+        mp4a_put_AudioObjectType( &bits, asc->audioObjectType );
+    mp4a_put_SamplingFrequencyIndex( &bits, asc->samplingFrequencyIndex, asc->samplingFrequency );
+    lsmash_bits_put( &bits, asc->channelConfiguration, 4 );
+    if( asc->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL )
+    {
+        mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency );
+        mp4a_put_AudioObjectType( &bits, asc->audioObjectType );
+    }
+    switch( asc->audioObjectType ){
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR:
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_SBR:
+#if 0 /* FIXME: here, stop currently unsupported codecs */
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_TwinVQ: /* NOTE: I think we already have a support for TwinVQ, but how to test this? */
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC:
+    case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD:
+#endif
+        mp4a_put_GASpecificConfig( &bits, (mp4a_GASpecificConfig_t*)asc->deepAudioSpecificConfig );
+        break;
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_1:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_2:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_3:
+        mp4a_put_MPEG_1_2_SpecificConfig( &bits, (mp4a_MPEG_1_2_SpecificConfig_t*)asc->deepAudioSpecificConfig );
+        break;
+    case MP4A_AUDIO_OBJECT_TYPE_ALS:
+        mp4a_put_ALSSpecificConfig( &bits, (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig );
+        break;
+    default:
+        break; /* FIXME: do we have to return error? */
+    }
+
+    /* FIXME: Error Resiliant stuff omitted here. */
+
+    if( asc->sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE || asc->sbr_mode == MP4A_AAC_SBR_NONE )
+    {
+        lsmash_bits_put( &bits, 0x2b7, 11 );
+        mp4a_put_AudioObjectType( &bits, asc->extensionAudioObjectType ); /* puts MP4A_AUDIO_OBJECT_TYPE_SBR */
+        if( asc->extensionAudioObjectType == MP4A_AUDIO_OBJECT_TYPE_SBR ) /* this is always true, due to current spec */
+        {
+            /* sbrPresentFlag */
+            if( asc->sbr_mode == MP4A_AAC_SBR_NONE )
+                lsmash_bits_put( &bits, 0x0, 1 );
+            else
+            {
+                lsmash_bits_put( &bits, 0x1, 1 );
+                mp4a_put_SamplingFrequencyIndex( &bits, asc->extensionSamplingFrequencyIndex, asc->extensionSamplingFrequency );
+            }
+        }
+    }
+    lsmash_bits_put_align( &bits );
+}
+
+static int mp4a_get_GASpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc )
+{
+    mp4a_GASpecificConfig_t *gasc = (mp4a_GASpecificConfig_t *)malloc( sizeof(mp4a_GASpecificConfig_t) );
+    if( !gasc )
+        return -1;
+    asc->deepAudioSpecificConfig = gasc;
+    gasc->frameLengthFlag = lsmash_bits_get( bits, 1 );
+    gasc->dependsOnCoreCoder = lsmash_bits_get( bits, 1 );
+    if( gasc->dependsOnCoreCoder )
+        lsmash_bits_get( bits, 14 );    /* coreCoderDelay */
+    gasc->extensionFlag = lsmash_bits_get( bits, 1 );
+    return 0;
+}
+
+static int mp4a_get_MPEG_1_2_SpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc )
+{
+    lsmash_bits_get( bits, 1 );
+    return 0;
+}
+
+static int mp4a_get_ALSSpecificConfig( lsmash_bits_t *bits, mp4a_AudioSpecificConfig_t *asc )
+{
+    mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)malloc( sizeof(mp4a_ALSSpecificConfig_t) );
+    if( !alssc )
+        return -1;
+    asc->deepAudioSpecificConfig = alssc;
+    lsmash_bits_get( bits, 32 );    /* als_id */
+    alssc->samp_freq = lsmash_bits_get( bits, 32 );
+    lsmash_bits_get( bits, 32 );    /* samples */
+    alssc->channels = lsmash_bits_get( bits, 16 );
+    lsmash_bits_get( bits, 3 );     /* file_type */
+    alssc->resolution = lsmash_bits_get( bits, 3 );
+    alssc->floating = lsmash_bits_get( bits, 1 );
+    lsmash_bits_get( bits, 1 );     /* msb_first */
+    alssc->frame_length = lsmash_bits_get( bits, 16 );
+    lsmash_bits_get( bits, 8 );     /* random_access */
+    lsmash_bits_get( bits, 2 );     /* ra_flag */
+    lsmash_bits_get( bits, 1 );     /* adapt_order */
+    lsmash_bits_get( bits, 2 );     /* coef_table */
+    lsmash_bits_get( bits, 1 );     /* long_term_prediction */
+    alssc->max_order = lsmash_bits_get( bits, 10 );
+    alssc->block_switching = lsmash_bits_get( bits, 2 );
+    alssc->bgmc_mode = lsmash_bits_get( bits, 1 );
+    lsmash_bits_get( bits, 1 );     /* sb_part */
+    lsmash_bits_get( bits, 1 );     /* joint_stereo */
+    lsmash_bits_get( bits, 1 );     /* mc_coding */
+    lsmash_bits_get( bits, 1 );     /* chan_config */
+    lsmash_bits_get( bits, 1 );     /* chan_sort */
+    lsmash_bits_get( bits, 1 );     /* crc_enabled */
+    alssc->RLSLMS = lsmash_bits_get( bits, 1 );
+    return 0;
+}
+
+static mp4a_AudioSpecificConfig_t * mp4a_get_AudioSpecificConfig( lsmash_bits_t *bits, uint8_t *dsi_payload, uint32_t dsi_payload_length )
+{
+    if( lsmash_bits_import_data( bits, dsi_payload, dsi_payload_length ) )
+        return NULL;
+    mp4a_AudioSpecificConfig_t *asc = (mp4a_AudioSpecificConfig_t *)lsmash_malloc_zero( sizeof(mp4a_AudioSpecificConfig_t) );
+    if( !asc )
+        return NULL;
+    asc->audioObjectType = lsmash_bits_get( bits, 5 );
+    if( asc->audioObjectType == 31 )
+        asc->extensionAudioObjectType = asc->audioObjectType += 1 + lsmash_bits_get( bits, 6 );
+    asc->samplingFrequencyIndex = lsmash_bits_get( bits, 4 );
+    if( asc->samplingFrequencyIndex == 0xf )
+        asc->samplingFrequency = lsmash_bits_get( bits, 24 );
+    asc->channelConfiguration = lsmash_bits_get( bits, 4 );
+    int ret = 0;
+    switch( asc->audioObjectType )
+    {
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LC :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable :
+        case MP4A_AUDIO_OBJECT_TYPE_TwinVQ :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD :
+            ret = mp4a_get_GASpecificConfig( bits, asc );
+            break;
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_1 :
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_2 :
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_3 :
+            ret = mp4a_get_MPEG_1_2_SpecificConfig( bits, asc );
+            break;
+        case MP4A_AUDIO_OBJECT_TYPE_ALS :
+            lsmash_bits_get( bits, 5 );
+            ret = mp4a_get_ALSSpecificConfig( bits, asc );
+            break;
+        default :
+            break;
+    }
+    return ret ? NULL : asc;
+}
+
+int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length )
+{
+    lsmash_bits_t *bits = lsmash_bits_adhoc_create();
+    if( !bits )
+        return -1;
+    mp4a_AudioSpecificConfig_t *asc = mp4a_get_AudioSpecificConfig( bits, dsi_payload, dsi_payload_length );
+    if( !asc )
+        goto fail;
+    summary->sample_type            = ISOM_CODEC_TYPE_MP4A_AUDIO;
+    summary->object_type_indication = MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3;
+    summary->stream_type            = MP4SYS_STREAM_TYPE_AudioStream;
+    summary->exdata                 = NULL;
+    summary->exdata_length          = 0;
+    summary->aot                    = asc->audioObjectType;
+    switch( asc->audioObjectType )
+    {
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LC :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_LTP :
+        case MP4A_AUDIO_OBJECT_TYPE_AAC_scalable :
+        case MP4A_AUDIO_OBJECT_TYPE_TwinVQ :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_BSAC :
+        case MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD :
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_1 :
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_2 :
+        case MP4A_AUDIO_OBJECT_TYPE_Layer_3 :
+            if( asc->samplingFrequencyIndex == 0xf )
+                summary->frequency = asc->samplingFrequency;
+            else
+            {
+                uint8_t i = 0x0;
+                while( i != 0xc )
+                {
+                    if( mp4a_sampling_frequency_table[i][2] == asc->samplingFrequencyIndex )
+                    {
+                        summary->frequency = mp4a_sampling_frequency_table[i][1];
+                        break;
+                    }
+                    ++i;
+                }
+                if( i == 0xc )
+                    goto fail;
+            }
+            if( asc->channelConfiguration < 8 )
+                summary->channels = asc->channelConfiguration != 7 ? asc->channelConfiguration : 8;
+            else
+                summary->channels = 0;      /* reserved */
+            summary->bit_depth = 16;
+            switch( asc->audioObjectType )
+            {
+                case MP4A_AUDIO_OBJECT_TYPE_AAC_SSR :
+                    summary->samples_in_frame = 1024;
+                    break;
+                case MP4A_AUDIO_OBJECT_TYPE_Layer_1 :
+                    summary->samples_in_frame = 384;
+                    break;
+                case MP4A_AUDIO_OBJECT_TYPE_Layer_2 :
+                case MP4A_AUDIO_OBJECT_TYPE_Layer_3 :
+                    summary->samples_in_frame = 1152;
+                    break;
+                default :
+                    summary->samples_in_frame = !((mp4a_GASpecificConfig_t *)asc->deepAudioSpecificConfig)->frameLengthFlag ? 1024 : 960;
+                    break;
+            }
+            break;
+        case MP4A_AUDIO_OBJECT_TYPE_ALS :
+        {
+            mp4a_ALSSpecificConfig_t *alssc = (mp4a_ALSSpecificConfig_t *)asc->deepAudioSpecificConfig;
+            summary->frequency        = alssc->samp_freq;
+            summary->channels         = alssc->channels + 1;
+            summary->bit_depth        = (alssc->resolution + 1) * 8;
+            summary->samples_in_frame = alssc->frame_length + 1;
+            break;
+        }
+        default :
+            break;
+    }
+    mp4a_remove_AudioSpecificConfig( asc );
+    lsmash_bits_adhoc_cleanup( bits );
+    return 0;
+fail:
+    mp4a_remove_AudioSpecificConfig( asc );
+    lsmash_bits_adhoc_cleanup( bits );
+    return -1;
+}
+
+/***************************************************************************
+    audioProfileLevelIndication
+***************************************************************************/
+/* NOTE: This function is not strictly preferable, but accurate.
+   The spec of audioProfileLevelIndication is too much complicated. */
+mp4a_audioProfileLevelIndication mp4a_get_audioProfileLevelIndication( lsmash_audio_summary_t *summary )
+{
+    if( !summary || summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream )
+        return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */
+    if( summary->object_type_indication != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 )
+        return MP4A_AUDIO_PLI_NOT_SPECIFIED; /* This is of audio stream, but not described in ISO/IEC 14496-3. */
+    if( summary->channels == 0 || summary->frequency == 0 )
+        return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */
+
+    mp4a_audioProfileLevelIndication pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
+    switch( summary->aot )
+    {
+    case MP4A_AUDIO_OBJECT_TYPE_AAC_LC:
+        if( summary->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL )
+        {
+            /* NOTE: This is not strictly preferable, but accurate; just possibly over-estimated.
+               We do not expect to use MP4A_AAC_SBR_HIERARCHICAL mode without SBR, nor downsampled mode with SBR. */
+            if( summary->channels <= 2 && summary->frequency <= 24000 )
+                pli = MP4A_AUDIO_PLI_HE_AAC_L2;
+            else if( summary->channels <= 5 && summary->frequency <= 48000 )
+                pli = MP4A_AUDIO_PLI_HE_AAC_L5;
+            else
+                pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
+            break;
+        }
+        /* pretending plain AAC-LC, if actually HE-AAC. */
+        static const uint32_t mp4sys_aac_pli_table[5][3] = {
+            /* channels, frequency,    audioProfileLevelIndication */
+            {         6,     96000,        MP4A_AUDIO_PLI_AAC_L5 }, /* FIXME: 6ch is not strictly correct, but works in many case. */
+            {         6,     48000,        MP4A_AUDIO_PLI_AAC_L4 }, /* FIXME: 6ch is not strictly correct, but works in many case. */
+            {         2,     48000,        MP4A_AUDIO_PLI_AAC_L2 },
+            {         2,     24000,        MP4A_AUDIO_PLI_AAC_L1 },
+            {         0,         0, MP4A_AUDIO_PLI_NOT_SPECIFIED }
+        };
+        for( int i = 0; summary->channels <= mp4sys_aac_pli_table[i][0] && summary->frequency <= mp4sys_aac_pli_table[i][1] ; i++ )
+            pli = mp4sys_aac_pli_table[i][2];
+        break;
+    case MP4A_AUDIO_OBJECT_TYPE_ALS:
+        /* FIXME: this is not stricly. Summary shall carry max_order, block_switching, bgmc_mode and RLSLMS. */
+        if( summary->channels <= 2 && summary->frequency <= 48000 && summary->bit_depth <= 16 && summary->samples_in_frame <= 4096 )
+            pli = MP4A_AUDIO_PLI_ALS_Simple_L1;
+        else
+            pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
+        break;
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_1:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_2:
+    case MP4A_AUDIO_OBJECT_TYPE_Layer_3:
+        pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; /* 14496-3, Audio profiles and levels, does not allow any pli. */
+        break;
+    default:
+        pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; /* something we don't know/support, or what the spec never covers. */
+        break;
+    }
+    return pli;
+}
+
+static int mp4sys_is_same_profile( mp4a_audioProfileLevelIndication a, mp4a_audioProfileLevelIndication b )
+{
+    switch( a )
+    {
+    case MP4A_AUDIO_PLI_Main_L1:
+    case MP4A_AUDIO_PLI_Main_L2:
+    case MP4A_AUDIO_PLI_Main_L3:
+    case MP4A_AUDIO_PLI_Main_L4:
+        if( MP4A_AUDIO_PLI_Main_L1 <= b && b <= MP4A_AUDIO_PLI_Main_L4 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_Scalable_L1:
+    case MP4A_AUDIO_PLI_Scalable_L2:
+    case MP4A_AUDIO_PLI_Scalable_L3:
+    case MP4A_AUDIO_PLI_Scalable_L4:
+        if( MP4A_AUDIO_PLI_Scalable_L1 <= b && b <= MP4A_AUDIO_PLI_Scalable_L4 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_Speech_L1:
+    case MP4A_AUDIO_PLI_Speech_L2:
+        if( MP4A_AUDIO_PLI_Speech_L1 <= b && b <= MP4A_AUDIO_PLI_Speech_L2 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_Synthetic_L1:
+    case MP4A_AUDIO_PLI_Synthetic_L2:
+    case MP4A_AUDIO_PLI_Synthetic_L3:
+        if( MP4A_AUDIO_PLI_Synthetic_L1 <= b && b <= MP4A_AUDIO_PLI_Synthetic_L3 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_HighQuality_L1:
+    case MP4A_AUDIO_PLI_HighQuality_L2:
+    case MP4A_AUDIO_PLI_HighQuality_L3:
+    case MP4A_AUDIO_PLI_HighQuality_L4:
+    case MP4A_AUDIO_PLI_HighQuality_L5:
+    case MP4A_AUDIO_PLI_HighQuality_L6:
+    case MP4A_AUDIO_PLI_HighQuality_L7:
+    case MP4A_AUDIO_PLI_HighQuality_L8:
+        if( MP4A_AUDIO_PLI_HighQuality_L1 <= b && b <= MP4A_AUDIO_PLI_HighQuality_L8 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_LowDelay_L1:
+    case MP4A_AUDIO_PLI_LowDelay_L2:
+    case MP4A_AUDIO_PLI_LowDelay_L3:
+    case MP4A_AUDIO_PLI_LowDelay_L4:
+    case MP4A_AUDIO_PLI_LowDelay_L5:
+    case MP4A_AUDIO_PLI_LowDelay_L6:
+    case MP4A_AUDIO_PLI_LowDelay_L7:
+    case MP4A_AUDIO_PLI_LowDelay_L8:
+        if( MP4A_AUDIO_PLI_LowDelay_L1 <= b && b <= MP4A_AUDIO_PLI_LowDelay_L8 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_Natural_L1:
+    case MP4A_AUDIO_PLI_Natural_L2:
+    case MP4A_AUDIO_PLI_Natural_L3:
+    case MP4A_AUDIO_PLI_Natural_L4:
+        if( MP4A_AUDIO_PLI_Natural_L1 <= b && b <= MP4A_AUDIO_PLI_Natural_L4 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_MobileInternetworking_L1:
+    case MP4A_AUDIO_PLI_MobileInternetworking_L2:
+    case MP4A_AUDIO_PLI_MobileInternetworking_L3:
+    case MP4A_AUDIO_PLI_MobileInternetworking_L4:
+    case MP4A_AUDIO_PLI_MobileInternetworking_L5:
+    case MP4A_AUDIO_PLI_MobileInternetworking_L6:
+        if( MP4A_AUDIO_PLI_MobileInternetworking_L1 <= b && b <= MP4A_AUDIO_PLI_MobileInternetworking_L6 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_AAC_L1:
+    case MP4A_AUDIO_PLI_AAC_L2:
+    case MP4A_AUDIO_PLI_AAC_L4:
+    case MP4A_AUDIO_PLI_AAC_L5:
+        if( MP4A_AUDIO_PLI_AAC_L1 <= b && b <= MP4A_AUDIO_PLI_AAC_L5 )
+            return 1;
+        return 0;
+        break;
+    case MP4A_AUDIO_PLI_HE_AAC_L2:
+    case MP4A_AUDIO_PLI_HE_AAC_L3:
+    case MP4A_AUDIO_PLI_HE_AAC_L4:
+    case MP4A_AUDIO_PLI_HE_AAC_L5:
+        if( MP4A_AUDIO_PLI_HE_AAC_L2 <= b && b <= MP4A_AUDIO_PLI_HE_AAC_L5 )
+            return 1;
+        return 0;
+        break;
+    default:
+        break;
+    }
+    return 0;
+}
+
+/* NOTE: This function is not strictly preferable, but accurate.
+   The spec of audioProfileLevelIndication is too much complicated. */
+mp4a_audioProfileLevelIndication mp4a_max_audioProfileLevelIndication( mp4a_audioProfileLevelIndication a, mp4a_audioProfileLevelIndication b )
+{
+    /* NONE_REQUIRED is minimal priotity, and NOT_SPECIFIED is max priority. */
+    if( a == MP4A_AUDIO_PLI_NOT_SPECIFIED || b == MP4A_AUDIO_PLI_NONE_REQUIRED )
+        return a;
+    if( a == MP4A_AUDIO_PLI_NONE_REQUIRED || b == MP4A_AUDIO_PLI_NOT_SPECIFIED )
+        return b;
+    mp4a_audioProfileLevelIndication c, d;
+    if( a < b )
+    {
+        c = a;
+        d = b;
+    }
+    else
+    {
+        c = b;
+        d = a;
+    }
+    /* AAC-LC and SBR specific; If mixtured there, use correspond HE_AAC profile. */
+    if( MP4A_AUDIO_PLI_AAC_L1 <= c && c <= MP4A_AUDIO_PLI_AAC_L5
+        && MP4A_AUDIO_PLI_HE_AAC_L2 <= d && d <= MP4A_AUDIO_PLI_HE_AAC_L5 )
+    {
+        if( c <= MP4A_AUDIO_PLI_AAC_L2 )
+            return d;
+        c += 4; /* upgrade to HE-AAC */
+        return c > d ? c : d;
+    }
+    /* General */
+    if( mp4sys_is_same_profile( c, d ) )
+        return d;
+    return MP4A_AUDIO_PLI_NOT_SPECIFIED;
+}
diff --git a/output/mp4/mp4a.h b/output/mp4/mp4a.h
new file mode 100644
index 0000000..7851e00
--- /dev/null
+++ b/output/mp4/mp4a.h
@@ -0,0 +1,130 @@
+/*****************************************************************************
+ * mp4a.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef MP4A_H
+#define MP4A_H
+
+#include "utils.h"
+
+/***************************************************************************
+    MPEG-4 Systems for MPEG-4 Audio
+***************************************************************************/
+
+/* 14496-3 audioProfileLevelIndication */
+typedef enum {
+    MP4A_AUDIO_PLI_Reserved                 = 0x00, /* Reserved for ISO use */
+    MP4A_AUDIO_PLI_Main_L1                  = 0x01, /* Main Audio Profile L1 */
+    MP4A_AUDIO_PLI_Main_L2                  = 0x02, /* Main Audio Profile L2 */
+    MP4A_AUDIO_PLI_Main_L3                  = 0x03, /* Main Audio Profile L3 */
+    MP4A_AUDIO_PLI_Main_L4                  = 0x04, /* Main Audio Profile L4 */
+    MP4A_AUDIO_PLI_Scalable_L1              = 0x05, /* Scalable Audio Profile L1 */
+    MP4A_AUDIO_PLI_Scalable_L2              = 0x06, /* Scalable Audio Profile L2 */
+    MP4A_AUDIO_PLI_Scalable_L3              = 0x07, /* Scalable Audio Profile L3 */
+    MP4A_AUDIO_PLI_Scalable_L4              = 0x08, /* Scalable Audio Profile L4 */
+    MP4A_AUDIO_PLI_Speech_L1                = 0x09, /* Speech Audio Profile L1 */
+    MP4A_AUDIO_PLI_Speech_L2                = 0x0A, /* Speech Audio Profile L2 */
+    MP4A_AUDIO_PLI_Synthetic_L1             = 0x0B, /* Synthetic Audio Profile L1 */
+    MP4A_AUDIO_PLI_Synthetic_L2             = 0x0C, /* Synthetic Audio Profile L2 */
+    MP4A_AUDIO_PLI_Synthetic_L3             = 0x0D, /* Synthetic Audio Profile L3 */
+    MP4A_AUDIO_PLI_HighQuality_L1           = 0x0E, /* High Quality Audio Profile L1 */
+    MP4A_AUDIO_PLI_HighQuality_L2           = 0x0F, /* High Quality Audio Profile L2 */
+    MP4A_AUDIO_PLI_HighQuality_L3           = 0x10, /* High Quality Audio Profile L3 */
+    MP4A_AUDIO_PLI_HighQuality_L4           = 0x11, /* High Quality Audio Profile L4 */
+    MP4A_AUDIO_PLI_HighQuality_L5           = 0x12, /* High Quality Audio Profile L5 */
+    MP4A_AUDIO_PLI_HighQuality_L6           = 0x13, /* High Quality Audio Profile L6 */
+    MP4A_AUDIO_PLI_HighQuality_L7           = 0x14, /* High Quality Audio Profile L7 */
+    MP4A_AUDIO_PLI_HighQuality_L8           = 0x15, /* High Quality Audio Profile L8 */
+    MP4A_AUDIO_PLI_LowDelay_L1              = 0x16, /* Low Delay Audio Profile L1 */
+    MP4A_AUDIO_PLI_LowDelay_L2              = 0x17, /* Low Delay Audio Profile L2 */
+    MP4A_AUDIO_PLI_LowDelay_L3              = 0x18, /* Low Delay Audio Profile L3 */
+    MP4A_AUDIO_PLI_LowDelay_L4              = 0x19, /* Low Delay Audio Profile L4 */
+    MP4A_AUDIO_PLI_LowDelay_L5              = 0x1A, /* Low Delay Audio Profile L5 */
+    MP4A_AUDIO_PLI_LowDelay_L6              = 0x1B, /* Low Delay Audio Profile L6 */
+    MP4A_AUDIO_PLI_LowDelay_L7              = 0x1C, /* Low Delay Audio Profile L7 */
+    MP4A_AUDIO_PLI_LowDelay_L8              = 0x1D, /* Low Delay Audio Profile L8 */
+    MP4A_AUDIO_PLI_Natural_L1               = 0x1E, /* Natural Audio Profile L1 */
+    MP4A_AUDIO_PLI_Natural_L2               = 0x1F, /* Natural Audio Profile L2 */
+    MP4A_AUDIO_PLI_Natural_L3               = 0x20, /* Natural Audio Profile L3 */
+    MP4A_AUDIO_PLI_Natural_L4               = 0x21, /* Natural Audio Profile L4 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L1 = 0x22, /* Mobile Audio Internetworking Profile L1 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L2 = 0x23, /* Mobile Audio Internetworking Profile L2 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L3 = 0x24, /* Mobile Audio Internetworking Profile L3 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L4 = 0x25, /* Mobile Audio Internetworking Profile L4 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L5 = 0x26, /* Mobile Audio Internetworking Profile L5 */
+    MP4A_AUDIO_PLI_MobileInternetworking_L6 = 0x27, /* Mobile Audio Internetworking Profile L6 */
+    MP4A_AUDIO_PLI_AAC_L1                   = 0x28, /* AAC Profile L1 */
+    MP4A_AUDIO_PLI_AAC_L2                   = 0x29, /* AAC Profile L2 */
+    MP4A_AUDIO_PLI_AAC_L4                   = 0x2A, /* AAC Profile L4 */
+    MP4A_AUDIO_PLI_AAC_L5                   = 0x2B, /* AAC Profile L5 */
+    MP4A_AUDIO_PLI_HE_AAC_L2                = 0x2C, /* High Efficiency AAC Profile L2 */
+    MP4A_AUDIO_PLI_HE_AAC_L3                = 0x2D, /* High Efficiency AAC Profile L3 */
+    MP4A_AUDIO_PLI_HE_AAC_L4                = 0x2E, /* High Efficiency AAC Profile L4 */
+    MP4A_AUDIO_PLI_HE_AAC_L5                = 0x2F, /* High Efficiency AAC Profile L5 */
+    MP4A_AUDIO_PLI_HE_AAC_v2_L2             = 0x30, /* High Efficiency AAC v2 Profile L2 */
+    MP4A_AUDIO_PLI_HE_AAC_v2_L3             = 0x31, /* High Efficiency AAC v2 Profile L3 */
+    MP4A_AUDIO_PLI_HE_AAC_v2_L4             = 0x32, /* High Efficiency AAC v2 Profile L4 */
+    MP4A_AUDIO_PLI_HE_AAC_v2_L5             = 0x33, /* High Efficiency AAC v2 Profile L5 */
+    MP4A_AUDIO_PLI_LowDelay_AAC_L1          = 0x34, /* Low Delay AAC Profile L1 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L1= 0x35, /* Baseline MPEG Surround Profile L1 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L2= 0x36, /* Baseline MPEG Surround Profile L2 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L3= 0x37, /* Baseline MPEG Surround Profile L3 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L4= 0x38, /* Baseline MPEG Surround Profile L4 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L5= 0x39, /* Baseline MPEG Surround Profile L5 */
+    MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L6= 0x3A, /* Baseline MPEG Surround Profile L6 */
+    MP4A_AUDIO_PLI_HD_AAC_L1                = 0x3B, /* High Definition AAC Profile L1 */
+    MP4A_AUDIO_PLI_ALS_Simple_L1            = 0x3C, /* ALS Simple Profile L1 */
+    MP4A_AUDIO_PLI_NOT_SPECIFIED            = 0xFE, /* no audio profile specified */
+    MP4A_AUDIO_PLI_NONE_REQUIRED            = 0xFF, /* no audio capability required */
+} mp4a_audioProfileLevelIndication;
+
+#ifndef MP4A_INTERNAL
+
+typedef void mp4a_AudioSpecificConfig_t;
+
+/* export for mp4sys / importer */
+mp4a_AudioSpecificConfig_t *mp4a_create_AudioSpecificConfig(
+    lsmash_mp4a_AudioObjectType aot,
+    uint32_t frequency,
+    uint32_t channels,
+    lsmash_mp4a_aac_sbr_mode sbr_mode,
+    uint8_t *exdata,
+    uint32_t exdata_length
+);
+void mp4a_put_AudioSpecificConfig( lsmash_bs_t* bs, mp4a_AudioSpecificConfig_t* asc );
+void mp4a_remove_AudioSpecificConfig( mp4a_AudioSpecificConfig_t* asc );
+
+/* export for importer */
+extern const uint32_t mp4a_sampling_frequency_table[13][5];
+
+/* setup for summary */
+int mp4a_setup_summary_from_AudioSpecificConfig( lsmash_audio_summary_t *summary, uint8_t *dsi_payload, uint32_t dsi_payload_length );
+
+/* profileLevelIndication relative functions. */
+mp4a_audioProfileLevelIndication mp4a_get_audioProfileLevelIndication( lsmash_audio_summary_t *summary );
+mp4a_audioProfileLevelIndication mp4a_max_audioProfileLevelIndication(
+    mp4a_audioProfileLevelIndication a,
+    mp4a_audioProfileLevelIndication b
+);
+
+#endif
+
+#endif
diff --git a/output/mp4/mp4sys.c b/output/mp4/mp4sys.c
new file mode 100644
index 0000000..ea769e4
--- /dev/null
+++ b/output/mp4/mp4sys.c
@@ -0,0 +1,977 @@
+/*****************************************************************************
+ * mp4sys.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#include "utils.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "mp4a.h"
+#define MP4SYS_INTERNAL
+#include "mp4sys.h"
+
+/***************************************************************************
+    MPEG-4 Systems
+***************************************************************************/
+
+#define ALWAYS_28BITS_LENGTH_CODING 1 // for some weird (but originator's) devices
+
+/* List of Class Tags for Descriptors */
+typedef enum {
+    MP4SYS_DESCRIPTOR_TAG_Forbidden                           = 0x00, /* Forbidden */
+    MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag                      = 0x01, /* ObjectDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag               = 0x02, /* InitialObjectDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ES_DescrTag                         = 0x03, /* ES_DescrTag */
+    MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag               = 0x04, /* DecoderConfigDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag                  = 0x05, /* DecSpecificInfoTag */
+    MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag                    = 0x06, /* SLConfigDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ContentIdentDescrTag                = 0x07, /* ContentIdentDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_SupplContentIdentDescrTag           = 0x08, /* SupplContentIdentDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerTag                 = 0x09, /* IPI_DescrPointerTag */
+    MP4SYS_DESCRIPTOR_TAG_IPMP_DescrPointerTag                = 0x0A, /* IPMP_DescrPointerTag */
+    MP4SYS_DESCRIPTOR_TAG_IPMP_DescrTag                       = 0x0B, /* IPMP_DescrTag */
+    MP4SYS_DESCRIPTOR_TAG_QoS_DescrTag                        = 0x0C, /* QoS_DescrTag */
+    MP4SYS_DESCRIPTOR_TAG_RegistrationDescrTag                = 0x0D, /* RegistrationDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag                        = 0x0E, /* ES_ID_IncTag */
+    MP4SYS_DESCRIPTOR_TAG_ES_ID_RefTag                        = 0x0F, /* ES_ID_RefTag */
+    MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag                         = 0x10, /* MP4_IOD_Tag, InitialObjectDescriptor for MP4 */
+    MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag                          = 0x11, /* MP4_OD_Tag, ObjectDescriptor for MP4 */
+    MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerRefTag              = 0x12, /* IPI_DescrPointerRefTag */
+    MP4SYS_DESCRIPTOR_TAG_ExtendedProfileLevelDescrTag        = 0x13, /* ExtendedProfileLevelDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_profileLevelIndicationIndexDescrTag = 0x14, /* profileLevelIndicationIndexDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ContentClassificationDescrTag       = 0x40, /* ContentClassificationDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_KeyWordDescrTag                     = 0x41, /* KeyWordDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_RatingDescrTag                      = 0x42, /* RatingDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_LanguageDescrTag                    = 0x43, /* LanguageDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ShortTextualDescrTag                = 0x44, /* ShortTextualDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ExpandedTextualDescrTag             = 0x45, /* ExpandedTextualDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ContentCreatorNameDescrTag          = 0x46, /* ContentCreatorNameDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_ContentCreationDateDescrTag         = 0x47, /* ContentCreationDateDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_OCICreatorNameDescrTag              = 0x48, /* OCICreatorNameDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_OCICreationDateDescrTag             = 0x49, /* OCICreationDateDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_SmpteCameraPositionDescrTag         = 0x4A, /* SmpteCameraPositionDescrTag */
+    MP4SYS_DESCRIPTOR_TAG_Forbidden1                          = 0xFF, /* Forbidden */
+} mp4sys_descriptor_tag;
+//    MP4SYS_DESCRIPTOR_TAG_ES_DescrRemoveRefTag                = 0x07, /* FIXME: (command tag), see 14496-14 Object Descriptors */
+
+typedef struct {
+    uint32_t size; // 2^28 at most
+    mp4sys_descriptor_tag tag;
+} mp4sys_descriptor_head_t;
+
+/* DecoderSpecificInfo */
+/* contents varies depends on ObjectTypeIndication and StreamType. */
+typedef struct {
+    mp4sys_descriptor_head_t header;
+    uint8_t* data;
+} mp4sys_DecoderSpecificInfo_t;
+
+/* DecoderConfigDescriptor */
+typedef struct {
+    mp4sys_descriptor_head_t header;
+    lsmash_mp4sys_object_type_indication objectTypeIndication;
+    lsmash_mp4sys_stream_type streamType;
+    uint8_t upStream; /* bit(1), always 0 in this muxer, used for interactive contents. */
+    uint8_t reserved; /* const bit(1), always 1. */
+    uint32_t bufferSizeDB; /* maybe CPB size in bytes, NOT bits. */
+    uint32_t maxBitrate;
+    uint32_t avgBitrate; /* 0 if VBR */
+    mp4sys_DecoderSpecificInfo_t* decSpecificInfo;  /* can be NULL. */
+    /* 14496-1 seems to say if we are in IOD(InitialObjectDescriptor), we might use this.
+       See ExtensionProfileLevelDescr, The Initial Object Descriptor.
+       But I don't think this is mandatory despite 14496-1, because 14496-14 says, in OD or IOD,
+       we have to use ES_ID_Inc instead of ES_Descriptor, which does not have DecoderConfigDescriptor. */
+    // profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr [0..255];
+} mp4sys_DecoderConfigDescriptor_t;
+
+/* SLConfigDescriptor */
+typedef struct {
+    mp4sys_descriptor_head_t header;
+    uint8_t predefined;     /* default the values from a set of predefined parameter sets as detailed below.
+                             *  0x00        : Custum
+                             *  0x01        : null SL packet header
+                             *  0x02        : Reserved for use in MP4 files
+                             *  0x03 - 0xFF : Reserved for ISO use
+                             * MP4 file that does not use URL_Flag shall have constant value 0x02. */
+    /* Custom values
+     * The following fields are placed if predefined == 0x00. */
+    unsigned useAccessUnitStartFlag       : 1;
+    unsigned useAccessUnitEndFlag         : 1;
+    unsigned useRandomAccessPointFlag     : 1;
+    unsigned hasRandomAccessUnitsOnlyFlag : 1;
+    unsigned usePaddingFlag               : 1;
+    unsigned useTimeStampsFlag            : 1;
+    unsigned useIdleFlag                  : 1;
+    unsigned durationFlag                 : 1;
+    uint32_t timeStampResolution;
+    uint32_t OCRResolution;
+    uint8_t  timeStampLength;
+    uint8_t  OCRLength;
+    uint8_t  AU_Length;
+    uint8_t  instantBitrateLength;
+    unsigned degradationPriorityLength    : 4;
+    unsigned AU_seqNumLength              : 5;
+    unsigned packetSeqNumLength           : 5;
+    unsigned reserved                     : 2;
+    /* The following fields are placed if durationFlag is true. */
+    uint32_t timeScale;
+    uint16_t accessUnitDuration;
+    uint16_t compositionUnitDuration;
+    /* The following fields are placed if useTimeStampsFlag is false. */
+    uint64_t startDecodingTimeStamp;
+    uint64_t startCompositionTimeStamp;
+} mp4sys_SLConfigDescriptor_t;
+
+/* ES_Descriptor */
+typedef struct
+{
+    mp4sys_descriptor_head_t header;
+    uint16_t ES_ID;
+    unsigned streamDependenceFlag : 1;  /* no stream depencies between streams in this muxer, ES_ID of another elementary stream */
+    unsigned URL_Flag             : 1;  /* no external URL referencing stream in MP4 */
+    unsigned OCRstreamFlag        : 1;  /* no Object Clock Reference stream in this muxer (shall be false in MP4, useful if we're importing from MPEG-2?) */
+    unsigned streamPriority       : 5;  /* no priority among streams in this muxer, higher is important */
+    uint16_t dependsOn_ES_ID;
+    uint8_t URLlength;
+    uint8_t URLstring[255];
+    uint16_t OCR_ES_Id;
+    mp4sys_DecoderConfigDescriptor_t *decConfigDescr; /* cannot be NULL. */
+    mp4sys_SLConfigDescriptor_t *slConfigDescr;
+    /* descriptors below are not mandatory, I think Language Descriptor may somewhat useful */
+    /*
+    IPI_DescrPointer ipiPtr[0 .. 1];               // used to indicate using other ES's IP_IdentificationDataSet
+    IP_IdentificationDataSet ipIDS[0 .. 255];      // abstract class, actually ContentIdentificationDescriptor(for commercial contents management),
+                                                   // or SupplementaryContentIdentificationDescriptor(for embedding titles)
+    IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255]; // used to intellectual property / protection management
+    LanguageDescriptor langDescr[0 .. 255];        // used to identify the language of the audio/speech or text object
+    QoS_Descriptor qosDescr[0 .. 1];               // used to achieve QoS
+    RegistrationDescriptor regDescr[0 .. 1];       // used to carry elementary streams with data whose format is not recognized by ISO/IEC 14496-1
+    ExtensionDescriptor extDescr[0 .. 255];        // abstract class, actually defined no subclass, maybe useless
+    */
+} mp4sys_ES_Descriptor_t;
+
+/* 14496-14 Object Descriptors (ES_ID_Inc) */
+typedef struct {
+    mp4sys_descriptor_head_t header;
+    uint32_t Track_ID;
+} mp4sys_ES_ID_Inc_t;
+
+/* 14496-1 ObjectDescriptor / InitialObjectDescriptor */
+typedef struct {
+    mp4sys_descriptor_head_t header;
+    uint16_t ObjectDescriptorID;
+    // uint8_t URL_Flag; /* bit(1) */
+    uint8_t includeInlineProfileLevelFlag; /* bit(1) */
+    //const uint8_t reserved=0x0F(0b1111) or 0x1F(0b1.1111); /* bit(4 or 5), width is 4 for IOD, 5 for OD */
+    /* if (URL_Flag) {
+        uint8_t URLlength; // bit(8)
+        char URLstring[256]; // bit(8)[]
+    }*/
+    /* else { */
+        mp4sys_ODProfileLevelIndication       ODProfileLevelIndication;
+        mp4sys_sceneProfileLevelIndication    sceneProfileLevelIndication;
+        mp4a_audioProfileLevelIndication      audioProfileLevelIndication;
+        mp4sys_visualProfileLevelIndication   visualProfileLevelIndication;
+        mp4sys_graphicsProfileLevelIndication graphicsProfileLevelIndication;
+        lsmash_entry_list_t* esDescr; /* List of ES_ID_Inc, not ES_Descriptor defined in 14496-1. 14496-14 overrides. */
+        // OCI_Descriptor ociDescr[0 .. 255];
+        // IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255];
+    /* } */
+    // ExtensionDescriptor extDescr[0 .. 255];
+} mp4sys_ObjectDescriptor_t;
+
+int mp4sys_remove_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd || !esd->decConfigDescr )
+        return -1;
+    if( !esd->decConfigDescr->decSpecificInfo )
+        return 0;
+    if( esd->decConfigDescr->decSpecificInfo->data )
+        free( esd->decConfigDescr->decSpecificInfo->data );
+    free( esd->decConfigDescr->decSpecificInfo );
+    esd->decConfigDescr->decSpecificInfo = NULL;
+    return 0;
+}
+
+int mp4sys_remove_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd )
+        return -1;
+    if( !esd->decConfigDescr )
+        return 0;
+    mp4sys_remove_DecoderSpecificInfo( esd );
+    free( esd->decConfigDescr );
+    esd->decConfigDescr = NULL;
+    return 0;
+}
+
+int mp4sys_remove_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd )
+        return -1;
+    if( !esd->slConfigDescr )
+        return 0;
+    free( esd->slConfigDescr );
+    esd->slConfigDescr = NULL;
+    return 0;
+}
+
+int mp4sys_remove_ES_Descriptor( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd )
+        return 0;
+    mp4sys_remove_DecoderConfigDescriptor( esd );
+    mp4sys_remove_SLConfigDescriptor( esd );
+    free( esd );
+    return 0;
+}
+
+int mp4sys_remove_ES_ID_Incs( mp4sys_ObjectDescriptor_t* od )
+{
+    if( !od )
+        return -1;
+    if( od->esDescr )
+    {
+        lsmash_remove_list( od->esDescr, NULL );
+        od->esDescr = NULL;
+    }
+    return 0;
+}
+
+int mp4sys_remove_ObjectDescriptor( mp4sys_ObjectDescriptor_t* od )
+{
+    if( !od )
+        return 0;
+    mp4sys_remove_ES_ID_Incs( od );
+    free( od );
+    return 0;
+}
+
+int mp4sys_add_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd, void* dsi_payload, uint32_t dsi_payload_length )
+{
+    if( !esd || !esd->decConfigDescr || dsi_payload == NULL || dsi_payload_length == 0 )
+        return -1;
+    mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderSpecificInfo_t) );
+    if( !dsi )
+        return -1;
+    dsi->header.tag = MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag;
+    dsi->data = lsmash_memdup( dsi_payload, dsi_payload_length );
+    if( !dsi->data )
+    {
+        free( dsi );
+        return -1;
+    }
+    dsi->header.size = dsi_payload_length;
+    debug_if( mp4sys_remove_DecoderSpecificInfo( esd ) )
+    {
+        free( dsi->data );
+        free( dsi );
+        return -1;
+    }
+    esd->decConfigDescr->decSpecificInfo = dsi;
+    return 0;
+}
+
+/*
+    bufferSizeDB is byte unit, NOT bit unit.
+    avgBitrate is 0 if VBR
+*/
+int mp4sys_add_DecoderConfigDescriptor(
+    mp4sys_ES_Descriptor_t* esd,
+    lsmash_mp4sys_object_type_indication objectTypeIndication,
+    lsmash_mp4sys_stream_type streamType,
+    uint32_t bufferSizeDB,
+    uint32_t maxBitrate,
+    uint32_t avgBitrate
+){
+    if( !esd )
+        return -1;
+    mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderConfigDescriptor_t) );
+    if( !dcd )
+        return -1;
+    dcd->header.tag = MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag;
+    dcd->objectTypeIndication = objectTypeIndication;
+    dcd->streamType = streamType;
+    dcd->reserved = 1;
+    dcd->bufferSizeDB = bufferSizeDB;
+    dcd->maxBitrate = maxBitrate;
+    dcd->avgBitrate = avgBitrate;
+    debug_if( mp4sys_remove_DecoderConfigDescriptor( esd ) )
+    {
+        free( dcd );
+        return -1;
+    }
+    esd->decConfigDescr = dcd;
+    return 0;
+}
+
+/*
+    bufferSizeDB is byte unit, NOT bit unit.
+    avgBitrate is 0 if VBR
+*/
+int mp4sys_update_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd, uint32_t bufferSizeDB, uint32_t maxBitrate, uint32_t avgBitrate )
+{
+    if( !esd || !esd->decConfigDescr )
+        return -1;
+    mp4sys_DecoderConfigDescriptor_t* dcd = esd->decConfigDescr;
+    dcd->bufferSizeDB = bufferSizeDB;
+    dcd->maxBitrate = maxBitrate;
+    dcd->avgBitrate = avgBitrate;
+    return 0;
+}
+
+int mp4sys_add_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd )
+        return -1;
+    mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_SLConfigDescriptor_t) );
+    if( !slcd )
+        return -1;
+    slcd->header.tag = MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag;
+    slcd->predefined = 0x02; /* MP4 file which does not use URL_Flag shall have constant value 0x02 */
+    debug_if( mp4sys_remove_SLConfigDescriptor( esd ) )
+    {
+        free( slcd );
+        return -1;
+    }
+    esd->slConfigDescr = slcd;
+    return 0;
+}
+
+/* ES_ID might be usually 0 or lower 16 bits of the TrackID
+   14496-14 says, "set to 0 as stored; when built into a stream, the lower 16 bits of the TrackID are used."
+   I'm not sure about actual meaning of "stored" and "built into a stream", but maybe 0 will do in stsd(esds). */
+mp4sys_ES_Descriptor_t* mp4sys_create_ES_Descriptor( uint16_t ES_ID )
+{
+    mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_Descriptor_t) );
+    if( !esd )
+        return NULL;
+    esd->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_DescrTag;
+    esd->ES_ID = ES_ID;
+    return esd;
+}
+
+/* NOTE: This is only for MP4_IOD and MP4_OD, not for Iso Base Media's ObjectDescriptor and InitialObjectDescriptor */
+int mp4sys_add_ES_ID_Inc( mp4sys_ObjectDescriptor_t* od, uint32_t Track_ID )
+{
+    if( !od )
+        return -1;
+    if( !od->esDescr && ( od->esDescr = lsmash_create_entry_list() ) == NULL )
+        return -1;
+    mp4sys_ES_ID_Inc_t *es_id_inc = (mp4sys_ES_ID_Inc_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_ID_Inc_t) );
+    if( !es_id_inc )
+        return -1;
+    es_id_inc->header.tag = MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag;
+    es_id_inc->Track_ID = Track_ID;
+    if( lsmash_add_entry( od->esDescr, es_id_inc ) )
+    {
+        free( es_id_inc );
+        return -1;
+    }
+    return 0;
+}
+
+/* NOTE: This is only for MP4_OD, not for Iso Base Media's ObjectDescriptor */
+mp4sys_ObjectDescriptor_t* mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID )
+{
+    mp4sys_ObjectDescriptor_t *od = (mp4sys_ObjectDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ObjectDescriptor_t) );
+    if( !od )
+        return NULL;
+    od->header.tag = MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag;
+    od->ObjectDescriptorID = ObjectDescriptorID;
+    od->includeInlineProfileLevelFlag = 1; /* 1 as part of reserved flag. */
+    od->ODProfileLevelIndication = MP4SYS_OD_PLI_NONE_REQUIRED;
+    od->sceneProfileLevelIndication = MP4SYS_SCENE_PLI_NONE_REQUIRED;
+    od->audioProfileLevelIndication = MP4A_AUDIO_PLI_NONE_REQUIRED;
+    od->visualProfileLevelIndication = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
+    od->graphicsProfileLevelIndication = MP4SYS_GRAPHICS_PLI_NONE_REQUIRED;
+    return od;
+}
+
+/* NOTE: This is only for MP4_IOD, not for Iso Base Media's InitialObjectDescriptor */
+int mp4sys_to_InitialObjectDescriptor(
+    mp4sys_ObjectDescriptor_t* od,
+    uint8_t include_inline_pli,
+    mp4sys_ODProfileLevelIndication od_pli,
+    mp4sys_sceneProfileLevelIndication scene_pli,
+    mp4a_audioProfileLevelIndication audio_pli,
+    mp4sys_visualProfileLevelIndication visual_pli,
+    mp4sys_graphicsProfileLevelIndication graph_pli
+){
+    if( !od )
+        return -1;
+    od->header.tag = MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag;
+    od->includeInlineProfileLevelFlag = include_inline_pli;
+    od->ODProfileLevelIndication = od_pli;
+    od->sceneProfileLevelIndication = scene_pli;
+    od->audioProfileLevelIndication = audio_pli;
+    od->visualProfileLevelIndication = visual_pli;
+    od->graphicsProfileLevelIndication = graph_pli;
+    return 0;
+}
+
+/* returns total size of descriptor, including header, 2 at least */
+static inline uint32_t mp4sys_get_descriptor_size( uint32_t payload_size_in_byte )
+{
+#if ALWAYS_28BITS_LENGTH_CODING
+    return payload_size_in_byte + 4 + 1; /* +4 means 28bits length coding, +1 means tag's space */
+#else
+    /* descriptor length will be split into 7bits
+       see 14496-1 Expandable classes and Length encoding of descriptors and commands */
+    uint32_t i;
+    for( i = 1; payload_size_in_byte >> ( 7 * i ); i++ );
+    return payload_size_in_byte + i + 1; /* +1 means tag's space */
+#endif
+}
+
+static uint32_t mp4sys_update_DecoderSpecificInfo_size( mp4sys_ES_Descriptor_t* esd )
+{
+    debug_if( !esd || !esd->decConfigDescr )
+        return 0;
+    if( !esd->decConfigDescr->decSpecificInfo )
+        return 0;
+    /* no need to update, header.size is already set */
+    return mp4sys_get_descriptor_size( esd->decConfigDescr->decSpecificInfo->header.size );
+}
+
+static uint32_t mp4sys_update_DecoderConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd )
+{
+    debug_if( !esd )
+        return 0;
+    if( !esd->decConfigDescr )
+        return 0;
+    uint32_t size = 13;
+    size += mp4sys_update_DecoderSpecificInfo_size( esd );
+    esd->decConfigDescr->header.size = size;
+    return mp4sys_get_descriptor_size( size );
+}
+
+static uint32_t mp4sys_update_SLConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd )
+{
+    debug_if( !esd )
+        return 0;
+    if( !esd->slConfigDescr )
+        return 0;
+    mp4sys_SLConfigDescriptor_t *slcd = esd->slConfigDescr;
+    uint32_t size = 1;
+    if( slcd->predefined == 0x00 )
+        size += 15;
+    if( slcd->durationFlag )
+        size += 8;
+    if( !slcd->useTimeStampsFlag )
+        size += (2 * slcd->timeStampLength + 7) / 8;
+    esd->slConfigDescr->header.size = size;
+    return mp4sys_get_descriptor_size( size );
+}
+
+uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd )
+{
+    if( !esd )
+        return 0;
+    uint32_t size = 3;
+    if( esd->streamDependenceFlag )
+        size += 2;
+    if( esd->URL_Flag )
+        size += 1 + esd->URLlength;
+    if( esd->OCRstreamFlag )
+        size += 2;
+    size += mp4sys_update_DecoderConfigDescriptor_size( esd );
+    size += mp4sys_update_SLConfigDescriptor_size( esd );
+    esd->header.size = size;
+    return mp4sys_get_descriptor_size( size );
+}
+
+static uint32_t mp4sys_update_ES_ID_Inc_size( mp4sys_ES_ID_Inc_t* es_id_inc )
+{
+    debug_if( !es_id_inc )
+        return 0;
+    es_id_inc->header.size = 4;
+    return mp4sys_get_descriptor_size( es_id_inc->header.size );
+}
+
+/* This function works as aggregate of ES_ID_Incs, so this function itself updates no size information */
+static uint32_t mp4sys_update_ES_ID_Incs_size( mp4sys_ObjectDescriptor_t* od )
+{
+    debug_if( !od )
+        return 0;
+    if( !od->esDescr )
+        return 0;
+    uint32_t size = 0;
+    for( lsmash_entry_t *entry = od->esDescr->head; entry; entry = entry->next )
+        size += mp4sys_update_ES_ID_Inc_size( (mp4sys_ES_ID_Inc_t*)entry->data );
+    return size;
+}
+
+uint32_t mp4sys_update_ObjectDescriptor_size( mp4sys_ObjectDescriptor_t* od )
+{
+    if( !od )
+        return 0;
+    uint32_t size = od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ? 7 : 2;
+    size += mp4sys_update_ES_ID_Incs_size( od );
+    od->header.size = size;
+    return mp4sys_get_descriptor_size( size );
+}
+
+static int mp4sys_put_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header )
+{
+    debug_if( !bs || !header )
+        return -1;
+    lsmash_bs_put_byte( bs, header->tag );
+    /* descriptor length will be splitted into 7bits
+       see 14496-1 Expandable classes and Length encoding of descriptors and commands */
+#if ALWAYS_28BITS_LENGTH_CODING
+    lsmash_bs_put_byte( bs, ( header->size >> 21 ) | 0x80 );
+    lsmash_bs_put_byte( bs, ( header->size >> 14 ) | 0x80 );
+    lsmash_bs_put_byte( bs, ( header->size >>  7 ) | 0x80 );
+#else
+    for( uint32_t i = mp4sys_get_descriptor_size( header->size ) - header->size - 2; i; i-- ){
+        lsmash_bs_put_byte( bs, ( header->size >> ( 7 * i ) ) | 0x80 );
+    }
+#endif
+    lsmash_bs_put_byte( bs, header->size & 0x7F );
+    return 0;
+}
+
+static int mp4sys_write_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_DecoderSpecificInfo_t* dsi )
+{
+    debug_if( !bs )
+        return -1;
+    if( !dsi )
+        return 0; /* can be NULL */
+    debug_if( mp4sys_put_descriptor_header( bs, &dsi->header ) )
+        return -1;
+    if( dsi->data && dsi->header.size != 0 )
+        lsmash_bs_put_bytes( bs, dsi->data, dsi->header.size );
+    return lsmash_bs_write_data( bs );
+}
+
+static int mp4sys_write_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_DecoderConfigDescriptor_t* dcd )
+{
+    debug_if( !bs )
+        return -1;
+    if( !dcd )
+        return -1; /* cannot be NULL */
+    debug_if( mp4sys_put_descriptor_header( bs, &dcd->header ) )
+        return -1;
+    lsmash_bs_put_byte( bs, dcd->objectTypeIndication );
+    uint8_t temp;
+    temp  = (dcd->streamType << 2) & 0x3F;
+    temp |= (dcd->upStream   << 1) & 0x01;
+    temp |=  dcd->reserved         & 0x01;
+    lsmash_bs_put_byte( bs, temp );
+    lsmash_bs_put_be24( bs, dcd->bufferSizeDB );
+    lsmash_bs_put_be32( bs, dcd->maxBitrate );
+    lsmash_bs_put_be32( bs, dcd->avgBitrate );
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return mp4sys_write_DecoderSpecificInfo( bs, dcd->decSpecificInfo );
+    /* here, profileLevelIndicationIndexDescriptor is omitted */
+}
+
+static int mp4sys_write_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_SLConfigDescriptor_t* slcd )
+{
+    debug_if( !bs )
+        return -1;
+    if( !slcd )
+        return 0;
+    debug_if( mp4sys_put_descriptor_header( bs, &slcd->header ) )
+        return -1;
+    lsmash_bs_put_byte( bs, slcd->predefined );
+    if( slcd->predefined == 0x00 )
+    {
+        uint8_t temp8;
+        temp8  = slcd->useAccessUnitStartFlag       << 7;
+        temp8 |= slcd->useAccessUnitEndFlag         << 6;
+        temp8 |= slcd->useRandomAccessPointFlag     << 5;
+        temp8 |= slcd->hasRandomAccessUnitsOnlyFlag << 4;
+        temp8 |= slcd->usePaddingFlag               << 3;
+        temp8 |= slcd->useTimeStampsFlag            << 2;
+        temp8 |= slcd->useIdleFlag                  << 1;
+        temp8 |= slcd->durationFlag;
+        lsmash_bs_put_byte( bs, temp8 );
+        lsmash_bs_put_be32( bs, slcd->timeStampResolution );
+        lsmash_bs_put_be32( bs, slcd->OCRResolution );
+        lsmash_bs_put_byte( bs, slcd->timeStampLength );
+        lsmash_bs_put_byte( bs, slcd->OCRLength );
+        lsmash_bs_put_byte( bs, slcd->AU_Length );
+        lsmash_bs_put_byte( bs, slcd->instantBitrateLength );
+        uint16_t temp16;
+        temp16  = slcd->degradationPriorityLength << 12;
+        temp16 |= slcd->AU_seqNumLength           << 7;
+        temp16 |= slcd->packetSeqNumLength        << 2;
+        temp16 |= slcd->reserved;
+        lsmash_bs_put_be16( bs, temp16 );
+    }
+    if( slcd->durationFlag )
+    {
+        lsmash_bs_put_be32( bs, slcd->timeScale );
+        lsmash_bs_put_be16( bs, slcd->accessUnitDuration );
+        lsmash_bs_put_be16( bs, slcd->compositionUnitDuration );
+    }
+    if( !slcd->useTimeStampsFlag )
+    {
+        lsmash_bits_t *bits = lsmash_bits_create( bs );
+        if( !bits )
+            return -1;
+        lsmash_bits_put( bits, slcd->startDecodingTimeStamp,    slcd->timeStampLength );
+        lsmash_bits_put( bits, slcd->startCompositionTimeStamp, slcd->timeStampLength );
+        lsmash_bits_put_align( bits );
+        lsmash_bits_cleanup( bits );
+    }
+    return lsmash_bs_write_data( bs );
+}
+
+int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t* esd )
+{
+    if( !bs || !esd )
+        return -1;
+    debug_if( mp4sys_put_descriptor_header( bs, &esd->header ) )
+        return -1;
+    lsmash_bs_put_be16( bs, esd->ES_ID );
+    uint8_t temp;
+    temp  = esd->streamDependenceFlag << 7;
+    temp |= esd->URL_Flag             << 6;
+    temp |= esd->OCRstreamFlag        << 5;
+    temp |= esd->streamPriority;
+    lsmash_bs_put_byte( bs, temp );
+    if( esd->streamDependenceFlag )
+        lsmash_bs_put_be16( bs, esd->dependsOn_ES_ID );
+    if( esd->URL_Flag )
+    {
+        lsmash_bs_put_byte( bs, esd->URLlength );
+        lsmash_bs_put_bytes( bs, esd->URLstring, esd->URLlength );
+    }
+    if( esd->OCRstreamFlag )
+        lsmash_bs_put_be16( bs, esd->OCR_ES_Id );
+    /* here, some syntax elements are omitted due to previous flags (all 0) */
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    if( mp4sys_write_DecoderConfigDescriptor( bs, esd->decConfigDescr ) )
+        return -1;
+    return mp4sys_write_SLConfigDescriptor( bs, esd->slConfigDescr );
+}
+
+static int mp4sys_put_ES_ID_Inc( lsmash_bs_t *bs, mp4sys_ES_ID_Inc_t* es_id_inc )
+{
+    debug_if( !es_id_inc )
+        return 0;
+    debug_if( mp4sys_put_descriptor_header( bs, &es_id_inc->header ) )
+        return -1;
+    lsmash_bs_put_be32( bs, es_id_inc->Track_ID );
+    return 0;
+}
+
+/* This function works as aggregate of ES_ID_Incs */
+static int mp4sys_write_ES_ID_Incs( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od )
+{
+    debug_if( !od )
+        return 0;
+    if( !od->esDescr )
+        return 0; /* This may violate the spec, but some muxer do this */
+    for( lsmash_entry_t *entry = od->esDescr->head; entry; entry = entry->next )
+        mp4sys_put_ES_ID_Inc( bs, (mp4sys_ES_ID_Inc_t*)entry->data );
+    return lsmash_bs_write_data( bs );
+}
+
+int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od )
+{
+    if( !bs || !od )
+        return -1;
+    debug_if( mp4sys_put_descriptor_header( bs, &od->header ) )
+        return -1;
+    uint16_t temp = (od->ObjectDescriptorID << 6);
+    // temp |= (0x0 << 5); /* URL_Flag */
+    temp |= (od->includeInlineProfileLevelFlag << 4); /* if MP4_OD, includeInlineProfileLevelFlag is 0x1. */
+    temp |= 0xF;  /* reserved */
+    lsmash_bs_put_be16( bs, temp );
+    /* here, since we don't support URL_Flag, we put ProfileLevelIndications */
+    if( od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag )
+    {
+        lsmash_bs_put_byte( bs, od->ODProfileLevelIndication );
+        lsmash_bs_put_byte( bs, od->sceneProfileLevelIndication );
+        lsmash_bs_put_byte( bs, od->audioProfileLevelIndication );
+        lsmash_bs_put_byte( bs, od->visualProfileLevelIndication );
+        lsmash_bs_put_byte( bs, od->graphicsProfileLevelIndication );
+    }
+    if( lsmash_bs_write_data( bs ) )
+        return -1;
+    return mp4sys_write_ES_ID_Incs( bs, od );
+}
+
+#ifdef LSMASH_DEMUXER_ENABLED
+static int mp4sys_copy_DecoderSpecificInfo( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src )
+{
+    if( !src || !src->decConfigDescr || !dst || !dst->decConfigDescr
+     || mp4sys_remove_DecoderSpecificInfo( dst ) )
+        return -1;
+    mp4sys_DecoderSpecificInfo_t *dsi = src->decConfigDescr->decSpecificInfo;
+    if( !dsi || !dsi->data || !dsi->header.size )
+        return 0;
+    return mp4sys_add_DecoderSpecificInfo( dst, dsi->data, dsi->header.size );
+}
+
+static int mp4sys_copy_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src )
+{
+    if( !src || !dst
+     || mp4sys_remove_DecoderConfigDescriptor( dst ) )
+        return -1;
+    if( !src->decConfigDescr )
+        return 0;
+    if( mp4sys_add_DecoderConfigDescriptor( dst, 0, 0, 0, 0, 0 ) )
+        return -1;
+    *dst->decConfigDescr = *src->decConfigDescr;
+    dst->decConfigDescr->decSpecificInfo = NULL;
+    return mp4sys_copy_DecoderSpecificInfo( dst, src );
+}
+
+static int mp4sys_copy_SLConfigDescriptor( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src )
+{
+    if( !src || !dst
+     || mp4sys_remove_SLConfigDescriptor( dst ) )
+        return -1;
+    if( !src->slConfigDescr )
+        return 0;
+    if( mp4sys_add_SLConfigDescriptor( dst ) )
+        return -1;
+    *dst->slConfigDescr = *src->slConfigDescr;
+    return 0;
+}
+
+mp4sys_ES_Descriptor_t *mp4sys_duplicate_ES_Descriptor( mp4sys_ES_Descriptor_t *src )
+{
+    if( !src )
+        return NULL;
+    mp4sys_ES_Descriptor_t *dst = mp4sys_create_ES_Descriptor( 0 );
+    if( !dst )
+        return NULL;
+    *dst = *src;
+    dst->decConfigDescr = NULL;
+    dst->slConfigDescr = NULL;
+    if( mp4sys_copy_DecoderConfigDescriptor( dst, src )
+     || mp4sys_copy_SLConfigDescriptor( dst, src ) )
+    {
+        mp4sys_remove_ES_Descriptor( dst );
+        return NULL;
+    }
+    return dst;
+}
+
+static void mp4sys_get_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header )
+{
+    header->tag  = lsmash_bs_get_byte( bs );
+    uint8_t temp = lsmash_bs_get_byte( bs );
+    int nextByte = temp & 0x80;
+    uint32_t sizeOfInstance = temp & 0x7F;
+    while( nextByte )
+    {
+        temp = lsmash_bs_get_byte( bs );
+        nextByte = temp & 0x80;
+        sizeOfInstance = (sizeOfInstance << 7) | (temp & 0x7F);
+    }
+    header->size = sizeOfInstance;
+}
+
+static int mp4sys_get_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd )
+{
+    mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderSpecificInfo_t) );
+    if( !dsi )
+        return -1;
+    esd->decConfigDescr->decSpecificInfo = dsi;
+    mp4sys_get_descriptor_header( bs, &dsi->header );
+    if( dsi->header.size )
+    {
+        dsi->data = lsmash_bs_get_bytes( bs, dsi->header.size );
+        if( !dsi->data )
+        {
+            mp4sys_remove_DecoderSpecificInfo( esd );
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int mp4sys_get_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd )
+{
+    mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_DecoderConfigDescriptor_t) );
+    if( !dcd )
+        return -1;
+    esd->decConfigDescr = dcd;
+    mp4sys_get_descriptor_header( bs, &dcd->header );
+    dcd->objectTypeIndication = lsmash_bs_get_byte( bs );
+    uint8_t temp              = lsmash_bs_get_byte( bs );
+    dcd->streamType = (temp >> 2) & 0x3F;
+    dcd->upStream   = (temp >> 1) & 0x01;
+    dcd->reserved   =  temp       & 0x01;
+    dcd->bufferSizeDB         = lsmash_bs_get_be24( bs );
+    dcd->maxBitrate           = lsmash_bs_get_be32( bs );
+    dcd->avgBitrate           = lsmash_bs_get_be32( bs );
+    if( dcd->header.size > 13
+     && mp4sys_get_DecoderSpecificInfo( bs, esd ) )
+    {
+        mp4sys_remove_DecoderConfigDescriptor( esd );
+        return -1;
+    }
+    return 0;
+}
+
+static int mp4sys_get_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd )
+{
+    mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_SLConfigDescriptor_t) );
+    if( !slcd )
+        return -1;
+    esd->slConfigDescr = slcd;
+    mp4sys_get_descriptor_header( bs, &slcd->header );
+    slcd->predefined = lsmash_bs_get_byte( bs );
+    if( slcd->predefined == 0x00 )
+    {
+        uint8_t temp8              = lsmash_bs_get_byte( bs );
+        slcd->useAccessUnitStartFlag       = (temp8 >> 7) & 0x01;
+        slcd->useAccessUnitEndFlag         = (temp8 >> 6) & 0x01;
+        slcd->useRandomAccessPointFlag     = (temp8 >> 5) & 0x01;
+        slcd->hasRandomAccessUnitsOnlyFlag = (temp8 >> 4) & 0x01;
+        slcd->usePaddingFlag               = (temp8 >> 3) & 0x01;
+        slcd->useTimeStampsFlag            = (temp8 >> 2) & 0x01;
+        slcd->useIdleFlag                  = (temp8 >> 1) & 0x01;
+        slcd->durationFlag                 =  temp8       & 0x01;
+        slcd->timeStampResolution  = lsmash_bs_get_be32( bs );
+        slcd->OCRResolution        = lsmash_bs_get_be32( bs );
+        slcd->timeStampLength      = lsmash_bs_get_byte( bs );
+        slcd->OCRLength            = lsmash_bs_get_byte( bs );
+        slcd->AU_Length            = lsmash_bs_get_byte( bs );
+        slcd->instantBitrateLength = lsmash_bs_get_byte( bs );
+        uint16_t temp16            = lsmash_bs_get_be16( bs );
+        slcd->degradationPriorityLength = (temp16 >> 12) & 0x0F;
+        slcd->AU_seqNumLength           = (temp16 >>  7) & 0x1F;
+        slcd->packetSeqNumLength        = (temp16 >>  2) & 0x1F;
+        slcd->reserved                  =  temp16        & 0x03;
+    }
+    else if( slcd->predefined == 0x01 )
+    {
+        slcd->timeStampResolution  = 1000;
+        slcd->timeStampLength      = 32;
+    }
+    else if( slcd->predefined == 0x02 )
+        slcd->useTimeStampsFlag = 1;
+    if( slcd->durationFlag )
+    {
+        slcd->timeScale               = lsmash_bs_get_be32( bs );
+        slcd->accessUnitDuration      = lsmash_bs_get_be16( bs );
+        slcd->compositionUnitDuration = lsmash_bs_get_be16( bs );
+    }
+    if( !slcd->useTimeStampsFlag )
+    {
+        lsmash_bits_t *bits = lsmash_bits_create( bs );
+        if( !bits )
+            return -1;
+        slcd->startDecodingTimeStamp    = lsmash_bits_get( bits, slcd->timeStampLength );
+        slcd->startCompositionTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength );
+        lsmash_bits_cleanup( bits );
+    }
+    return 0;
+}
+
+mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs )
+{
+    mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)lsmash_malloc_zero( sizeof(mp4sys_ES_Descriptor_t) );
+    if( !esd )
+        return NULL;
+    mp4sys_get_descriptor_header( bs, &esd->header );
+    esd->ES_ID   = lsmash_bs_get_be16( bs );
+    uint8_t temp = lsmash_bs_get_byte( bs );
+    esd->streamDependenceFlag = (temp >> 7) & 0x01;
+    esd->URL_Flag             = (temp >> 6) & 0x01;
+    esd->OCRstreamFlag        = (temp >> 5) & 0x01;
+    esd->streamPriority       =  temp       & 0x1F;
+    if( esd->streamDependenceFlag )
+        esd->dependsOn_ES_ID = lsmash_bs_get_be16( bs );
+    if( esd->URL_Flag )
+    {
+        esd->URLlength = lsmash_bs_get_byte( bs );
+        for( uint8_t i = 0; i < esd->URLlength; i++ )
+            esd->URLstring[i] = lsmash_bs_get_byte( bs );
+    }
+    if( esd->OCRstreamFlag )
+        esd->OCR_ES_Id = lsmash_bs_get_be16( bs );
+    if( mp4sys_get_DecoderConfigDescriptor( bs, esd )
+     || mp4sys_get_SLConfigDescriptor( bs, esd ) )
+    {
+        mp4sys_remove_ES_Descriptor( esd );
+        return NULL;
+    }
+    return esd;
+}
+
+static uint8_t *mp4sys_export_DecoderSpecificInfo( mp4sys_ES_Descriptor_t *esd, uint32_t *dsi_payload_length )
+{
+    if( !esd || !esd->decConfigDescr || !esd->decConfigDescr->decSpecificInfo )
+        return NULL;
+    mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)esd->decConfigDescr->decSpecificInfo;
+    uint8_t *dsi_payload = NULL;
+    /* DecoderSpecificInfo can be absent. */
+    if( dsi->header.size )
+    {
+        dsi_payload = lsmash_memdup( dsi->data, dsi->header.size );
+        if( !dsi_payload )
+            return NULL;
+    }
+    if( dsi_payload_length )
+        *dsi_payload_length = dsi->header.size;
+    return dsi_payload;
+}
+
+/* Sumamry is needed to decide ProfileLevelIndication.
+ * Currently, support audio's only. */
+int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd )
+{
+    uint32_t dsi_payload_length = UINT32_MAX;       /* arbitrary */
+    uint8_t *dsi_payload = mp4sys_export_DecoderSpecificInfo( esd, &dsi_payload_length );
+    if( !dsi_payload && dsi_payload_length )
+        return -1;
+    if( dsi_payload_length && mp4a_setup_summary_from_AudioSpecificConfig( summary, dsi_payload, dsi_payload_length ) )
+    {
+        free( dsi_payload );
+        return -1;
+    }
+    return 0;
+}
+#endif /* LSMASH_DEMUXER_ENABLED */
+
+/**** following functions are for facilitation purpose ****/
+
+mp4sys_ES_Descriptor_t* mp4sys_setup_ES_Descriptor( mp4sys_ES_Descriptor_params_t* params )
+{
+    if( !params )
+        return NULL;
+    mp4sys_ES_Descriptor_t* esd = mp4sys_create_ES_Descriptor( params->ES_ID );
+    if( !esd )
+        return NULL;
+    if( mp4sys_add_SLConfigDescriptor( esd )
+        || mp4sys_add_DecoderConfigDescriptor( esd, params->objectTypeIndication, params->streamType, params->bufferSizeDB, params->maxBitrate, params->avgBitrate )
+        || ( params->dsi_payload && params->dsi_payload_length != 0 && mp4sys_add_DecoderSpecificInfo( esd, params->dsi_payload, params->dsi_payload_length ) )
+    ){
+        mp4sys_remove_ES_Descriptor( esd );
+        return NULL;
+    }
+    return esd;
+}
diff --git a/output/mp4/mp4sys.h b/output/mp4/mp4sys.h
new file mode 100644
index 0000000..edf38cc
--- /dev/null
+++ b/output/mp4/mp4sys.h
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * mp4sys.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef MP4SYS_H
+#define MP4SYS_H
+
+/***************************************************************************
+    MPEG-4 Systems
+***************************************************************************/
+
+/* ODProfileLevelIndication */
+typedef enum {
+    MP4SYS_OD_PLI_Forbidden     = 0x00, /* Forbidden */
+    MP4SYS_OD_PLI_NOT_SPECIFIED = 0xFE, /* no OD profile specified */
+    MP4SYS_OD_PLI_NONE_REQUIRED = 0xFF, /* no OD capability required */
+} mp4sys_ODProfileLevelIndication;
+
+/* sceneProfileLevelIndication */
+typedef enum {
+    MP4SYS_SCENE_PLI_RESERVED      = 0x00, /* Reserved for ISO use */
+    MP4SYS_SCENE_PLI_Simple2D_L1   = 0x01, /* Simple 2D L1 */
+    MP4SYS_SCENE_PLI_Simple2D_L2   = 0x02, /* Simple 2D L2 */
+    MP4SYS_SCENE_PLI_Audio_L1      = 0x03, /* Audio L1 */
+    MP4SYS_SCENE_PLI_Audio_L2      = 0x04, /* Audio L2 */
+    MP4SYS_SCENE_PLI_Audio_L3      = 0x05, /* Audio L3 */
+    MP4SYS_SCENE_PLI_Audio_L4      = 0x06, /* Audio L4 */
+    MP4SYS_SCENE_PLI_3D_Audio_L1   = 0x07, /* 3D Audio L1 */
+    MP4SYS_SCENE_PLI_3D_Audio_L2   = 0x08, /* 3D Audio L2 */
+    MP4SYS_SCENE_PLI_3D_Audio_L3   = 0x09, /* 3D Audio L3 */
+    MP4SYS_SCENE_PLI_3D_Audio_L4   = 0x0A, /* 3D Audio L4 */
+    MP4SYS_SCENE_PLI_Basic2D_L1    = 0x0B, /* Basic 2D L1 */
+    MP4SYS_SCENE_PLI_Core2D_L1     = 0x0C, /* Core 2D L1 */
+    MP4SYS_SCENE_PLI_Core2D_L2     = 0x0D, /* Core 2D L2 */
+    MP4SYS_SCENE_PLI_Advanced2D_L1 = 0x0E, /* Advanced 2D L1 */
+    MP4SYS_SCENE_PLI_Advanced2D_L2 = 0x0F, /* Advanced 2D L2 */
+    MP4SYS_SCENE_PLI_Advanced2D_L3 = 0x10, /* Advanced 2D L3 */
+    MP4SYS_SCENE_PLI_Main2D_L1     = 0x11, /* Main 2D L1 */
+    MP4SYS_SCENE_PLI_Main2D_L2     = 0x12, /* Main 2D L2 */
+    MP4SYS_SCENE_PLI_Main2D_L3     = 0x13, /* Main 2D L3 */
+    MP4SYS_SCENE_PLI_NOT_SPECIFIED = 0xFE, /* no scene profile specified */
+    MP4SYS_SCENE_PLI_NONE_REQUIRED = 0xFF, /* no scene capability required */
+} mp4sys_sceneProfileLevelIndication;
+
+/* 14496-2 Profile and level indication and restrictions */
+typedef enum {
+    MP4SYS_VISUAL_PLI_Reserved                       = 0x00, /* 0b00000000, Reserved */
+    MP4SYS_VISUAL_PLI_Simple_PL1                     = 0x01, /* 0b00000001, Simple Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Simple_PL2                     = 0x02, /* 0b00000010, Simple Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Simple_PL3                     = 0x03, /* 0b00000011, Simple Profile/Level 3 */
+    MP4SYS_VISUAL_PLI_Simple_Scalable_PL1            = 0x11, /* 0b00010001, Simple Scalable Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Simple_Scalable_PL2            = 0x12, /* 0b00010010, Simple Scalable Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Core_PL1                       = 0x21, /* 0b00100001, Core Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Core_PL2                       = 0x22, /* 0b00100010, Core Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Main_PL2                       = 0x32, /* 0b00110010, Main Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Main_PL3                       = 0x33, /* 0b00110011, Main Profile/Level 3 */
+    MP4SYS_VISUAL_PLI_Main_PL4                       = 0x34, /* 0b00110100, Main Profile/Level 4 */
+    MP4SYS_VISUAL_PLI_N_bit_PL2                      = 0x42, /* 0b01000010, N-bit Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Scalable_Texture_PL1           = 0x51, /* 0b01010001, Scalable Texture Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Simple_Face_Animation_PL1      = 0x61, /* 0b01100001, Simple Face Animation Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Simple_Face_Animation_PL2      = 0x62, /* 0b01100010, Simple Face Animation Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Simple_FBA_PL1                 = 0x63, /* 0b01100011, Simple FBA Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Simple_FBA_PL2                 = 0x64, /* 0b01100100, Simple FBA Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Basic_Animated_Texture_PL1     = 0x71, /* 0b01110001, Basic Animated Texture Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Basic_Animated_Texture_PL2     = 0x72, /* 0b01110010, Basic Animated Texture Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_H264_AVC                       = 0x7F, /* ISO/IEC 14496-10 Advanced Video Codec / H.264, defined in ISO/IEC 14496-1:2001/Amd.7:2004 */
+                                                             /* NOTE: Some other implementations seem to use 0x15(0b00010101) for AVC, but I think that's wrong. */
+    MP4SYS_VISUAL_PLI_Hybrid_PL1                     = 0x81, /* 0b10000001, Hybrid Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Hybrid_PL2                     = 0x82, /* 0b10000010, Hybrid Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL1  = 0x91, /* 0b10010001, Advanced Real Time Simple Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL2  = 0x92, /* 0b10010010, Advanced Real Time Simple Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL3  = 0x93, /* 0b10010011, Advanced Real Time Simple Profile/Level 3 */
+    MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL4  = 0x94, /* 0b10010100, Advanced Real Time Simple Profile/Level 4 */
+    MP4SYS_VISUAL_PLI_Core_Scalable_PL1              = 0xA1, /* 0b10100001, Core Scalable Profile/Level1 */
+    MP4SYS_VISUAL_PLI_Core_Scalable_PL2              = 0xA2, /* 0b10100010, Core Scalable Profile/Level2 */
+    MP4SYS_VISUAL_PLI_Core_Scalable_PL3              = 0xA3, /* 0b10100011, Core Scalable Profile/Level3 */
+    MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL1 = 0xB1, /* 0b10110001, Advanced Coding Efficiency Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL2 = 0xB2, /* 0b10110010, Advanced Coding Efficiency Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL3 = 0xB3, /* 0b10110011, Advanced Coding Efficiency Profile/Level 3 */
+    MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL4 = 0xB4, /* 0b10110100, Advanced Coding Efficiency Profile/Level 4 */
+    MP4SYS_VISUAL_PLI_Advanced_Core_PL1              = 0xC1, /* 0b11000001, Advanced Core Profile/Level 1 */
+    MP4SYS_VISUAL_PLI_Advanced_Core_PL2              = 0xC2, /* 0b11000010, Advanced Core Profile/Level 2 */
+    MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L1   = 0xD1, /* 0b11010001, Advanced Scalable Texture/Level1 */
+    MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L2   = 0xD2, /* 0b11010010, Advanced Scalable Texture/Level2 */
+    MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L3   = 0xD3, /* 0b11010011, Advanced Scalable Texture/Level3 */
+    MP4SYS_VISUAL_PLI_NOT_SPECIFIED                  = 0xFE, /* no visual profile specified */
+    MP4SYS_VISUAL_PLI_NONE_REQUIRED                  = 0xFF, /* no visual capability required */
+} mp4sys_visualProfileLevelIndication;
+
+/* graphicsProfileLevelIndication */
+typedef enum {
+    MP4SYS_GRAPHICS_PLI_RESERVED         = 0x00, /* Reserved for ISO use */
+    MP4SYS_GRAPHICS_PLI_Simple2D_L1      = 0x01, /* Simple2D profile L1 */
+    MP4SYS_GRAPHICS_PLI_Simple2D_Text_L1 = 0x02, /* Simple 2D + Text profile L1 */
+    MP4SYS_GRAPHICS_PLI_Simple2D_Text_L2 = 0x03, /* Simple 2D + Text profile L2 */
+    MP4SYS_GRAPHICS_PLI_Core2D_L1        = 0x04, /* Core 2D profile L1 */
+    MP4SYS_GRAPHICS_PLI_Core2D_L2        = 0x05, /* Core 2D profile L2 */
+    MP4SYS_GRAPHICS_PLI_Advanced2D_L1    = 0x06, /* Advanced 2D profile L1 */
+    MP4SYS_GRAPHICS_PLI_Advanced2D_L2    = 0x07, /* Advanced 2D profile L2 */
+    MP4SYS_GRAPHICS_PLI_NOT_SPECIFIED    = 0xFE, /* no graphics profile specified */
+    MP4SYS_GRAPHICS_PLI_NONE_REQUIRED    = 0xFF, /* no graphics capability required */
+} mp4sys_graphicsProfileLevelIndication;
+
+/* Just for mp4sys_setup_ES_Descriptor, to facilitate to make ES_Descriptor */
+typedef struct {
+    uint16_t ES_ID;                 /* Maybe 0 in stsd(esds), or alternatively, lower 16 bits of the TrackID */
+    lsmash_mp4sys_object_type_indication objectTypeIndication;
+    lsmash_mp4sys_stream_type streamType;
+    uint32_t bufferSizeDB;          /* byte unit, NOT bit unit. */
+    uint32_t maxBitrate;
+    uint32_t avgBitrate;            /* 0 if VBR */
+    void* dsi_payload;              /* AudioSpecificConfig or so */
+    uint32_t dsi_payload_length ;   /* size of dsi_payload */
+} mp4sys_ES_Descriptor_params_t;
+
+#ifndef MP4SYS_INTERNAL
+
+#include "utils.h"
+
+typedef void mp4sys_ES_Descriptor_t;
+typedef void mp4sys_ObjectDescriptor_t;
+
+int mp4sys_remove_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd );
+int mp4sys_remove_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd );
+int mp4sys_remove_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd );
+int mp4sys_remove_ES_Descriptor( mp4sys_ES_Descriptor_t* esd );
+int mp4sys_remove_ES_ID_Incs( mp4sys_ObjectDescriptor_t* od );
+int mp4sys_remove_ObjectDescriptor( mp4sys_ObjectDescriptor_t* od );
+
+int mp4sys_add_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd, void* dsi_payload, uint32_t dsi_payload_length );
+/*
+    bufferSizeDB is byte unit, NOT bit unit.
+    avgBitrate is 0 if VBR
+*/
+int mp4sys_add_DecoderConfigDescriptor(
+    mp4sys_ES_Descriptor_t* esd,
+    lsmash_mp4sys_object_type_indication objectTypeIndication,
+    lsmash_mp4sys_stream_type streamType,
+    uint32_t bufferSizeDB,
+    uint32_t maxBitrate,
+    uint32_t avgBitrate
+);
+int mp4sys_add_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd );
+int mp4sys_add_ES_ID_Inc( mp4sys_ObjectDescriptor_t* od, uint32_t Track_ID);
+
+/* ES_ID might be usually 0 or lower 16 bits of the TrackID
+   14496-14 says, "set to 0 as stored; when built into a stream, the lower 16 bits of the TrackID are used."
+   I'm not sure about actual meaning of "stored" and "built into a stream", but maybe 0 will do in stsd(esds). */
+mp4sys_ES_Descriptor_t* mp4sys_create_ES_Descriptor( uint16_t ES_ID );
+mp4sys_ObjectDescriptor_t* mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID );
+int mp4sys_to_InitialObjectDescriptor(
+    mp4sys_ObjectDescriptor_t* od,
+    uint8_t include_inline_pli,
+    mp4sys_ODProfileLevelIndication od_pli,
+    mp4sys_sceneProfileLevelIndication scene_pli,
+    mp4a_audioProfileLevelIndication audio_pli,
+    mp4sys_visualProfileLevelIndication visual_pli,
+    mp4sys_graphicsProfileLevelIndication graph_pli
+);
+
+uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd );
+uint32_t mp4sys_update_ObjectDescriptor_size( mp4sys_ObjectDescriptor_t* od );
+
+int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t* esd );
+int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od );
+
+int mp4sys_update_DecoderConfigDescriptor(
+    mp4sys_ES_Descriptor_t* esd,
+    uint32_t bufferSizeDB,
+    uint32_t maxBitrate,
+    uint32_t avgBitrate
+);
+
+#ifdef LSMASH_DEMUXER_ENABLED
+mp4sys_ES_Descriptor_t *mp4sys_duplicate_ES_Descriptor( mp4sys_ES_Descriptor_t *src );
+
+mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs );
+
+int mp4sys_setup_summary_from_DecoderSpecificInfo( lsmash_audio_summary_t *summary, mp4sys_ES_Descriptor_t *esd );
+#endif
+
+/* to facilitate to make ES_Descriptor */
+mp4sys_ES_Descriptor_t* mp4sys_setup_ES_Descriptor( mp4sys_ES_Descriptor_params_t* params );
+
+#endif /* #ifndef MP4SYS_INTERNAL */
+
+#endif /* #ifndef MP4SYS_H */
diff --git a/output/mp4/summary.c b/output/mp4/summary.c
new file mode 100644
index 0000000..06a770f
--- /dev/null
+++ b/output/mp4/summary.c
@@ -0,0 +1,121 @@
+/*****************************************************************************
+ * summary.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Takashi Hirata <silverfilain@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "importer.h"
+#include "mp4a.h"
+
+/***************************************************************************
+    summary and AudioSpecificConfig relative tools
+***************************************************************************/
+
+/* create AudioSpecificConfig as memory block from summary, and set it into that summary itself */
+int lsmash_setup_AudioSpecificConfig( lsmash_audio_summary_t* summary )
+{
+    if( !summary )
+        return -1;
+    lsmash_bs_t* bs = lsmash_bs_create( NULL ); /* no file writing */
+    if( !bs )
+        return -1;
+    mp4a_AudioSpecificConfig_t *asc =
+        mp4a_create_AudioSpecificConfig( summary->aot,
+                                         summary->frequency,
+                                         summary->channels,
+                                         summary->sbr_mode,
+                                         summary->exdata,
+                                         summary->exdata_length );
+    if( !asc )
+    {
+        lsmash_bs_cleanup( bs );
+        return -1;
+    }
+    mp4a_put_AudioSpecificConfig( bs, asc );
+    void* new_asc;
+    uint32_t new_length;
+    new_asc = lsmash_bs_export_data( bs, &new_length );
+    mp4a_remove_AudioSpecificConfig( asc );
+    lsmash_bs_cleanup( bs );
+    if( !new_asc )
+        return -1;
+    if( summary->exdata )
+        free( summary->exdata );
+    summary->exdata = new_asc;
+    summary->exdata_length = new_length;
+    return 0 ;
+}
+
+/* Copy exdata into summary from memory block */
+int lsmash_summary_add_exdata( lsmash_summary_t *summary, void* exdata, uint32_t exdata_length )
+{
+    if( !summary )
+        return -1;
+    /* atomic operation */
+    void* new_exdata = NULL;
+    if( exdata && exdata_length != 0 )
+    {
+        new_exdata = lsmash_memdup( exdata, exdata_length );
+        if( !new_exdata )
+            return -1;
+        summary->exdata_length = exdata_length;
+    }
+    else
+        summary->exdata_length = 0;
+
+    if( summary->exdata )
+        free( summary->exdata );
+    summary->exdata = new_exdata;
+    return 0;
+}
+
+lsmash_summary_t *lsmash_create_summary( lsmash_mp4sys_stream_type stream_type )
+{
+    size_t summary_size;
+    switch( stream_type )
+    {
+        case MP4SYS_STREAM_TYPE_VisualStream :
+            summary_size = sizeof(lsmash_video_summary_t);
+            break;
+        case MP4SYS_STREAM_TYPE_AudioStream :
+            summary_size = sizeof(lsmash_audio_summary_t);
+            break;
+        default :
+            return NULL;
+    }
+    lsmash_summary_t *summary = (lsmash_summary_t *)lsmash_malloc_zero( summary_size );
+    if( !summary )
+        return NULL;
+    summary->stream_type = stream_type;
+    return summary;
+}
+
+void lsmash_cleanup_summary( lsmash_summary_t *summary )
+{
+    if( !summary )
+        return;
+    if( summary->exdata )
+        free( summary->exdata );
+    free( summary );
+}
diff --git a/output/mp4/utils.c b/output/mp4/utils.c
new file mode 100644
index 0000000..eff3abf
--- /dev/null
+++ b/output/mp4/utils.c
@@ -0,0 +1,728 @@
+/*****************************************************************************
+ * utils.c:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#include "internal.h" /* must be placed first */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "utils.h"
+
+uint64_t lsmash_bs_get_pos( lsmash_bs_t *bs )
+{
+    return bs->pos;
+}
+
+void lsmash_bs_empty( lsmash_bs_t *bs )
+{
+    if( !bs )
+        return;
+    memset( bs->data, 0, bs->alloc );
+    bs->store = 0;
+    bs->pos = 0;
+}
+
+void lsmash_bs_free( lsmash_bs_t *bs )
+{
+    if( bs->data )
+        free( bs->data );
+    bs->data = NULL;
+    bs->alloc = 0;
+    bs->store = 0;
+    bs->pos = 0;
+}
+
+void lsmash_bs_alloc( lsmash_bs_t *bs, uint64_t size )
+{
+    if( (bs->alloc >= size) || bs->error )
+        return;
+    uint64_t alloc = size + (1<<16);
+    uint8_t *data;
+    if( !bs->data )
+        data = malloc( alloc );
+    else
+        data = realloc( bs->data, alloc );
+    if( !data )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return;
+    }
+    bs->data  = data;
+    bs->alloc = alloc;
+}
+
+/*---- bitstream writer ----*/
+void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value )
+{
+    lsmash_bs_alloc( bs, bs->store + 1 );
+    if( bs->error )
+        return;
+    bs->data[bs->store ++] = value;
+}
+
+void lsmash_bs_put_bytes( lsmash_bs_t *bs, void *value, uint32_t size )
+{
+    if( !size || !value )
+        return;
+    lsmash_bs_alloc( bs, bs->store + size );
+    if( bs->error )
+        return;
+    memcpy( bs->data + bs->store, value, size );
+    bs->store += size;
+}
+
+void lsmash_bs_put_be16( lsmash_bs_t *bs, uint16_t value )
+{
+    lsmash_bs_put_byte( bs, (uint8_t)((value>>8)&0xff) );
+    lsmash_bs_put_byte( bs, (uint8_t)(value&0xff) );
+}
+
+void lsmash_bs_put_be24( lsmash_bs_t *bs, uint32_t value )
+{
+    lsmash_bs_put_byte( bs, (uint8_t)((value>>16)&0xff) );
+    lsmash_bs_put_be16( bs, (uint16_t)(value&0xffff) );
+}
+
+void lsmash_bs_put_be32( lsmash_bs_t *bs, uint32_t value )
+{
+    lsmash_bs_put_be16( bs, (uint16_t)((value>>16)&0xffff) );
+    lsmash_bs_put_be16( bs, (uint16_t)(value&0xffff) );
+}
+
+void lsmash_bs_put_be64( lsmash_bs_t *bs, uint64_t value )
+{
+    lsmash_bs_put_be32( bs, (uint32_t)((value>>32)&0xffffffff) );
+    lsmash_bs_put_be32( bs, (uint32_t)(value&0xffffffff) );
+}
+
+void lsmash_bs_put_byte_from_64( lsmash_bs_t *bs, uint64_t value )
+{
+    lsmash_bs_put_byte( bs, (uint8_t)(value&0xff) );
+}
+
+void lsmash_bs_put_be16_from_64( lsmash_bs_t *bs, uint64_t value )
+{
+    lsmash_bs_put_be16( bs, (uint16_t)(value&0xffff) );
+}
+
+void lsmash_bs_put_be24_from_64( lsmash_bs_t *bs, uint64_t value )
+{
+    lsmash_bs_put_be24( bs, (uint32_t)(value&0xffffff) );
+}
+
+void lsmash_bs_put_be32_from_64( lsmash_bs_t *bs, uint64_t value )
+{
+    lsmash_bs_put_be32( bs, (uint32_t)(value&0xffffffff) );
+}
+
+int lsmash_bs_write_data( lsmash_bs_t *bs )
+{
+    if( !bs )
+        return -1;
+    if( !bs->store || !bs->data )
+        return 0;
+    if( bs->error || !bs->stream || fwrite( bs->data, 1, bs->store, bs->stream ) != bs->store )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return -1;
+    }
+    bs->written += bs->store;
+    bs->store = 0;
+    return 0;
+}
+
+lsmash_bs_t* lsmash_bs_create( char* filename )
+{
+    lsmash_bs_t* bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) );
+    if( !bs )
+        return NULL;
+    if( filename && (bs->stream = fopen( filename, "wb" )) == NULL )
+    {
+        free( bs );
+        return NULL;
+    }
+    return bs;
+}
+
+void lsmash_bs_cleanup( lsmash_bs_t *bs )
+{
+    if( !bs )
+        return;
+    if( bs->stream )
+        fclose( bs->stream );
+    lsmash_bs_free( bs );
+    free( bs );
+}
+
+void* lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t* length )
+{
+    if( !bs || !bs->data || bs->store == 0 || bs->error )
+        return NULL;
+    void *buf = lsmash_memdup( bs->data, bs->store );
+    if( !buf )
+        return NULL;
+    if( length )
+        *length = bs->store;
+    return buf;
+}
+/*---- ----*/
+
+/*---- bitstream reader ----*/
+uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs )
+{
+    if( bs->error || !bs->data )
+        return 0;
+    if( bs->pos + 1 > bs->store )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return 0;
+    }
+    return bs->data[bs->pos ++];
+}
+
+uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size )
+{
+    if( bs->error || !size )
+        return NULL;
+    if( bs->pos + size > bs->store )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return NULL;
+    }
+    uint8_t *value = lsmash_memdup( bs->data + bs->pos, size );
+    if( !value )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return NULL;
+    }
+    bs->pos += size;
+    return value;
+}
+
+uint16_t lsmash_bs_get_be16( lsmash_bs_t *bs )
+{
+    uint16_t    value = lsmash_bs_get_byte( bs );
+    return (value<<8) | lsmash_bs_get_byte( bs );
+}
+
+uint32_t lsmash_bs_get_be24( lsmash_bs_t *bs )
+{
+    uint32_t     value = lsmash_bs_get_byte( bs );
+    return (value<<16) | lsmash_bs_get_be16( bs );
+}
+
+uint32_t lsmash_bs_get_be32( lsmash_bs_t *bs )
+{
+    uint32_t     value = lsmash_bs_get_be16( bs );
+    return (value<<16) | lsmash_bs_get_be16( bs );
+}
+
+uint64_t lsmash_bs_get_be64( lsmash_bs_t *bs )
+{
+    uint64_t     value = lsmash_bs_get_be32( bs );
+    return (value<<32) | lsmash_bs_get_be32( bs );
+}
+
+uint64_t lsmash_bs_get_byte_to_64( lsmash_bs_t *bs )
+{
+    return lsmash_bs_get_byte( bs );
+}
+
+uint64_t lsmash_bs_get_be16_to_64( lsmash_bs_t *bs )
+{
+    return lsmash_bs_get_be16( bs );
+}
+
+uint64_t lsmash_bs_get_be24_to_64( lsmash_bs_t *bs )
+{
+    return lsmash_bs_get_be24( bs );
+}
+
+uint64_t lsmash_bs_get_be32_to_64( lsmash_bs_t *bs )
+{
+    return lsmash_bs_get_be32( bs );
+}
+
+int lsmash_bs_read_data( lsmash_bs_t *bs, uint32_t size )
+{
+    if( !bs )
+        return -1;
+    if( !size )
+        return 0;
+    lsmash_bs_alloc( bs, bs->store + size );
+    if( bs->error || !bs->stream )
+    {
+        lsmash_bs_free( bs );
+        bs->error = 1;
+        return -1;
+    }
+    uint64_t read_size = fread( bs->data + bs->store, 1, size, bs->stream );
+    if( read_size != size && !feof( bs->stream ) )
+    {
+        bs->error = 1;
+        return -1;
+    }
+    bs->store += read_size;
+    return 0;
+}
+
+int lsmash_bs_import_data( lsmash_bs_t *bs, void* data, uint32_t length )
+{
+    if( !bs || bs->error || !data || length == 0 )
+        return -1;
+    lsmash_bs_alloc( bs, bs->store + length );
+    if( bs->error || !bs->data ) /* means, failed to alloc. */
+    {
+        lsmash_bs_free( bs );
+        return -1;
+    }
+    memcpy( bs->data + bs->store, data, length );
+    bs->store += length;
+    return 0;
+}
+/*---- ----*/
+
+/*---- bitstream ----*/
+void lsmash_bits_init( lsmash_bits_t *bits, lsmash_bs_t *bs )
+{
+    debug_if( !bits || !bs )
+        return;
+    bits->bs = bs;
+    bits->store = 0;
+    bits->cache = 0;
+}
+
+lsmash_bits_t *lsmash_bits_create( lsmash_bs_t *bs )
+{
+    debug_if( !bs )
+        return NULL;
+    lsmash_bits_t *bits = (lsmash_bits_t *)malloc( sizeof(lsmash_bits_t) );
+    if( !bits )
+        return NULL;
+    lsmash_bits_init( bits, bs );
+    return bits;
+}
+
+void lsmash_bits_empty( lsmash_bits_t *bits )
+{
+    debug_if( !bits )
+        return;
+    lsmash_bs_empty( bits->bs );
+    bits->store = 0;
+    bits->cache = 0;
+}
+
+#define BITS_IN_BYTE 8
+void lsmash_bits_put_align( lsmash_bits_t *bits )
+{
+    debug_if( !bits )
+        return;
+    if( !bits->store )
+        return;
+    lsmash_bs_put_byte( bits->bs, bits->cache << ( BITS_IN_BYTE - bits->store ) );
+}
+
+void lsmash_bits_get_align( lsmash_bits_t *bits )
+{
+    debug_if( !bits )
+        return;
+    bits->store = 0;
+    bits->cache = 0;
+}
+
+/* Must be used ONLY for bits struct created with isom_create_bits.
+   Otherwise, just free() the bits struct. */
+void lsmash_bits_cleanup( lsmash_bits_t *bits )
+{
+    debug_if( !bits )
+        return;
+    free( bits );
+}
+
+/* we can change value's type to unsigned int for 64-bit operation if needed. */
+static inline uint8_t lsmash_bits_mask_lsb8( uint32_t value, uint32_t width )
+{
+    return (uint8_t)( value & ~( ~0U << width ) );
+}
+
+/* We can change value's type to unsigned int for 64-bit operation if needed. */
+void lsmash_bits_put( lsmash_bits_t *bits, uint32_t value, uint32_t width )
+{
+    debug_if( !bits || !width )
+        return;
+    if( bits->store )
+    {
+        if( bits->store + width < BITS_IN_BYTE )
+        {
+            /* cache can contain all of value's bits. */
+            bits->cache <<= width;
+            bits->cache |= lsmash_bits_mask_lsb8( value, width );
+            bits->store += width;
+            return;
+        }
+        /* flush cache with value's some leading bits. */
+        uint32_t free_bits = BITS_IN_BYTE - bits->store;
+        bits->cache <<= free_bits;
+        bits->cache |= lsmash_bits_mask_lsb8( value >> (width -= free_bits), free_bits );
+        lsmash_bs_put_byte( bits->bs, bits->cache );
+        bits->store = 0;
+        bits->cache = 0;
+    }
+    /* cache is empty here. */
+    /* byte unit operation. */
+    while( width > BITS_IN_BYTE )
+        lsmash_bs_put_byte( bits->bs, (uint8_t)(value >> (width -= BITS_IN_BYTE)) );
+    /* bit unit operation for residual. */
+    if( width )
+    {
+        bits->cache = lsmash_bits_mask_lsb8( value, width );
+        bits->store = width;
+    }
+}
+
+/* We can change value's type to unsigned int for 64-bit operation if needed. */
+uint32_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width )
+{
+    debug_if( !bits || !width )
+        return 0;
+    uint32_t value = 0;
+    if( bits->store )
+    {
+        if( bits->store >= width )
+        {
+            /* cache contains all of bits required. */
+            bits->store -= width;
+            return lsmash_bits_mask_lsb8( bits->cache >> bits->store, width );
+        }
+        /* fill value's leading bits with cache's residual. */
+        value = lsmash_bits_mask_lsb8( bits->cache, bits->store );
+        width -= bits->store;
+        bits->store = 0;
+        bits->cache = 0;
+    }
+    /* cache is empty here. */
+    /* byte unit operation. */
+    while( width > BITS_IN_BYTE )
+    {
+        value <<= BITS_IN_BYTE;
+        width -= BITS_IN_BYTE;
+        value |= lsmash_bs_get_byte( bits->bs );
+    }
+    /* bit unit operation for residual. */
+    if( width )
+    {
+        bits->cache = lsmash_bs_get_byte( bits->bs );
+        bits->store = BITS_IN_BYTE - width;
+        value <<= width;
+        value |= lsmash_bits_mask_lsb8( bits->cache >> bits->store, width );
+    }
+    return value;
+}
+
+/****
+ bitstream with bytestream for adhoc operation
+****/
+
+lsmash_bits_t* lsmash_bits_adhoc_create()
+{
+    lsmash_bs_t* bs = lsmash_bs_create( NULL ); /* no file writing */
+    if( !bs )
+        return NULL;
+    lsmash_bits_t* bits = lsmash_bits_create( bs );
+    if( !bits )
+    {
+        lsmash_bs_cleanup( bs );
+        return NULL;
+    }
+    return bits;
+}
+
+void lsmash_bits_adhoc_cleanup( lsmash_bits_t* bits )
+{
+    if( !bits )
+        return;
+    lsmash_bs_cleanup( bits->bs );
+    lsmash_bits_cleanup( bits );
+}
+
+void* lsmash_bits_export_data( lsmash_bits_t* bits, uint32_t* length )
+{
+    lsmash_bits_put_align( bits );
+    return lsmash_bs_export_data( bits->bs, length );
+}
+
+int lsmash_bits_import_data( lsmash_bits_t* bits, void* data, uint32_t length )
+{
+    return lsmash_bs_import_data( bits->bs, data, length );
+}
+/*---- ----*/
+
+/*---- list ----*/
+void lsmash_init_entry_list( lsmash_entry_list_t *list )
+{
+    list->head = NULL;
+    list->tail = NULL;
+    list->last_accessed_entry = NULL;
+    list->last_accessed_number = 0;
+    list->entry_count = 0;
+}
+
+lsmash_entry_list_t *lsmash_create_entry_list( void )
+{
+    lsmash_entry_list_t *list = malloc( sizeof(lsmash_entry_list_t) );
+    if( !list )
+        return NULL;
+    lsmash_init_entry_list( list );
+    return list;
+}
+
+int lsmash_add_entry( lsmash_entry_list_t *list, void *data )
+{
+    if( !list )
+        return -1;
+    lsmash_entry_t *entry = malloc( sizeof(lsmash_entry_t) );
+    if( !entry )
+        return -1;
+    entry->next = NULL;
+    entry->prev = list->tail;
+    entry->data = data;
+    if( list->head )
+        list->tail->next = entry;
+    else
+        list->head = entry;
+    list->tail = entry;
+    list->entry_count += 1;
+    return 0;
+}
+
+int lsmash_remove_entry_direct( lsmash_entry_list_t *list, lsmash_entry_t *entry, void* eliminator )
+{
+    if( !entry )
+        return -1;
+    if( !eliminator )
+        eliminator = free;
+    lsmash_entry_t *next = entry->next;
+    lsmash_entry_t *prev = entry->prev;
+    if( entry == list->head )
+        list->head = next;
+    else
+        prev->next = next;
+    if( entry == list->tail )
+        list->tail = prev;
+    else
+        next->prev = prev;
+    if( entry->data )
+        ((lsmash_entry_data_eliminator)eliminator)( entry->data );
+    if( entry == list->last_accessed_entry )
+    {
+        if( next )
+            list->last_accessed_entry = next;
+        else if( prev )
+        {
+            list->last_accessed_entry = prev;
+            list->last_accessed_number -= 1;
+        }
+        else
+        {
+            list->last_accessed_entry = NULL;
+            list->last_accessed_number = 0;
+        }
+    }
+    else
+    {
+        /* We can't know the current entry number immediately,
+         * so discard the last accessed entry info because time is wasted to know it. */
+        list->last_accessed_entry = NULL;
+        list->last_accessed_number = 0;
+    }
+    free( entry );
+    list->entry_count -= 1;
+    return 0;
+}
+
+int lsmash_remove_entry( lsmash_entry_list_t *list, uint32_t entry_number, void* eliminator )
+{
+    lsmash_entry_t *entry = lsmash_get_entry( list, entry_number );
+    return lsmash_remove_entry_direct( list, entry, eliminator );
+}
+
+void lsmash_remove_entries( lsmash_entry_list_t *list, void* eliminator )
+{
+    if( !list )
+        return;
+    if( !eliminator )
+        eliminator = free;
+    for( lsmash_entry_t *entry = list->head; entry; )
+    {
+        lsmash_entry_t *next = entry->next;
+        if( entry->data )
+            ((lsmash_entry_data_eliminator)eliminator)( entry->data );
+        free( entry );
+        entry = next;
+    }
+    lsmash_init_entry_list( list );
+}
+
+void lsmash_remove_list( lsmash_entry_list_t *list, void* eliminator )
+{
+    if( !list )
+        return;
+    lsmash_remove_entries( list, eliminator );
+    free( list );
+}
+
+lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number )
+{
+    if( !list || !entry_number || entry_number > list->entry_count )
+        return NULL;
+    int shortcut = 1;
+    lsmash_entry_t *entry = NULL;
+    if( list->last_accessed_entry )
+    {
+        if( entry_number == list->last_accessed_number )
+            entry = list->last_accessed_entry;
+        else if( entry_number == list->last_accessed_number + 1 )
+            entry = list->last_accessed_entry->next;
+        else if( entry_number == list->last_accessed_number - 1 )
+            entry = list->last_accessed_entry->prev;
+        else
+            shortcut = 0;
+    }
+    else
+        shortcut = 0;
+    if( !shortcut )
+    {
+        if( entry_number <= (list->entry_count >> 1) )
+        {
+            /* Look for from the head. */
+            uint32_t distance_plus_one = entry_number;
+            for( entry = list->head; entry && --distance_plus_one; entry = entry->next );
+        }
+        else
+        {
+            /* Look for from the tail. */
+            uint32_t distance = list->entry_count - entry_number;
+            for( entry = list->tail; entry && distance--; entry = entry->prev );
+        }
+    }
+    if( entry )
+    {
+        list->last_accessed_entry = entry;
+        list->last_accessed_number = entry_number;
+    }
+    return entry;
+}
+
+void *lsmash_get_entry_data( lsmash_entry_list_t *list, uint32_t entry_number )
+{
+    lsmash_entry_t *entry = lsmash_get_entry( list, entry_number );
+    return entry ? entry->data : NULL;
+}
+/*---- ----*/
+
+/*---- type ----*/
+double lsmash_fixed2double( uint64_t value, int frac_width )
+{
+    return value / (double)(1ULL << frac_width);
+}
+
+float lsmash_int2float32( uint32_t value )
+{
+    return (union {uint32_t i; float f;}){value}.f;
+}
+
+double lsmash_int2float64( uint64_t value )
+{
+    return (union {uint64_t i; double d;}){value}.d;
+}
+/*---- ----*/
+
+/*---- allocator ----*/
+void *lsmash_malloc_zero( size_t size )
+{
+    if( !size )
+        return NULL;
+    void *p = malloc( size );
+    if( !p )
+        return NULL;
+    memset( p, 0, size );
+    return p;
+}
+
+void *lsmash_memdup( void *src, size_t size )
+{
+    if( !size )
+        return NULL;
+    void *dst = malloc( size );
+    if( !dst )
+        return NULL;
+    memcpy( dst, src, size );
+    return dst;
+}
+/*---- ----*/
+
+/*---- others ----*/
+void lsmash_log( lsmash_log_level level, const char* message, ... )
+{
+    char *prefix;
+    va_list args;
+
+    va_start( args, message );
+    switch( level )
+    {
+        case LSMASH_LOG_ERROR:
+            prefix = "Error";
+            break;
+        case LSMASH_LOG_WARNING:
+            prefix = "Warning";
+            break;
+        case LSMASH_LOG_INFO:
+            prefix = "Info";
+            break;
+        default:
+            prefix = "Unknown";
+            break;
+    }
+
+    fprintf( stderr, "[%s]: ", prefix );
+    vfprintf( stderr, message, args );
+    va_end( args );
+}
+
+/* for qsort function */
+int lsmash_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
+{
+    int64_t diff = (int64_t)(a->dts - b->dts);
+    return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
+}
+
+int lsmash_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
+{
+    int64_t diff = (int64_t)(a->cts - b->cts);
+    return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
+}
diff --git a/output/mp4/utils.h b/output/mp4/utils.h
new file mode 100644
index 0000000..92705eb
--- /dev/null
+++ b/output/mp4/utils.h
@@ -0,0 +1,177 @@
+/*****************************************************************************
+ * utils.h:
+ *****************************************************************************
+ * Copyright (C) 2010 L-SMASH project
+ *
+ * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *****************************************************************************/
+
+/* This file is available under an ISC license. */
+
+#ifndef LSMASH_UTIL_H
+#define LSMASH_UTIL_H
+
+#define debug_if(x) if(x)
+
+#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b))
+#define LSMASH_MIN( a, b ) ((a) < (b) ? (a) : (b))
+
+/*---- bytestream ----*/
+
+typedef struct
+{
+    FILE *stream;     /* I/O stream */
+    uint8_t error;
+    uint8_t *data;    /* buffer for reading/writing */
+    uint64_t store;   /* valid data size on buffer */
+    uint64_t alloc;   /* total buffer size including invalid area */
+    uint64_t pos;     /* data position on buffer to be read next */
+    uint64_t written; /* data size written into "stream" already */
+} lsmash_bs_t;
+
+uint64_t lsmash_bs_get_pos( lsmash_bs_t *bs );
+void lsmash_bs_empty( lsmash_bs_t *bs );
+void lsmash_bs_free( lsmash_bs_t *bs );
+void lsmash_bs_alloc( lsmash_bs_t *bs, uint64_t size );
+lsmash_bs_t* lsmash_bs_create( char* filename );
+void lsmash_bs_cleanup( lsmash_bs_t *bs );
+
+/*---- bytestream writer ----*/
+
+void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value );
+void lsmash_bs_put_bytes( lsmash_bs_t *bs, void *value, uint32_t size );
+void lsmash_bs_put_be16( lsmash_bs_t *bs, uint16_t value );
+void lsmash_bs_put_be24( lsmash_bs_t *bs, uint32_t value );
+void lsmash_bs_put_be32( lsmash_bs_t *bs, uint32_t value );
+void lsmash_bs_put_be64( lsmash_bs_t *bs, uint64_t value );
+void lsmash_bs_put_byte_from_64( lsmash_bs_t *bs, uint64_t value );
+void lsmash_bs_put_be16_from_64( lsmash_bs_t *bs, uint64_t value );
+void lsmash_bs_put_be24_from_64( lsmash_bs_t *bs, uint64_t value );
+void lsmash_bs_put_be32_from_64( lsmash_bs_t *bs, uint64_t value );
+int lsmash_bs_write_data( lsmash_bs_t *bs );
+
+void* lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t* length );
+
+/*---- bytestream reader ----*/
+uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs );
+uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size );
+uint16_t lsmash_bs_get_be16( lsmash_bs_t *bs );
+uint32_t lsmash_bs_get_be24( lsmash_bs_t *bs );
+uint32_t lsmash_bs_get_be32( lsmash_bs_t *bs );
+uint64_t lsmash_bs_get_be64( lsmash_bs_t *bs );
+uint64_t lsmash_bs_get_byte_to_64( lsmash_bs_t *bs );
+uint64_t lsmash_bs_get_be16_to_64( lsmash_bs_t *bs );
+uint64_t lsmash_bs_get_be24_to_64( lsmash_bs_t *bs );
+uint64_t lsmash_bs_get_be32_to_64( lsmash_bs_t *bs );
+int lsmash_bs_read_data( lsmash_bs_t *bs, uint32_t size );
+
+/*---- bitstream ----*/
+typedef struct {
+    lsmash_bs_t* bs;
+    uint8_t store;
+    uint8_t cache;
+} lsmash_bits_t;
+
+void lsmash_bits_init( lsmash_bits_t* bits, lsmash_bs_t *bs );
+lsmash_bits_t* lsmash_bits_create( lsmash_bs_t *bs );
+void lsmash_bits_empty( lsmash_bits_t *bits );
+void lsmash_bits_put_align( lsmash_bits_t *bits );
+void lsmash_bits_get_align( lsmash_bits_t *bits );
+void lsmash_bits_cleanup( lsmash_bits_t *bits );
+
+/*---- bitstream writer ----*/
+void lsmash_bits_put( lsmash_bits_t *bits, uint32_t value, uint32_t width );
+uint32_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width );
+lsmash_bits_t* lsmash_bits_adhoc_create();
+void lsmash_bits_adhoc_cleanup( lsmash_bits_t* bits );
+void* lsmash_bits_export_data( lsmash_bits_t* bits, uint32_t* length );
+int lsmash_bits_import_data( lsmash_bits_t* bits, void* data, uint32_t length );
+
+/*---- list ----*/
+
+typedef struct lsmash_entry_tag lsmash_entry_t;
+
+struct lsmash_entry_tag
+{
+    lsmash_entry_t *next;
+    lsmash_entry_t *prev;
+    void *data;
+};
+
+typedef struct
+{
+    lsmash_entry_t *head;
+    lsmash_entry_t *tail;
+    lsmash_entry_t *last_accessed_entry;
+    uint32_t last_accessed_number;
+    uint32_t entry_count;
+} lsmash_entry_list_t;
+
+typedef void (*lsmash_entry_data_eliminator)(void* data); /* very same as free() of standard c lib; void free(void *); */
+
+void lsmash_init_entry_list( lsmash_entry_list_t *list );
+lsmash_entry_list_t *lsmash_create_entry_list( void );
+int lsmash_add_entry( lsmash_entry_list_t *list, void *data );
+int lsmash_remove_entry_direct( lsmash_entry_list_t *list, lsmash_entry_t *entry, void* eliminator );
+int lsmash_remove_entry( lsmash_entry_list_t *list, uint32_t entry_number, void* eliminator );
+void lsmash_remove_entries( lsmash_entry_list_t *list, void* eliminator );
+void lsmash_remove_list( lsmash_entry_list_t *list, void* eliminator );
+
+lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number );
+void *lsmash_get_entry_data( lsmash_entry_list_t *list, uint32_t entry_number );
+
+/*---- type ----*/
+double lsmash_fixed2double( uint64_t value, int frac_width );
+float lsmash_int2float32( uint32_t value );
+double lsmash_int2float64( uint64_t value );
+
+/*---- allocator ----*/
+void *lsmash_malloc_zero( size_t size );
+void *lsmash_memdup( void *src, size_t size );
+
+typedef enum
+{
+    LSMASH_LOG_ERROR,
+    LSMASH_LOG_WARNING,
+    LSMASH_LOG_INFO,
+} lsmash_log_level;
+
+/*---- others ----*/
+void lsmash_log( lsmash_log_level level, const char* message, ... );
+int lsmash_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b );
+int lsmash_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b );
+
+static inline uint64_t lsmash_get_gcd( uint64_t a, uint64_t b )
+{
+    if( !b )
+        return a;
+    while( 1 )
+    {
+        uint64_t c = a % b;
+        if( !c )
+            return b;
+        a = b;
+        b = c;
+    }
+}
+
+static inline uint64_t lsmash_get_lcm( uint64_t a, uint64_t b )
+{
+    if( !a )
+        return 0;
+    return (a / lsmash_get_gcd( a, b )) * b;
+}
+
+#endif
diff --git a/x264.c b/x264.c
index d735ab4..433c54b 100644
--- a/x264.c
+++ b/x264.c
@@ -113,9 +113,7 @@ static const char * const muxer_names[] =
     "raw",
     "mkv",
     "flv",
-#if HAVE_GPAC
     "mp4",
-#endif
     0
 };
 
@@ -383,7 +381,10 @@ static void help( x264_param_t *defaults, int longhelp )
         " .264 -> Raw bytestream\n"
         " .mkv -> Matroska\n"
         " .flv -> Flash Video\n"
-        " .mp4 -> MP4 if compiled with GPAC support (%s)\n"
+        " .mp4 -> MP4\n"
+        " .3gp -> MP4 (branded '3gp6')\n"
+        " .3g2 -> MP4 (branded '3gp6' and '3g2a')\n"
+        " .mov or .qt -> QuickTime File Format\n"
         "Output bit depth: %d (configured at compile time)\n"
         "\n"
         "Options:\n"
@@ -408,11 +409,6 @@ static void help( x264_param_t *defaults, int longhelp )
 #else
         "no",
 #endif
-#if HAVE_GPAC
-        "yes",
-#else
-        "no",
-#endif
         x264_bit_depth
       );
     H0( "Example usage:\n" );
@@ -1022,9 +1018,9 @@ static int select_output( const char *muxer, char *filename, x264_param_t *param
     if( !strcmp( filename, "-" ) || strcasecmp( muxer, "auto" ) )
         ext = muxer;
 
-    if( !strcasecmp( ext, "mp4" ) )
+    if( !strcasecmp( ext, "mp4" ) || !strcasecmp( ext, "3gp" ) || !strcasecmp( ext, "3g2" ) ||
+        !strcasecmp( ext, "mov" ) || !strcasecmp( ext, "qt" ) )
     {
-#if HAVE_GPAC
         cli_output = mp4_output;
         param->b_annexb = 0;
         param->b_repeat_headers = 0;
@@ -1033,10 +1029,6 @@ static int select_output( const char *muxer, char *filename, x264_param_t *param
             x264_cli_log( "x264", X264_LOG_WARNING, "cbr nal-hrd is not compatible with mp4\n" );
             param->i_nal_hrd = X264_NAL_HRD_VBR;
         }
-#else
-        x264_cli_log( "x264", X264_LOG_ERROR, "not compiled with MP4 output support\n" );
-        return -1;
-#endif
     }
     else if( !strcasecmp( ext, "mkv" ) )
     {
-- 
1.7.7.msysgit.1

