From 71c7b04b1b4ae17ad3d15c298bb3b83b8e8adbf1 Mon Sep 17 00:00:00 2001 From: Anton Mitrofanov Date: Wed, 2 Mar 2011 02:56:25 +0300 Subject: [PATCH] Using L-SMASH for mp4 muxing --- Makefile | 7 +- configure | 30 +- output/mp4.c | 465 ++- output/mp4/box.h | 1211 ++++++ output/mp4/importer.c | 1059 +++++ output/mp4/importer.h | 49 + output/mp4/isom.c |10684 +++++++++++++++++++++++++++++++++++++++++++++++++ output/mp4/lsmash.h | 1234 ++++++ output/mp4/mp4a.c | 640 +++ output/mp4/mp4a.h | 121 + output/mp4/mp4sys.c | 637 +++ output/mp4/mp4sys.h | 199 + output/mp4/summary.c | 103 + output/mp4/summary.h | 40 + output/mp4/utils.c | 532 +++ output/mp4/utils.h | 126 + x264.c | 20 +- 17 files changed, 16914 insertions(+), 243 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/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/summary.h create mode 100644 output/mp4/utils.c create mode 100644 output/mp4/utils.h diff --git a/Makefile b/Makefile index a66a9ee..e52fd1a 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 96544ce..7c33fce 100755 --- a/configure +++ b/configure @@ -10,7 +10,6 @@ available options: --disable-avs disables avisynth support (windows only) --disable-lavf disables libavformat support --disable-ffms disables ffmpegsource support - --disable-gpac disables gpac support --disable-gpl disables GPL-only features --disable-thread disables multithreaded encoding --enable-win32thread use win32threads (windows only) @@ -152,7 +151,6 @@ DEVNULL='/dev/null' avs="auto" lavf="auto" ffms="auto" -gpac="auto" gpl="yes" thread="auto" swscale="auto" @@ -174,7 +172,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" +CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS AVS GPL" # parse options @@ -208,9 +206,6 @@ for opt do --disable-ffms) ffms="no" ;; - --disable-gpac) - gpac="no" - ;; --disable-gpl) gpl="no" ;; @@ -682,28 +677,6 @@ if [ "$swscale" = "yes" ]; then fi fi -GPAC_LIBS="-lgpac_static" -if [ $SYS = MINGW ]; then - GPAC_LIBS="$GPAC_LIBS -lwinmm" -fi -if [ "$gpac" = "auto" ] ; then - gpac="no" - 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" if [ $SYS = MINGW ] && cc_check extras/avisynth_c.h ; then @@ -853,7 +826,6 @@ asm: $asm 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 b5aee64..da20bcb 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 * Loren Merritt + * Yusuke Nakamura + * Takashi Hirata + * golgol7777 * * 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,85 +28,78 @@ *****************************************************************************/ #include "output.h" -#include +#include "mp4/lsmash.h" +#include "mp4/importer.h" /* FIXME: will be replaced with summary.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 -#define malloc gf_malloc -#define free gf_free -#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_code major_brand; + int i_brand_3gpp; + int b_brand_qt; + uint32_t i_track; + uint32_t i_sample_entry; + uint64_t i_time_inc; + int64_t i_start_offset; + 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 b_no_pasp; + int b_use_recovery; } 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; @@ -111,56 +107,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_root && p_mp4->i_track ) { - 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->i_track ) + /* 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" ); + + /* + * 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. + */ + uint32_t mvhd_timescale = lsmash_get_movie_timescale( p_mp4->p_root ); + uint32_t mdhd_timescale = lsmash_get_media_timescale( p_mp4->p_root, p_mp4->i_track ); + double actual_duration = 0; + if( mdhd_timescale != 0 ) /* avoid zero division */ { - /* 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 ); - } - - /* 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 ) - { - 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 ); - } - gf_isom_sample_del( &sample ); - - recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track ); + actual_duration = (double)((largest_pts + last_delta) * p_mp4->i_time_inc) * mvhd_timescale / mdhd_timescale; + MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, actual_duration, p_mp4->i_start_offset * p_mp4->i_time_inc, ISOM_EDIT_MODE_NORMAL ), + "failed to set 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 ); + else + MP4_LOG_ERROR( "mdhd timescale is broken.\n" ); + + MP4_LOG_IF_ERR( lsmash_update_bitrate_info( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry ), + "failed to update bitrate information for video.\n" ); + + MP4_LOG_IF_ERR( lsmash_finish_movie( p_mp4->p_root, NULL ), "failed to finish movie.\n" ); + + /* Write media data size here. */ + MP4_LOG_IF_ERR( lsmash_write_mdat_size( p_mp4->p_root ), "failed to write mdat size.\n" ); } - free( p_mp4 ); + remove_mp4_hnd( p_mp4 ); /* including lsmash_destroy_root( p_mp4->p_root ); */ return 0; } @@ -170,27 +157,41 @@ 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 ) + FILE *fh = fopen( psz_filename, "wb" ); + MP4_FAIL_IF_ERR( !fh, "cannot open output file `%s'.\n", psz_filename ); + int b_regular = x264_is_regular_file( fh ); fclose( fh ); + MP4_FAIL_IF_ERR( !b_regular, "MP4 output is incompatible with non-regular file `%s'\n", psz_filename ); - if( !(p_mp4 = malloc( sizeof(mp4_hnd_t) )) ) - return -1; - + 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_no_pasp = 0; + p_mp4->b_use_recovery = 0; + + p_mp4->p_root = lsmash_open_movie( psz_filename, LSMASH_FILE_MODE_WRITE ); + MP4_FAIL_IF_ERR_EX( !p_mp4->p_root, "failed to create root.\n" ); - if( !(p_mp4->p_sample = gf_isom_sample_new()) ) + char* ext = get_filename_extension( psz_filename ); + if( !strcmp( ext, "mov" ) || !strcmp( ext, "qt" ) ) { - close_file( p_mp4, 0, 0 ); - return -1; + p_mp4->major_brand = ISOM_BRAND_TYPE_QT; + p_mp4->b_brand_qt = 1; } - - gf_isom_set_brand_info( p_mp4->p_file, GF_ISOM_BRAND_AVC1, 0 ); + else if( !strcmp( ext, "3gp" ) ) + { + 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; *p_handle = p_mp4; @@ -200,42 +201,114 @@ 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 ); - - 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 ); + MP4_FAIL_IF_ERR( i_media_timescale > UINT32_MAX, "MP4 media timescale %"PRIu64" exceeds maximum\n", i_media_timescale ); + /* Set brands. */ + lsmash_brand_type_code 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 roll recovery. */ + } + else + { + brands[brand_count++] = ISOM_BRAND_TYPE_ISOM; + brands[brand_count++] = ISOM_BRAND_TYPE_MP41; + brands[brand_count++] = ISOM_BRAND_TYPE_MP42; + 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; + } + if( p_mp4->b_use_recovery ) + { + brands[brand_count++] = ISOM_BRAND_TYPE_AVC1; /* sdtp/sgpd/sbgp */ + brands[brand_count++] = ISOM_BRAND_TYPE_ISO4; /* cslg */ + brands[brand_count++] = ISOM_BRAND_TYPE_QT; /* tapt/cslg/stps/sdtp */ + p_mp4->b_brand_qt = 1; + } + } + MP4_FAIL_IF_ERR( lsmash_set_brands( p_mp4->p_root, p_mp4->major_brand, minor_version, brands, brand_count ), + "failed to set brands / ftyp.\n" ); + + /* Set max duration per chunk. */ + MP4_FAIL_IF_ERR( lsmash_set_max_chunk_duration( p_mp4->p_root, 0.5 ), + "failed to set max duration per chunk.\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" ); + + /* Set timescale. */ + MP4_FAIL_IF_ERR( lsmash_set_movie_timescale( p_mp4->p_root, 600 ), + "failed to set movie timescale.\n" ); + MP4_FAIL_IF_ERR( lsmash_set_media_timescale( p_mp4->p_root, p_mp4->i_track, i_media_timescale ), + "failed to set media timescale for video.\n" ); + + /* Set handler name. */ + MP4_FAIL_IF_ERR( lsmash_set_media_handler_name( p_mp4->p_root, p_mp4->i_track, "X264 Video Media Handler" ), + "failed to set media hander name for video.\n" ); + if( p_mp4->b_brand_qt ) + MP4_FAIL_IF_ERR( lsmash_set_data_handler_name( p_mp4->p_root, p_mp4->i_track, "X264 URL Data Handler" ), + "failed to set data hander name for video.\n" ); + + if( p_mp4->b_use_recovery ) + MP4_FAIL_IF_ERR( lsmash_create_grouping( p_mp4->p_root, p_mp4->i_track, ISOM_GROUP_TYPE_ROLL ), + "failed to create roll recovery grouping\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; + } + } + MP4_FAIL_IF_ERR( lsmash_set_track_presentation_size( p_mp4->p_root, p_mp4->i_track, i_display_width, i_display_height ), + "failed to set presentation size.\n" ); + if( p_mp4->b_brand_qt ) + { + summary.primaries = p_param->vui.i_colorprim; + summary.transfer = p_param->vui.i_transfer; + summary.matrix = p_param->vui.i_colmatrix; } - p_mp4->p_sample->data = malloc( p_param->i_width * p_param->i_height * 3 / 2 ); - if( !p_mp4->p_sample->data ) - return -1; + /* 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" ); + if( p_mp4->b_brand_qt && !p_mp4->b_no_pasp ) + MP4_FAIL_IF_ERR( lsmash_set_track_aperture_modes( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry ), + "failed to set track aperture mode.\n" ); return 0; } @@ -243,49 +316,36 @@ static int set_param( hnd_t handle, x264_param_t *p_param ) 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 - - 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; + + /* Write ftyp. */ + MP4_FAIL_IF_ERR( lsmash_write_ftyp( p_mp4->p_root ), + "failed to write brands / ftyp.\n" ); + + /* Write mdat header. */ + MP4_FAIL_IF_ERR( lsmash_add_mdat( p_mp4->p_root ), "failed to add mdat.\n" ); return sei_size + sps_size + pps_size; } @@ -293,19 +353,29 @@ 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; - - 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; + + 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); @@ -313,16 +383,19 @@ 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_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 ); + p_sample->dts = dts; + p_sample->cts = cts; + p_sample->index = p_mp4->i_sample_entry; + p_sample->prop.sync_point = p_picture->b_keyframe; + + /* Write data per sample. */ + MP4_FAIL_IF_ERR( lsmash_write_sample( p_mp4->p_root, p_mp4->i_track, p_sample ), + "failed to write a video frame.\n" ); - p_mp4->p_sample->dataLength = 0; 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..2b5c67f --- /dev/null +++ b/output/mp4/box.h @@ -0,0 +1,1211 @@ +/***************************************************************************** + * box.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * 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 +#define ISOM_MAC_EPOCH_OFFSET 2082844800 + +#include "lsmash.h" +#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. This is useful for pipe I/O. + * usertype is for uuid. */ +#define ISOM_BASEBOX_COMMON \ + isom_box_t *parent; /* pointer of parent box */ \ + uint8_t manager; /* flags for L-SMASH */ \ + uint64_t size; \ + uint32_t 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_DEFAULT_BOX_HEADER_SIZE 8 +#define ISOM_DEFAULT_FULLBOX_HEADER_SIZE 12 +#define ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE 16 + +struct isom_box_tag +{ + ISOM_FULLBOX_COMMON; +}; + +/* File Type Box */ +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 */ +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 */ + /* */ + 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. + * Note: this field isn't defined in MP4 version 1. */ + 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. */ + 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 */ +typedef struct +{ + /* version == 0: 64bits -> 32bits */ + uint64_t segment_duration; /* the duration of this edit expressed in the time-scale indicated in the mvhd */ + 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 + * 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 */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_elst_t *elst; /* Edit List Box */ +} isom_edts_t; + +/* Track Reference Box */ +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; + isom_tref_type_t *ref; /* Track Reference Type Box */ + + uint32_t type_count; /* number of reference types */ +} isom_tref_t; + +/* Media Header Box */ +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 */ + /* */ +#define ISOM_LANG( lang ) ((((lang[0]-0x60)&0x1f)<<10) | (((lang[1]-0x60)&0x1f)<<5) | ((lang[2]-0x60)&0x1f)) + uint16_t language; /* ISOM: ISO-639-2/T language codes. The first 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. + * ISOM: this box might be also in Meta Box + * QTFF: this box might be also in Media 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; /* ISOM and QT: when present in Media Handler Reference Box, this field defines the type of media data + * 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 **/ +/* Video Media Header Box */ +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 */ +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 */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t maxPDUsize; + uint16_t avgPDUsize; + uint32_t maxbitrate; + uint32_t avgbitrate; + uint32_t reserved; +} isom_hmhd_t; + +/* Null Media Header Box */ +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; + +/* Bit Rate Box */ +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 */ +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 */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t hSpacing; /* horizontal spacing */ + uint32_t vSpacing; /* vertical spacing */ +} isom_pasp_t; + +/* Color Parameter Box + * This box is defined by 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 */ +#define ISOM_VISUAL_SAMPLE_ENTRY \ + 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 */ \ + /* pixel counts that the codec will deliver */ \ + 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. */ \ + isom_clap_t *clap; /* Clean Aperture Box / optional */ \ + isom_pasp_t *pasp; /* Pixel Aspect Ratio Box / optional */ \ + isom_colr_t *colr; /* ISOM: null / QTFF: Color Parameter Box @ optional */ \ + isom_stsl_t *stsl; /* ISOM: Sample Scale Box @ optional / QTFF null */ + +typedef struct +{ + ISOM_VISUAL_SAMPLE_ENTRY; +} isom_visual_entry_t; + +/* MP4 Visual Sample Entry */ +typedef struct +{ + ISOM_VISUAL_SAMPLE_ENTRY; + isom_esds_t *esds; /* ES Descriptor Box */ +} isom_mp4v_entry_t; + +/* Parameter Set Entry */ +typedef struct +{ + uint16_t parameterSetLength; + uint8_t *parameterSetNALUnit; +} isom_avcC_ps_entry_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; + +/* AVC Sample Entry */ +typedef struct +{ + ISOM_VISUAL_SAMPLE_ENTRY; + isom_avcC_t *avcC; /* AVCDecoderConfigurationRecord */ + isom_btrt_t *btrt; /* MPEG4BitRateBox / optional */ + // isom_m4ds_t *m4ds; /* MPEG4ExtensionDescriptorsBox / optional */ +} isom_avc_entry_t; + +/* Format Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t data_format; /* copy of sample description type */ +} isom_frma_t; + +/* MPEG-4 Audio Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t unknown; /* always 0? */ +} isom_mp4a_t; + +/* Terminator Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; /* size = 8, type = 0x00000000 */ +} isom_terminator_t; + +/* Sound Information Decompression Parameters Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_frma_t *frma; /* Format Box */ + isom_mp4a_t *mp4a; /* MPEG-4 Audio Box */ + isom_esds_t *esds; /* ES Descriptor Box */ + isom_terminator_t *terminator; /* Terminator Box */ +} 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 */ +#define ISOM_AUDIO_SAMPLE_ENTRY \ + 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 frequency 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_wave_t *wave; /* ISOM: null / QTFF: Sound Information Decompression Parameters Box */ \ + isom_chan_t *chan; /* ISOM: null / QTFF: Channel Compositor Box @ optional */ + +typedef struct +{ + ISOM_AUDIO_SAMPLE_ENTRY; + uint32_t exdata_length; + void *exdata; +} 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 */ +typedef struct +{ + uint32_t sample_count; /* number of consecutive samples that have the given sample_delta */ + uint32_t sample_delta; /* 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 + * ISOM: if version is set to 1, sample_offset is signed 32bit integer. + * QT: sample_offset is always signed 32bit 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) */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + int32_t compositionToDTSShift; + int32_t leastDecodeToDisplayDelta; /* the smallest sample_offset */ + int32_t greatestDecodeToDisplayDelta; /* the largest sample_offset */ + 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 */ +} isom_cslg_t; + +/* Sample Size Box */ +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 */ +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 + * description_length are available only if version == 1 and default_length == 0. */ +/* Roll Recovery Entry */ +typedef struct +{ + /* grouping_type is 'roll' */ + uint32_t description_length; + int16_t roll_distance; /* the number of samples that must be decoded in order for a sample to be decoded correctly */ +} isom_roll_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + 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_t; + +/* Sample to Group Box */ +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 */ +} isom_sbgp_entry_t; + +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_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 */ + isom_sgpd_t *sgpd; /* ISOM: Sample Group Description Box / QTFF: null */ + isom_sbgp_t *sbgp; /* ISOM: Sample To Group Box / QTFF: null */ + + uint32_t sgpd_count; + uint32_t sbgp_count; +} 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 */ +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 */ + /* */ + 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 fileds 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 time scale 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 time scale 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 */ +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 */ +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 + * 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; + +/* User Data Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_chpl_t *chpl; /* Chapter List Box */ +} isom_udta_t; + +/* Movie Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mvhd_t *mvhd; /* Movie Header Box */ + isom_iods_t *iods; /* MP4: Object Descriptor Box / ISOM & QTFF: null */ + lsmash_entry_list_t *trak_list; /* Track Box List */ + isom_udta_t *udta; /* User Data 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 */ + isom_mdat_t *mdat; /* Media Data Box */ + isom_free_t *free; /* Free Space Box */ + + lsmash_bs_t *bs; /* bytestream manager */ + double max_chunk_duration; /* max duration per chunk in seconds */ + 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_audio; /* compatibility with iTunes Audio */ + uint8_t max_3gpp_version; /* maximum 3GPP version */ + lsmash_entry_list_t *print; +}; + +typedef int (*isom_print_box_t)( lsmash_root_t *, isom_box_t *, int ); + +typedef struct +{ + int level; + isom_box_t *box; + isom_print_box_t func; +} isom_print_entry_t; + +/** Track Box **/ +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 */ + lsmash_entry_list_t *pool; /* samples pooled to interleave */ +} isom_chunk_t; + +typedef struct +{ + uint64_t dts; + uint64_t cts; +} isom_timestamp_t; + +typedef struct +{ + isom_sbgp_entry_t *sample_to_group; /* the address corresponding to the entry in Sample to Group Box */ + isom_roll_entry_t *roll_recovery; /* the address corresponding to the roll recovery entry in Sample Group Description Box */ + uint32_t first_sample; /* number of the first sample of the group */ + uint32_t recovery_point; + 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 +{ + // uint32_t grouping_type; + lsmash_entry_list_t *pool; /* grouping pooled to delimit and describe */ +} isom_grouping_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_cache_t; + +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 */ + + lsmash_root_t *root; /* go to root */ + isom_mdat_t *mdat; /* go to referenced mdat box */ + isom_cache_t *cache; + uint32_t related_track_ID; + uint8_t is_chapter; +} isom_trak_entry_t; +/** **/ + +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 }, + { UINT16_MAX, 0 } +}; + +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_compression_id_code +{ + 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_code +{ + 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, +}; + +#endif diff --git a/output/mp4/importer.c b/output/mp4/importer.c new file mode 100644 index 0000000..a2ef08d --- /dev/null +++ b/output/mp4/importer.c @@ -0,0 +1,1059 @@ +/***************************************************************************** + * importer.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * Contributors: Yusuke Nakamura + * + * 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 __MINGW32__ +#define _FILE_OFFSET_BITS 64 /* FIXME: This is redundant. Should be concentrated in utils.h */ +#endif + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +#include "box.h" + +#include +#include + +#ifdef __MINGW32__ /* FIXME: This is redundant. Should be concentrated in utils.h */ +#define mp4sys_fseek fseeko64 +#define mp4sys_ftell ftello64 +#else +#define mp4sys_fseek fseek +#define mp4sys_ftell ftell +#endif + +/*************************************************************************** + importer framework +***************************************************************************/ +struct mp4sys_importer_tag; + +typedef void ( *mp4sys_importer_cleanup )( struct mp4sys_importer_tag* importer ); +typedef int ( *mp4sys_importer_get_accessunit )( struct mp4sys_importer_tag*, uint32_t track_number , void* buf, uint32_t* size ); +typedef int ( *mp4sys_importer_probe )( struct mp4sys_importer_tag* importer ); + +typedef struct +{ + const char* name; + int detectable; + mp4sys_importer_probe probe; + mp4sys_importer_get_accessunit get_accessunit; + 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*)malloc( sizeof(lsmash_audio_summary_t) ); + if( !summary ) + return NULL; + memset( summary, 0, sizeof(lsmash_audio_summary_t) ); + 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->max_au_length = MP4SYS_ADTS_MAX_FRAME_LENGTH; + summary->frequency = mp4a_AAC_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, 8.6.7 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( mp4sys_setup_AudioSpecificConfig( summary ) ) + { + mp4sys_cleanup_audio_summary( 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; +} mp4sys_adts_info_t; + +static int mp4sys_adts_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number , void* userbuf, uint32_t *size ) +{ + debug_if( !importer || !importer->info || !userbuf || !size ) + 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 || *size < raw_data_block_size ) + return -1; + if( current_status == MP4SYS_IMPORTER_EOF ) + { + *size = 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; + mp4sys_cleanup_audio_summary( entry->data ); + entry->data = summary; + } + + /* read a raw_data_block(), typically == payload of a ADTS frame */ + if( fread( userbuf, 1, raw_data_block_size, importer->stream ) != raw_data_block_size ) + { + info->status = MP4SYS_IMPORTER_ERROR; + return -1; + } + *size = raw_data_block_size; + + /* 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( userbuf, 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 = malloc( sizeof(mp4sys_adts_info_t) ); + if( !info ) + { + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + memset( info, 0, sizeof(mp4sys_adts_info_t) ); + info->status = MP4SYS_IMPORTER_OK; + info->raw_data_block_idx = 0; + info->header = header; + info->variable_header = variable_header; + + if( lsmash_add_entry( importer->summaries, summary ) ) + { + free( info ); + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + importer->info = info; + + return 0; +} + +const static mp4sys_importer_functions mp4sys_adts_importer = { + "adts", + 1, + mp4sys_adts_probe, + mp4sys_adts_get_accessunit, + 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_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*)malloc( sizeof(lsmash_audio_summary_t) ); + if( !summary ) + return NULL; + memset( summary, 0, sizeof(lsmash_audio_summary_t) ); + 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->stream_type = MP4SYS_STREAM_TYPE_AudioStream; + 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( mp4sys_setup_AudioSpecificConfig( summary ) ) + { + mp4sys_cleanup_audio_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]; +} mp4sys_mp3_info_t; + +static int mp4sys_mp3_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number , void* userbuf, uint32_t *size ) +{ + debug_if( !importer || !importer->info || !userbuf || !size ) + 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, 2.4.2.1 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 || *size < frame_size ) + return -1; + if( current_status == MP4SYS_IMPORTER_EOF ) + { + *size = 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; + mp4sys_cleanup_audio_summary( entry->data ); + entry->data = summary; + } + /* read a frame's data. */ + memcpy( userbuf, info->raw_header, MP4SYS_MP3_HEADER_LENGTH ); + frame_size -= MP4SYS_MP3_HEADER_LENGTH; + if( fread( ((uint8_t*)userbuf)+MP4SYS_MP3_HEADER_LENGTH, 1, frame_size, importer->stream ) != frame_size ) + { + info->status = MP4SYS_IMPORTER_ERROR; + return -1; + } + *size = MP4SYS_MP3_HEADER_LENGTH + frame_size; + + /* 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 = malloc( sizeof(mp4sys_mp3_info_t) ); + if( !info ) + { + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + memset( info, 0, sizeof(mp4sys_mp3_info_t) ); + info->status = MP4SYS_IMPORTER_OK; + info->header = header; + memcpy( info->raw_header, buf, MP4SYS_MP3_HEADER_LENGTH ); + + if( lsmash_add_entry( importer->summaries, summary ) ) + { + free( info ); + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + importer->info = info; + + return 0; +} + +const static mp4sys_importer_functions mp4sys_mp3_importer = { + "MPEG-1/2BC_Audio_Legacy", + 1, + mp4sys_mp3_probe, + mp4sys_mp3_get_accessunit, + 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 ); +} + +static int mp4sys_amr_get_accessunit( mp4sys_importer_t* importer, uint32_t track_number , void* userbuf, uint32_t *size ) +{ + debug_if( !importer || !importer->info || !userbuf || !size ) + return -1; + if( track_number != 1 ) + return -1; + uint8_t wb = *(uint8_t*)importer->info; + + uint8_t* buf = userbuf; + if( fread( buf, 1, 1, importer->stream ) == 0 ) + { + /* EOF */ + *size = 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[wb][FT]; + if( read_size == 0 || *size < read_size-- ) + return -1; + if( read_size == 0 ) + { + *size = 1; + return 0; + } + if( fread( buf+1, 1, read_size, importer->stream ) != read_size ) + return -1; + *size = read_size + 1; + 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 = malloc( sizeof(lsmash_audio_summary_t) ); + memset( summary, 0, sizeof(lsmash_audio_summary_t) ); + 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->stream_type = MP4SYS_STREAM_TYPE_AudioStream; + 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 */ + importer->info = malloc( sizeof(uint8_t) ); + if( !importer->info ) + { + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + *(uint8_t*)importer->info = wb; + if( mp4sys_amr_create_damr( summary ) || lsmash_add_entry( importer->summaries, summary ) ) + { + free( importer->info ); + importer->info = NULL; + mp4sys_cleanup_audio_summary( summary ); + return -1; + } + return 0; +} + +const static mp4sys_importer_functions mp4sys_amr_importer = { + "amr", + 1, + mp4sys_amr_probe, + mp4sys_amr_get_accessunit, + mp4sys_amr_cleanup +}; + +/* 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 ) +{ + /* Requires the following 7 bytes. + * syncword : 16 + * crc1 : 16 + * fscod : 2 + * frmsizecod : 6 + * bsid : 5 + * bsmod : 3 + * acmod : 3 + * if((acmod & 0x01) && (acmod != 0x01)) cmixlev : 2 + * if(acmod & 0x04) surmixlev : 2 + * if(acmod == 0x02) dsurmod : 2 + * lfeon : 1 + */ + if( data_length < 7 ) + return -1; + /* check syncword */ + if( data[0] != 0x0b || data[1] != 0x77 ) + return -1; + /* get necessary data for AC3SpecificBox */ + uint32_t fscod, bsid, bsmod, acmod, lfeon, frmsizecod; + fscod = data[4] >> 6; + frmsizecod = data[4] & 0x3f; + bsid = data[5] >> 3; + bsmod = data[5] & 0x07; + acmod = data[6] >> 5; + if( acmod == 0x02 ) + lfeon = data[6] >> 2; /* skip dsurmod */ + else + { + if( (acmod & 0x01) && acmod != 0x01 && (acmod & 0x04) ) + lfeon = data[6]; /* skip cmixlev and surmixlev */ + else if( ((acmod & 0x01) && acmod != 0x01) || (acmod & 0x04) ) + lfeon = data[6] >> 2; /* skip cmixlev or surmixlev */ + else + lfeon = data[6] >> 4; + } + lfeon &= 0x01; + /* create AC3SpecificBox */ + lsmash_bits_t *bits = lsmash_bits_adhoc_create(); + lsmash_bits_put( bits, 11, 32 ); + lsmash_bits_put( bits, ISOM_BOX_TYPE_DAC3, 32 ); + lsmash_bits_put( bits, fscod, 2 ); + lsmash_bits_put( bits, bsid, 5 ); + lsmash_bits_put( bits, bsmod, 3 ); + lsmash_bits_put( bits, acmod, 3 ); + lsmash_bits_put( bits, lfeon, 1 ); + lsmash_bits_put( bits, frmsizecod >> 1, 5 ); + lsmash_bits_put( bits, 0, 5 ); + if( summary->exdata ) + free( summary->exdata ); + summary->exdata = lsmash_bits_export_data( bits, &summary->exdata_length ); + lsmash_bits_adhoc_cleanup( bits ); + return 0; +} + +/*************************************************************************** + importer public interfaces +***************************************************************************/ + + +/******** importer listing table ********/ +const static mp4sys_importer_functions* mp4sys_importer_tbl[] = { + &mp4sys_adts_importer, + &mp4sys_mp3_importer, + &mp4sys_amr_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 ); + /* FIXME: To be extended to support visual summary. */ + lsmash_remove_list( importer->summaries, mp4sys_cleanup_audio_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*)malloc( sizeof(mp4sys_importer_t) ); + if( !importer ) + return NULL; + memset( importer, 0, sizeof(mp4sys_importer_t) ); + + 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 ) || mp4sys_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, void* buf, uint32_t* size ) +{ + if( !importer || !importer->funcs.get_accessunit || !buf || !size || *size == 0 ) + return -1; + return importer->funcs.get_accessunit( importer, track_number, buf, size ); +} + +lsmash_audio_summary_t* mp4sys_duplicate_audio_summary( mp4sys_importer_t* importer, uint32_t track_number ) +{ + if( !importer ) + return NULL; + lsmash_audio_summary_t* summary = (lsmash_audio_summary_t*)malloc( sizeof(lsmash_audio_summary_t) ); + if( !summary ) + return NULL; + lsmash_audio_summary_t* src_summary = lsmash_get_entry_data( importer->summaries, track_number ); + if( !src_summary ) + return NULL; + memcpy( summary, src_summary, sizeof(lsmash_audio_summary_t) ); + summary->exdata = NULL; + summary->exdata_length = 0; + if( mp4sys_summary_add_exdata( summary, src_summary->exdata, src_summary->exdata_length ) ) + { + free( summary ); + return NULL; + } + return summary; +} diff --git a/output/mp4/importer.h b/output/mp4/importer.h new file mode 100644 index 0000000..ea4076f --- /dev/null +++ b/output/mp4/importer.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * importer.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * Contributors: Yusuke Nakamura + * + * 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 +***************************************************************************/ + +#include "summary.h" + +#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, void* buf, uint32_t* buf_size ); +unsigned int mp4sys_importer_get_track_count( mp4sys_importer_t* importer ); /* currently not supported */ +lsmash_audio_summary_t* mp4sys_duplicate_audio_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/isom.c b/output/mp4/isom.c new file mode 100644 index 0000000..df28b13 --- /dev/null +++ b/output/mp4/isom.c @@ -0,0 +1,10684 @@ +/***************************************************************************** + * isom.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Yusuke Nakamura + * Contributors: Takashi Hirata + * + * 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 "box.h" +#include "summary.h" /* FIXME: to be replaced with lsmash.h or whatnot */ + +/* MP4 Audio Sample Entry */ +typedef struct +{ + ISOM_AUDIO_SAMPLE_ENTRY; + isom_esds_t *esds; + mp4a_audioProfileLevelIndication pli; /* This is not used in mp4a box itself, but the value is specific for that. */ +} isom_mp4a_entry_t; + + +#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_FULLBOX_CASE \ + ISOM_BOX_TYPE_MVHD : \ + case ISOM_BOX_TYPE_IODS : \ + case ISOM_BOX_TYPE_ESDS : \ + case ISOM_BOX_TYPE_TKHD : \ + case QT_BOX_TYPE_CLEF : \ + case QT_BOX_TYPE_PROF : \ + case QT_BOX_TYPE_ENOF : \ + case ISOM_BOX_TYPE_ELST : \ + case ISOM_BOX_TYPE_MDHD : \ + case ISOM_BOX_TYPE_HDLR : \ + case ISOM_BOX_TYPE_VMHD : \ + case ISOM_BOX_TYPE_SMHD : \ + case ISOM_BOX_TYPE_HMHD : \ + case ISOM_BOX_TYPE_NMHD : \ + case QT_BOX_TYPE_GMIN : \ + case ISOM_BOX_TYPE_DREF : \ + case ISOM_BOX_TYPE_URL : \ + case ISOM_BOX_TYPE_STSD : \ + case ISOM_BOX_TYPE_STSL : \ + case QT_BOX_TYPE_CHAN : \ + case ISOM_BOX_TYPE_STTS : \ + case ISOM_BOX_TYPE_CTTS : \ + case ISOM_BOX_TYPE_CSLG : \ + case ISOM_BOX_TYPE_STSS : \ + case QT_BOX_TYPE_STPS : \ + case ISOM_BOX_TYPE_SDTP : \ + case ISOM_BOX_TYPE_STSC : \ + case ISOM_BOX_TYPE_STSZ : \ + case ISOM_BOX_TYPE_STCO : \ + case ISOM_BOX_TYPE_CO64 : \ + case ISOM_BOX_TYPE_SGPD : \ + case ISOM_BOX_TYPE_SBGP : \ + case ISOM_BOX_TYPE_CHPL + +#define QT_CODEC_TYPE_LPCM_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 + +/*---- ----*/ + +static inline void isom_init_basebox_common( isom_box_t *box, isom_box_t *parent, uint32_t type ) +{ + 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->parent = parent; + box->size = 0; + box->type = type; + box->usertype = NULL; + box->version = 0; + box->flags = 0; +} + +static void isom_init_box_common( void *box, void *parent, uint32_t type ) +{ + if( parent && ((isom_box_t *)parent)->type == ISOM_BOX_TYPE_STSD ) + { + isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); + return; + } + switch( ((isom_box_t *)box)->type ) + { + case ISOM_FULLBOX_CASE : + isom_init_fullbox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); + break; + default : + isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); + break; + } +} + +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 *p = (isom_box_t *)box; + if( p->parent && p->parent->type == ISOM_BOX_TYPE_STSD ) + { + isom_bs_put_basebox_common( bs, p ); + return; + } + switch( p->type ) + { + case ISOM_FULLBOX_CASE : + isom_bs_put_fullbox_common( bs, p ); + break; + default : + isom_bs_put_basebox_common( bs, p ); + break; + } +} + +static 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 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 > UINT32_MAX ) + elst->version = 1; + return 0; +} + +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 = malloc( sizeof(isom_dref_entry_t) ); + if( !data ) + return -1; + memset( data, 0, sizeof(isom_dref_entry_t) ); + 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 = malloc( data->location_length ); + if( !data->location ) + { + free( data ); + return -1; + } + memcpy( data->location, location, data->location_length ); + } + if( name ) + { + data->name_length = strlen( name ) + 1; + data->name = malloc( data->name_length ); + if( !data->name ) + { + if( data->location ) + free( data->location ); + free( data ); + return -1; + } + memcpy( data->name, name, data->name_length ); + } + 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; +} + +static 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->parameterSetLength = ps_size; + entry->parameterSetNALUnit = malloc( ps_size ); + if( !entry->parameterSetNALUnit ) + { + free( entry ); + return NULL; + } + memcpy( entry->parameterSetNALUnit, ps, ps_size ); + return entry; +} + +static 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_avc_entry_t *data = (isom_avc_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; + 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_avc_entry_t *data = (isom_avc_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; + 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 isom_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_avc_entry_t *data = (isom_avc_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; + 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; +} + +static 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 ); + free( avcC ); +} + +static int isom_add_avcC( lsmash_entry_list_t *list, void *parent ) +{ + if( !list ) + return -1; + isom_avc_entry_t *data = (isom_avc_entry_t *)lsmash_get_entry_data( list, list->entry_count ); + if( !data ) + return -1; + isom_create_box( avcC, parent, 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; + } + data->avcC = avcC; + return 0; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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_visual_extensions( isom_visual_entry_t *visual ) +{ + if( !visual ) + return; + if( visual->clap ) + free( visual->clap ); + if( visual->pasp ) + free( visual->pasp ); + if( visual->colr ) + free( visual->colr ); + if( visual->stsl ) + free( visual->stsl ); +} + +static int isom_add_visual_extensions( isom_visual_entry_t *visual, lsmash_video_summary_t *summary ) +{ + if( 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; + } + if( 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; + } + if( 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; + } + if( 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; + } + return 0; +} + +static int isom_add_avc_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_avc_entry_t *avc = malloc( sizeof(isom_avc_entry_t) ); + if( !avc ) + return -1; + memset( avc, 0, sizeof(isom_avc_entry_t) ); + isom_init_box_common( avc, stsd, sample_type ); + avc->data_reference_index = 1; + avc->width = (uint16_t)summary->width; + avc->height = (uint16_t)summary->height; + avc->horizresolution = avc->vertresolution = 0x00480000; + avc->frame_count = 1; + switch( sample_type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + strcpy( avc->compressorname, "\012AVC Coding" ); + break; + case ISOM_CODEC_TYPE_AVCP_VIDEO : + strcpy( avc->compressorname, "\016AVC Parameters" ); + break; + default : + return -1; + } + avc->depth = 0x0018; + avc->color_table_ID = -1; + if( lsmash_add_entry( list, avc ) || isom_add_avcC( list, avc ) ) + { + free( avc ); + return -1; + } + return isom_add_visual_extensions( (isom_visual_entry_t *)avc, summary ); +} + +static int isom_add_mp4a_entry( isom_stsd_t *stsd, lsmash_audio_summary_t *summary ) +{ + if( !stsd || !stsd->list || !summary + || 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, NULL, 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 ) + { + free( esds ); + return -1; + } + isom_mp4a_entry_t *mp4a = malloc( sizeof(isom_mp4a_entry_t) ); + if( !mp4a ) + { + mp4sys_remove_ES_Descriptor( esds->ES ); + free( esds ); + return -1; + } + memset( mp4a, 0, sizeof(isom_mp4a_entry_t) ); + isom_init_box_common( mp4a, stsd, ISOM_CODEC_TYPE_MP4A_AUDIO ); + mp4a->data_reference_index = 1; + /* 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, "6 Template fields used". */ + mp4a->channelcount = summary->channels; + mp4a->samplesize = summary->bit_depth; + /* 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??? */ + mp4a->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0; + mp4a->esds = esds; + mp4a->pli = mp4sys_get_audioProfileLevelIndication( summary ); + if( lsmash_add_entry( stsd->list, mp4a ) ) + { + mp4sys_remove_ES_Descriptor( esds->ES ); + free( esds ); + free( mp4a ); + return -1; + } + esds->parent = (isom_box_t *)mp4a; + return 0; +} + +#if 0 +static int isom_add_mp4v_entry( isom_stsd_t *stsd, lsmash_video_summary_t *summary ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_mp4v_entry_t *mp4v = malloc( sizeof(isom_visual_entry_t) ); + if( !mp4v ) + return -1; + memset( mp4v, 0, sizeof(isom_mp4v_entry_t) ); + isom_init_box_common( mp4v, stsd, ISOM_CODEC_TYPE_MP4V_VIDEO ); + mp4v->data_reference_index = 1; + mp4v->width = (uint16_t)summary->width; + mp4v->height = (uint16_t)summary->height; + mp4v->horizresolution = mp4v->vertresolution = 0x00480000; + mp4v->frame_count = 1; + mp4v->compressorname[32] = '\0'; + mp4v->depth = 0x0018; + mp4v->color_table_ID = -1; + if( lsmash_add_entry( stsd->list, mp4v ) ) + { + free( mp4v ); + return -1; + } + return isom_add_visual_extensions( (isom_visual_entry_t *)mp4v, summary ); +} + +static int isom_add_mp4s_entry( isom_stsd_t *stsd ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_mp4s_entry_t *mp4s = malloc( sizeof(isom_mp4s_entry_t) ); + if( !mp4s ) + return -1; + memset( mp4s, 0, sizeof(isom_mp4s_entry_t) ); + 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; +} + +static int isom_add_visual_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_video_summary_t *summary ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_visual_entry_t *visual = malloc( sizeof(isom_visual_entry_t) ); + if( !visual ) + return -1; + memset( visual, 0, sizeof(isom_visual_entry_t) ); + 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; + visual->compressorname[32] = '\0'; + visual->depth = 0x0018; + visual->color_table_ID = -1; + if( lsmash_add_entry( list, visual ) ) + { + free( visual ); + return -1; + } + return isom_add_visual_extensions( visual, summary );; +} +#endif + +static void isom_remove_wave( isom_wave_t *wave ); + +static 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; +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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_add_audio_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_audio_summary_t *summary ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_audio_entry_t *audio = malloc( sizeof(isom_audio_entry_t) ); + if( !audio ) + return -1; + memset( audio, 0, sizeof(isom_audio_entry_t) ); + isom_init_box_common( audio, stsd, sample_type ); + audio->data_reference_index = 1; + audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0; + audio->numAudioChannels = summary->channels; /* store the actual number of channels, here */ + if( sample_type == ISOM_CODEC_TYPE_MP4A_AUDIO ) + { + if( isom_add_wave( audio ) || + isom_add_frma( audio->wave ) || + isom_add_mp4a( audio->wave ) || + isom_add_terminator( audio->wave ) ) + goto fail; + 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 = -2; /* assume VBR */ + audio->packet_size = 0; + if( audio->version == 1 ) + { + 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->always7F000000 = 0x7F000000; + audio->constBitsPerChannel = 0; + audio->formatSpecificFlags = 0; + audio->constBytesPerAudioPacket = 0; + audio->constLPCMFramesPerAudioPacket = summary->samples_in_frame; + } + audio->wave->frma->data_format = sample_type; + /* create ES Descriptor */ + isom_esds_t *esds = malloc( sizeof(isom_esds_t) ); + if( !esds ) + goto fail; + memset( esds, 0, sizeof(isom_esds_t) ); + 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 ) + { + free( esds ); + goto fail; + } + audio->wave->esds = esds; + } + else if( sample_type == QT_CODEC_TYPE_LPCM_AUDIO || + sample_type == QT_CODEC_TYPE_TWOS_AUDIO || sample_type == QT_CODEC_TYPE_SOWT_AUDIO || + 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 || + sample_type == QT_CODEC_TYPE_23NI_AUDIO || sample_type == QT_CODEC_TYPE_RAW_AUDIO || + sample_type == QT_CODEC_TYPE_NONE_AUDIO || sample_type == QT_CODEC_TYPE_NOT_SPECIFIED ) + { + /* 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; + 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->always7F000000 = 0x7F000000; + audio->constBitsPerChannel = summary->bit_depth; + audio->constBytesPerAudioPacket = (audio->constBitsPerChannel * audio->numAudioChannels) / 8; + 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->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); + } + else /* audio->version == 0 */ + { + audio->channelcount = summary->channels; + audio->samplesize = summary->bit_depth; + audio->compression_ID = 0; + } + } + else + { + audio->channelcount = 2; + audio->samplesize = 16; + if( summary->exdata ) + { + audio->exdata_length = summary->exdata_length; + audio->exdata = malloc( audio->exdata_length ); + if( !audio->exdata ) + goto fail; + memcpy( audio->exdata, summary->exdata, audio->exdata_length ); + } + else + audio->exdata = NULL; + } + if( lsmash_add_entry( stsd->list, audio ) ) + goto fail; + return 0; +fail: + isom_remove_wave( audio->wave ); + 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 = malloc( sizeof(isom_text_entry_t) ); + if( !text ) + return -1; + memset( text, 0, sizeof(isom_text_entry_t) ); + 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; +} + +static int isom_add_ftab( isom_tx3g_entry_t *tx3g ) +{ + if( !tx3g ) + return -1; + isom_ftab_t *ftab = malloc( sizeof(isom_ftab_t) ); + if( !ftab ) + return -1; + memset( ftab, 0, sizeof(isom_ftab_t) ); + 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 = malloc( sizeof(isom_tx3g_entry_t) ); + if( !tx3g ) + return -1; + memset( tx3g, 0, sizeof(isom_tx3g_entry_t) ); + 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 : +#endif + ret = isom_add_avc_entry( stsd, sample_type, (lsmash_video_summary_t *)summary ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4V_VIDEO : + ret = isom_add_mp4v_entry( stsd, (lsmash_video_summary_t *)summary ); + break; +#endif + case ISOM_CODEC_TYPE_MP4A_AUDIO : + if( trak->root->ftyp->major_brand != ISOM_BRAND_TYPE_QT ) + ret = isom_add_mp4a_entry( stsd, (lsmash_audio_summary_t *)summary ); + else + ret = isom_add_audio_entry( stsd, sample_type, (lsmash_audio_summary_t *)summary ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + ret = isom_add_mp4s_entry( stsd ); + break; + 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 : + ret = isom_add_visual_entry( stsd, sample_type, (lsmash_video_summary_t *)summary ); + break; +#endif + 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_LPCM_AUDIO_CASE : +#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_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 : +#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; +} + +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; +} + +static isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + isom_sgpd_t *sgpd = NULL; + for( uint32_t i = 0; i < stbl->sgpd_count; i++ ) + { + sgpd = stbl->sgpd + i; + if( !sgpd || !sgpd->list ) + return NULL; + if( sgpd->grouping_type == grouping_type ) + break; + } + return sgpd; +} + +static isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + isom_sbgp_t *sbgp = NULL; + for( uint32_t i = 0; i < stbl->sbgp_count; i++ ) + { + sbgp = stbl->sbgp + i; + if( !sbgp || !sbgp->list ) + return NULL; + if( sbgp->grouping_type == grouping_type ) + break; + } + return sbgp; +} + +static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_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->roll_distance = roll_distance; + if( lsmash_add_entry( sgpd->list, data ) ) + { + free( data ); + return NULL; + } + return data; +} + +static isom_sbgp_entry_t *isom_add_sbgp_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index ) +{ + if( !sbgp ) + return NULL; + isom_sbgp_entry_t *data = malloc( sizeof(isom_sbgp_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 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_DEFAULT_BOX_HEADER_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 : + *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, ((isom_mp4a_entry_t*)sample_entry)->pli ); + 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_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_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 : +#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 ); + switch( handler_type ) + { + case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK : + tkhd->matrix[0] = 0x00010000; + tkhd->matrix[4] = 0x00010000; + tkhd->matrix[8] = 0x40000000; + break; + case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK : + tkhd->volume = 0x0100; + break; + default : + break; + } + 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; +} + +static 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; +} + +static 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 ); + 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_minf_t *minf, uint32_t media_type, lsmash_root_t *root ) +{ + if( (!mdia && !minf) || (mdia && minf) ) + return -1; /* Either one must be given. */ + if( (mdia && mdia->hdlr) || (minf && minf->hdlr) ) + return -1; /* Selected one must not have hdlr yet. */ + isom_box_t *parent = mdia ? (isom_box_t *)mdia : (isom_box_t *)minf; + isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR ); + uint32_t type = mdia ? (root->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 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; + switch( type ) + { + case QT_HANDLER_TYPE_DATA : + type_name = "Data "; + type_name_length = 5; + break; + default : + type_name = "Media "; + type_name_length = 6; + break; + } + switch( subtype ) + { + case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK : + subtype_name = "Sound "; + subtype_name_length = 6; + break; + case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK : + subtype_name = "Video "; + subtype_name_length = 6; + break; + case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK : + subtype_name = "Hint "; + subtype_name_length = 5; + break; + case ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK : + subtype_name = "Meta "; + subtype_name_length = 5; + break; + case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK : + subtype_name = "Text "; + subtype_name_length = 5; + break; + case QT_REFERENCE_HANDLER_TYPE_ALIAS : + subtype_name = "Alias "; + subtype_name_length = 6; + break; + case QT_REFERENCE_HANDLER_TYPE_RESOURCE : + subtype_name = "Resource "; + subtype_name_length = 9; + break; + case QT_REFERENCE_HANDLER_TYPE_URL : + subtype_name = "URL "; + subtype_name_length = 4; + break; + default : + subtype_name = "Unknown "; + subtype_name_length = 8; + 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 + 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 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_avc_entry_t *data = lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data ) + return -1; + isom_create_box( btrt, data, ISOM_BOX_TYPE_BTRT ); + data->btrt = btrt; + return 0; +} + +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; +} + +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 int isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl ) + return -1; + uint64_t sgpd_count = 1; + isom_sgpd_t *sgpd_array; + if( !stbl->sgpd ) + sgpd_array = malloc( sizeof(isom_sgpd_t) ); + else + { + sgpd_count += stbl->sgpd_count; + sgpd_array = realloc( stbl->sgpd, sgpd_count * sizeof(isom_sgpd_t) ); + } + if( !sgpd_array ) + return -1; + isom_sgpd_t *sgpd = sgpd_array + sgpd_count - 1; + memset( sgpd, 0, sizeof(isom_sgpd_t) ); + isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD ); + sgpd->list = lsmash_create_entry_list(); + if( !sgpd->list ) + { + stbl->sgpd = NULL; + free( sgpd_array ); + return -1; + } + stbl->sgpd = sgpd_array; + stbl->sgpd_count = sgpd_count; + 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_ROLL : + sgpd->default_length = 2; + break; + default : + /* We don't consider other grouping types currently. */ + break; + } + return 0; +} + +static int isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl ) + return -1; + uint64_t sbgp_count = 1; + isom_sbgp_t *sbgp_array; + if( !stbl->sbgp ) + sbgp_array = malloc( sizeof(isom_sbgp_t) ); + else + { + sbgp_count += stbl->sbgp_count; + sbgp_array = realloc( stbl->sbgp, sbgp_count * sizeof(isom_sbgp_t) ); + } + if( !sbgp_array ) + return -1; + isom_sbgp_t *sbgp = sbgp_array + sbgp_count - 1; + memset( sbgp, 0, sizeof(isom_sbgp_t) ); + isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP ); + sbgp->list = lsmash_create_entry_list(); + if( !sbgp->list ) + { + stbl->sbgp = NULL; + free( sbgp_array ); + return -1; + } + stbl->sbgp = sbgp_array; + stbl->sbgp_count = sbgp_count; + sbgp->grouping_type = grouping_type; + return 0; +} + +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, ISOM_BOX_TYPE_CHPL ); + chpl->version = 1; /* version = 1 is popular. */ + moov->udta->chpl = chpl; + 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 || !root->moov->mvhd ) + 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 = malloc( sizeof(isom_trak_entry_t) ); + if( !trak ) + return NULL; + memset( trak, 0, sizeof(isom_trak_entry_t) ); + isom_init_box_common( trak, moov, ISOM_BOX_TYPE_TRAK ); + isom_cache_t *cache = malloc( sizeof(isom_cache_t) ); + if( !cache ) + return NULL; + memset( cache, 0, sizeof(isom_cache_t) ); + if( lsmash_add_entry( moov->trak_list, trak ) ) + return NULL; + trak->root = root; + trak->cache = cache; + return trak; +} + +#define isom_remove_fullbox( box_type, container ) \ + isom_##box_type##_t *box_type = container->box_type; \ + if( box_type ) \ + free( box_type ) + +#define isom_remove_list_fullbox( box_type, container ) \ + isom_##box_type##_t *box_type = container->box_type; \ + if( box_type ) \ + { \ + lsmash_remove_list( box_type->list, NULL ); \ + free( box_type ); \ + } + +static void isom_remove_ftyp( isom_ftyp_t *ftyp ) +{ + if( !ftyp ) + return; + if( ftyp->compatible_brands ) + free( ftyp->compatible_brands ); + free( ftyp ); +} + +static void isom_remove_tapt( isom_tapt_t *tapt ) +{ + if( !tapt ) + return; + isom_remove_fullbox( clef, tapt ); + isom_remove_fullbox( prof, tapt ); + isom_remove_fullbox( enof, tapt ); + free( tapt ); +} + +static void isom_remove_edts( isom_edts_t *edts ) +{ + if( !edts ) + return; + isom_remove_list_fullbox( elst, edts ); + free( edts ); +} + +static void isom_remove_tref( isom_tref_t *tref ) +{ + if( !tref ) + return; + for( uint32_t i = 0; i < tref->type_count; i++ ) + { + isom_tref_type_t *ref = &tref->ref[i]; + if( ref && ref->track_ID ) + free( ref->track_ID ); + } + free( tref->ref ); + free( tref ); +} + +static void isom_remove_esds( isom_esds_t *esds ) +{ + if( !esds ) + return; + mp4sys_remove_ES_Descriptor( esds->ES ); + free( esds ); +} + +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 ); +} + +static void isom_remove_ftab( isom_ftab_t *ftab ) +{ + if( !ftab ) + return; + lsmash_remove_list( ftab->list, isom_remove_font_record ); + free( ftab ); +} + +static void isom_remove_wave( isom_wave_t *wave ) +{ + if( !wave ) + return; + free( wave->frma ); + free( wave->mp4a ); + isom_remove_esds( wave->esds ); + free( wave->terminator ); + free( wave ); +} + +static void isom_remove_chan( isom_chan_t *chan ) +{ + if( !chan ) + return; + if( chan->channelDescriptions ) + free( chan->channelDescriptions ); + free( chan ); +} + +static void isom_remove_stsd( isom_stsd_t *stsd ) +{ + if( !stsd ) + return; + if( !stsd->list ) + { + free( stsd ); + return; + } + for( lsmash_entry_t *entry = stsd->list->head; entry; ) + { + isom_sample_entry_t *sample = (isom_sample_entry_t *)entry->data; + if( !sample ) + { + lsmash_entry_t *next = entry->next; + free( entry ); + entry = next; + continue; + } + 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 : +#endif + { + isom_avc_entry_t *avc = (isom_avc_entry_t *)entry->data; + isom_remove_visual_extensions( (isom_visual_entry_t *)avc ); + if( avc->avcC ) + isom_remove_avcC( avc->avcC ); + if( avc->btrt ) + free( avc->btrt ); + free( avc ); + break; + } +#if 0 + case ISOM_CODEC_TYPE_MP4V_VIDEO : + { + isom_mp4v_entry_t *mp4v = (isom_mp4v_entry_t *)entry->data; + isom_remove_visual_extensions( (isom_visual_entry_t *)mp4v ); + isom_remove_esds( mp4v->esds ); + free( mp4v ); + break; + } +#endif + case ISOM_CODEC_TYPE_MP4A_AUDIO : + { + if( !((isom_audio_entry_t *)sample)->version ) + { + isom_mp4a_entry_t *mp4a = (isom_mp4a_entry_t *)entry->data; + isom_remove_esds( mp4a->esds ); + free( mp4a ); + } + else + { + /* MPEG-4 Audio in QTFF */ + isom_audio_entry_t *audio = (isom_audio_entry_t *)entry->data; + isom_remove_wave( audio->wave ); + isom_remove_chan( audio->chan ); + if( audio->exdata ) + free( audio->exdata ); + free( audio ); + } + break; + } +#if 0 + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + { + isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)entry->data; + isom_remove_esds( mp4s->esds ); + free( mp4s ); + break; + } + 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 *)entry->data; + isom_remove_visual_extensions( visual ); + free( visual ); + break; + } +#endif + 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_LPCM_AUDIO_CASE : +#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_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 : +#endif + { + isom_audio_entry_t *audio = (isom_audio_entry_t *)entry->data; + if( audio->exdata ) + free( audio->exdata ); + free( audio ); + 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 : + { + isom_hint_entry_t *hint = (isom_hint_entry_t *)entry->data; + 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 *)entry->data; + free( metadata ); + break; + } +#endif + case ISOM_CODEC_TYPE_TX3G_TEXT : + { + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)entry->data; + 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 *)entry->data; + if( text->font_name ) + free( text->font_name ); + free( text ); + break; + } + default : + break; + } + lsmash_entry_t *next = entry->next; + free( entry ); + entry = next; + } + free( stsd->list ); + free( stsd ); +} + +static void isom_remove_stbl( isom_stbl_t *stbl ) +{ + if( !stbl ) + return; + isom_remove_stsd( stbl->stsd ); + isom_remove_fullbox( cslg, stbl ); + isom_remove_list_fullbox( stts, stbl ); + isom_remove_list_fullbox( ctts, stbl ); + isom_remove_list_fullbox( stsc, stbl ); + isom_remove_list_fullbox( stsz, stbl ); + isom_remove_list_fullbox( stss, stbl ); + isom_remove_list_fullbox( stps, stbl ); + isom_remove_list_fullbox( sdtp, stbl ); + isom_remove_list_fullbox( stco, stbl ); + for( uint32_t i = stbl->sgpd_count; i ; i-- ) + lsmash_remove_list( (stbl->sgpd + i - 1)->list, NULL ); + for( uint32_t i = stbl->sbgp_count; i ; i-- ) + lsmash_remove_list( (stbl->sbgp + i - 1)->list, NULL ); + if( stbl->sbgp ) + free( stbl->sbgp ); + if( stbl->sgpd ) + free( stbl->sgpd ); + free( stbl ); +} + +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 ); + free( dref ); +} + +static void isom_remove_dinf( isom_dinf_t *dinf ) +{ + if( !dinf ) + return; + isom_remove_dref( dinf->dref ); + free( dinf ); +} + +static void isom_remove_hdlr( isom_hdlr_t *hdlr ) +{ + if( !hdlr ) + return; + if( hdlr->componentName ) + free( hdlr->componentName ); + free( hdlr ); +} + +static void isom_remove_gmhd( isom_gmhd_t *gmhd ) +{ + if( !gmhd ) + return; + isom_remove_fullbox( gmin, gmhd ); + if( gmhd->text ) + free( gmhd->text ); + free( gmhd ); +} + +static void isom_remove_minf( isom_minf_t *minf ) +{ + if( !minf ) + return; + isom_remove_fullbox( vmhd, minf ); + isom_remove_fullbox( smhd, minf ); + isom_remove_fullbox( hmhd, minf ); + isom_remove_fullbox( nmhd, minf ); + isom_remove_gmhd( minf->gmhd ); + isom_remove_hdlr( minf->hdlr ); + isom_remove_dinf( minf->dinf ); + isom_remove_stbl( minf->stbl ); + free( minf ); +} + +static void isom_remove_mdia( isom_mdia_t *mdia ) +{ + if( !mdia ) + return; + isom_remove_fullbox( mdhd, mdia ); + isom_remove_minf( mdia->minf ); + isom_remove_hdlr( mdia->hdlr ); + free( mdia ); +} + +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 ); + free( chpl ); +} + +static void isom_remove_udta( isom_udta_t *udta ) +{ + if( !udta ) + return; + isom_remove_chpl( udta->chpl ); + free( udta ); +} + +static void isom_remove_trak( isom_trak_entry_t *trak ) +{ + if( !trak ) + return; + isom_remove_fullbox( tkhd, trak ); + 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 ); + if( trak->cache ) + { + lsmash_remove_list( trak->cache->chunk.pool, lsmash_delete_sample ); + lsmash_remove_list( trak->cache->roll.pool, NULL ); + 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 ); + free( iods ); +} + +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 ); + isom_remove_udta( moov->udta ); + if( moov->trak_list ) + lsmash_remove_list( moov->trak_list, isom_remove_trak ); + free( moov ); + root->moov = NULL; +} + +static void isom_remove_mdat( isom_mdat_t *mdat ) +{ + if( mdat ) + free( mdat ); +} + +static void isom_remove_free( isom_free_t *skip ) +{ + if( skip ) + { + if( skip->data ) + free( skip->data ); + free( skip ); + } +} + +/* 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; + 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 ); + for( uint32_t i = 0; i < tref->type_count; i++ ) + { + isom_tref_type_t *ref = &tref->ref[i]; + isom_bs_put_box_common( bs, ref ); + for( uint32_t j = 0; j < ref->ref_count; j++ ) + lsmash_bs_put_be32( bs, ref->track_ID[j] ); + } + 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_trak_entry_t *trak, uint8_t is_media_handler ) +{ + isom_hdlr_t *hdlr = is_media_handler ? trak->mdia->hdlr : trak->mdia->minf->hdlr; + if( !hdlr ) + return 0; + 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_trak_entry_t *trak ) +{ + isom_dref_t *dref = trak->mdia->minf->dinf->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_trak_entry_t *trak ) +{ + isom_dinf_t *dinf = trak->mdia->minf->dinf; + if( !dinf ) + return -1; + isom_bs_put_box_common( bs, dinf ); + if( lsmash_bs_write_data( bs ) ) + return -1; + return isom_write_dref( bs, trak ); +} + +static void isom_put_pasp( lsmash_bs_t *bs, isom_pasp_t *pasp ) +{ + if( !pasp ) + return; + isom_bs_put_box_common( bs, pasp ); + lsmash_bs_put_be32( bs, pasp->hSpacing ); + lsmash_bs_put_be32( bs, pasp->vSpacing ); +} + +static void isom_put_clap( lsmash_bs_t *bs, isom_clap_t *clap ) +{ + if( !clap ) + return; + 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 ); +} + +static void isom_put_colr( lsmash_bs_t *bs, isom_colr_t *colr ) +{ + if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF ) + return; + 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 ); +} + +static void isom_put_stsl( lsmash_bs_t *bs, isom_stsl_t *stsl ) +{ + if( !stsl ) + return; + 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 ); +} + +static void isom_put_visual_extensions( lsmash_bs_t *bs, isom_visual_entry_t *visual ) +{ + if( !visual ) + return; + isom_put_clap( bs, visual->clap ); + isom_put_pasp( bs, visual->pasp ); + isom_put_colr( bs, visual->colr ); + isom_put_stsl( bs, visual->stsl ); +} + +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_put_avcC( lsmash_bs_t *bs, isom_avcC_t *avcC ) +{ + if( !bs || !avcC || !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 0; +} + +static void isom_put_btrt( lsmash_bs_t *bs, isom_btrt_t *btrt ) +{ + if( !bs || !btrt ) + return; + 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 ); +} + +static int isom_write_esds( lsmash_bs_t *bs, isom_esds_t *esds ) +{ + if( !bs || !esds ) + return -1; + isom_bs_put_box_common( bs, esds ); + return mp4sys_write_ES_Descriptor( bs, esds->ES ); +} + +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_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_mp4a( bs, wave->mp4a ) || + 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_avc_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_avc_entry_t *data = (isom_avc_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 ); + isom_put_visual_extensions( bs, (isom_visual_entry_t *)data ); + if( !data->avcC ) + return -1; + isom_put_avcC( bs, data->avcC ); + if( data->btrt ) + isom_put_btrt( bs, data->btrt ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mp4a_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_mp4a_entry_t *data = (isom_mp4a_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( lsmash_bs_write_data( bs ) ) + return -1; + return isom_write_esds( bs, data->esds ); +} + +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; + if( data->version && isom_write_wave( bs, data->wave ) ) + return -1; + return isom_write_chan( bs, data->chan ); +} + +#if 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 ); + isom_put_visual_extensions( bs, data ); + if( data->type == ISOM_CODEC_TYPE_AVC1_VIDEO ) + { + isom_avc_entry_t *avc = (isom_avc_entry_t *)data; + if( !avc || !avc->avcC ) + return -1; + isom_put_avcC( bs, avc->avcC ); + if( avc->btrt ) + isom_put_btrt( bs, avc->btrt ); + } + return lsmash_bs_write_data( bs ); +} + +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 : +#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 + ret = isom_write_avc_entry( bs, entry ); + break; + case ISOM_CODEC_TYPE_MP4A_AUDIO : + if( !((isom_audio_entry_t *)sample)->version ) + ret = isom_write_mp4a_entry( bs, entry ); + else + ret = isom_write_audio_entry( bs, entry ); + 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 : + ret = isom_write_visual_entry( bs, entry ); + break; +#endif + 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_LPCM_AUDIO_CASE : +#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_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 : +#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; + lsmash_bs_put_byte( bs, (data->is_leading<<6) | + (data->sample_depends_on<<4) | + (data->sample_is_depended_on<<2) | + data->sample_has_redundancy ); + } + 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_t *sgpd = trak->mdia->minf->stbl->sgpd + grouping_number - 1; + 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_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_t *sbgp = trak->mdia->minf->stbl->sbgp + grouping_number - 1; + 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_sbgp_entry_t *data = (isom_sbgp_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; + for( uint32_t i = 1; i <= stbl->sgpd_count; i++ ) + if( isom_write_sgpd( bs, trak, i ) ) + return -1; + for( uint32_t i = 1; i <= stbl->sbgp_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, trak, 0 ) || + isom_write_dinf( bs, trak ) || + 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, trak, 1 ) || + 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_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; + 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 ) ) + 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 int isom_bs_write_largesize_placeholder( lsmash_bs_t *bs ) +{ + lsmash_bs_put_be32( bs, ISOM_DEFAULT_BOX_HEADER_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 ) +{ + if( !root || !root->bs || !root->mdat ) + return -1; + isom_mdat_t *mdat = root->mdat; + lsmash_bs_t *bs = root->bs; + mdat->placeholder_pos = ftell( bs->stream ); + if( isom_bs_write_largesize_placeholder( bs ) ) + return -1; + mdat->size = ISOM_DEFAULT_BOX_HEADER_SIZE; + isom_bs_put_box_common( bs, mdat ); + return lsmash_bs_write_data( bs ); +} + +static int isom_check_compatibility( lsmash_root_t *root ) +{ + if( !root ) + return -1; + /* Check brand to decide mandatory boxes. */ + if( !root->ftyp || !root->ftyp->brand_count ) + { + /* We assume this file is not a QuickTime but MP4 version 1 format file. */ + root->mp4_version1 = 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_ISO2 : + case ISOM_BRAND_TYPE_ISO3 : + case ISOM_BRAND_TYPE_ISO4 : + root->avc_extensions = 1; + break; + case ISOM_BRAND_TYPE_M4A : + case ISOM_BRAND_TYPE_M4B : + root->itunes_audio = 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; + } + } + root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_audio || root->max_3gpp_version; + return 0; +} + +static 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 void isom_iprintf( int indent, const char *format, ... ) +{ +#include + va_list args; + va_start( args, format ); + for( int i = 0; i < indent; i++ ) + printf( " " ); + vprintf( format, args ); + va_end( args ); +} + +static double isom_fixed2double( uint64_t value, int frac_width ) +{ + return value / (double)(1ULL << frac_width); +} + +static float isom_int2float32( uint32_t value ) +{ + return (union {uint32_t i; float f;}){value}.f; +} + +static double isom_int2float64( uint64_t value ) +{ + return (union {uint64_t i; double d;}){value}.d; +} + +static void isom_iprintf_duration( int indent, uint64_t duration, uint32_t timescale ) +{ + if( !timescale ) + { + isom_iprintf( indent, "duration = %"PRIu64"\n", duration ); + return; + } + int dur = duration / timescale; + int hour = (dur / 3600) % 24; + int min = (dur / 60) % 60; + int sec = dur % 60; + int ms = ((double)duration / timescale - (hour * 3600 + min * 60 + sec)) * 1e3 + 0.5; + static char str[32]; + sprintf( str, "%02d:%02d:%02d.%03d", hour, min, sec, ms ); + isom_iprintf( indent, "duration = %"PRIu64" (%s)\n", duration, str ); +} + +static char *isom_mp4time2utc( uint64_t mp4time ) +{ + int year_offset = mp4time / 31536000; + int leap_years = year_offset / 4 + ((mp4time / 86400) > 366); /* 1904 itself is leap year */ + int day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; + while( day < 1 ) + { + --year_offset; + leap_years = year_offset / 4 + ((mp4time / 86400) > 366); + day = (mp4time / 86400) - (year_offset * 365) - leap_years + 1; + } + int year = 1904 + year_offset; + int is_leap = (!(year % 4) && (year % 100)) || !(year % 400); + int month_days[13] = { 29, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int month; + for( month = 1; month <= 12; month++ ) + { + int i = (month == 2 && is_leap) ? 0 : month; + if( day <= month_days[i] ) + break; + day -= month_days[i]; + } + int hour = (mp4time / 3600) % 24; + int min = (mp4time / 60) % 60; + int sec = mp4time % 60; + static char utc[64]; + sprintf( utc, "UTC %d/%02d/%02d, %02d:%02d:%02d\n", year, month, day, hour, min, sec ); + return utc; +} + +static char *isom_unpack_iso_language( uint16_t language ) +{ + static char unpacked[4]; + unpacked[0] = ((language >> 10) & 0x1f) + 0x60; + unpacked[1] = ((language >> 5) & 0x1f) + 0x60; + unpacked[2] = ( language & 0x1f) + 0x60; + unpacked[3] = 0; + return unpacked; +} + +static void isom_iprint_matrix( int indent, int32_t *matrix ) +{ + isom_iprintf( indent, "| a, b, u | | %f, %f, %f |\n", isom_fixed2double( matrix[0], 16 ), + isom_fixed2double( matrix[1], 16 ), + isom_fixed2double( matrix[2], 30 ) ); + isom_iprintf( indent, "| c, d, v | = | %f, %f, %f |\n", isom_fixed2double( matrix[3], 16 ), + isom_fixed2double( matrix[4], 16 ), + isom_fixed2double( matrix[5], 30 ) ); + isom_iprintf( indent, "| x, y, z | | %f, %f, %f |\n", isom_fixed2double( matrix[6], 16 ), + isom_fixed2double( matrix[7], 16 ), + isom_fixed2double( matrix[8], 30 ) ); +} + +static void isom_iprint_rgb_color( int indent, uint16_t *color ) +{ + isom_iprintf( indent, "{ R, G, B } = { %"PRIu16", %"PRIu16", %"PRIu16" }\n", color[0], color[1], color[2] ); +} + +static void isom_iprint_rgba_color( int indent, uint8_t *color ) +{ + isom_iprintf( indent, "{ R, G, B, A } = { %"PRIu8", %"PRIu8", %"PRIu8", %"PRIu8" }\n", color[0], color[1], color[2], color[3] ); +} + +static inline int isom_print_simple( lsmash_root_t *root, isom_box_t *box, int level, char *name ) +{ + if( !box ) + return -1; + int indent = level; + isom_iprintf( indent++, "[%s: %s]\n", isom_4cc2str( box->type ), name ); + isom_iprintf( indent, "size = %"PRIu64"\n", box->size ); + return 0; +} + +static int isom_print_unknown( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + int indent = level; + isom_iprintf( indent++, "[%s]\n", isom_4cc2str( box->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", box->size ); + return 0; +} + +static int isom_print_ftyp( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_ftyp_t *ftyp = (isom_ftyp_t *)box; + int indent = level; + isom_iprintf( indent++, "[ftyp: File Type Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", ftyp->size ); + isom_iprintf( indent, "major_brand = %s\n", isom_4cc2str( ftyp->major_brand ) ); + isom_iprintf( indent, "minor_version = %"PRIu32"\n", ftyp->minor_version ); + isom_iprintf( indent++, "compatible_brands\n" ); + for( uint32_t i = 0; i < ftyp->brand_count; i++ ) + isom_iprintf( indent, "brand[%"PRIu32"] = %s\n", i, isom_4cc2str( ftyp->compatible_brands[i] ) ); + return 0; +} + +static int isom_print_moov( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Movie Box" ); +} + +static int isom_print_mvhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_mvhd_t *mvhd = (isom_mvhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[mvhd: Movie Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", mvhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", mvhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", mvhd->flags ); + isom_iprintf( indent, "creation_time = %s", isom_mp4time2utc( mvhd->creation_time ) ); + isom_iprintf( indent, "modification_time = %s", isom_mp4time2utc( mvhd->modification_time ) ); + isom_iprintf( indent, "timescale = %"PRIu32"\n", mvhd->timescale ); + isom_iprintf_duration( indent, mvhd->duration, mvhd->timescale ); + isom_iprintf( indent, "rate = %f\n", isom_fixed2double( mvhd->rate, 16 ) ); + isom_iprintf( indent, "volume = %f\n", isom_fixed2double( mvhd->volume, 8 ) ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", mvhd->reserved ); + if( root->qt_compatible ) + { + isom_iprintf( indent, "preferredLong1 = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); + isom_iprintf( indent, "preferredLong2 = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); + isom_iprintf( indent, "transformation matrix\n" ); + isom_iprint_matrix( indent + 1, mvhd->matrix ); + isom_iprintf( indent, "previewTime = %"PRId32"\n", mvhd->previewTime ); + isom_iprintf( indent, "previewDuration = %"PRId32"\n", mvhd->previewDuration ); + isom_iprintf( indent, "posterTime = %"PRId32"\n", mvhd->posterTime ); + isom_iprintf( indent, "selectionTime = %"PRId32"\n", mvhd->selectionTime ); + isom_iprintf( indent, "selectionDuration = %"PRId32"\n", mvhd->selectionDuration ); + isom_iprintf( indent, "currentTime = %"PRId32"\n", mvhd->currentTime ); + } + else + { + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[0] ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", mvhd->preferredLong[1] ); + isom_iprintf( indent, "transformation matrix\n" ); + isom_iprint_matrix( indent + 1, mvhd->matrix ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewTime ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->previewDuration ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->posterTime ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionTime ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->selectionDuration ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", mvhd->currentTime ); + } + isom_iprintf( indent, "next_track_ID = %"PRIu32"\n", mvhd->next_track_ID ); + return 0; +} + +static int isom_print_iods( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_iods_t *iods = (isom_iods_t *)box; + int indent = level; + isom_iprintf( indent++, "[iods: Object Descriptor Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", iods->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", iods->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", iods->flags & 0x00ffffff ); + return 0; +} + +static int isom_print_esds( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_esds_t *esds = (isom_esds_t *)box; + int indent = level; + isom_iprintf( indent++, "[esds: ES Descriptor Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", esds->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", esds->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", esds->flags & 0x00ffffff ); + return 0; +} + +static int isom_print_trak( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Track Box" ); +} + +static int isom_print_tkhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_tkhd_t *tkhd = (isom_tkhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[tkhd: Track Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", tkhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", tkhd->version ); + isom_iprintf( indent++, "flags = 0x%06"PRIx32"\n", tkhd->flags ); + if( tkhd->flags & ISOM_TRACK_ENABLED ) + isom_iprintf( indent, "Track enabled\n" ); + else + isom_iprintf( indent, "Track disabled\n" ); + if( tkhd->flags & ISOM_TRACK_IN_MOVIE ) + isom_iprintf( indent, "Track in movie\n" ); + if( tkhd->flags & ISOM_TRACK_IN_PREVIEW ) + isom_iprintf( indent, "Track in preview\n" ); + if( root->qt_compatible && (tkhd->flags & QT_TRACK_IN_POSTER) ) + isom_iprintf( indent, "Track in poster\n" ); + isom_iprintf( --indent, "creation_time = %s", isom_mp4time2utc( tkhd->creation_time ) ); + isom_iprintf( indent, "modification_time = %s", isom_mp4time2utc( tkhd->modification_time ) ); + isom_iprintf( indent, "track_ID = %"PRIu32"\n", tkhd->track_ID ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved1 ); + if( root && root->moov && root->moov->mvhd ) + isom_iprintf_duration( indent, tkhd->duration, root->moov->mvhd->timescale ); + else + isom_iprintf_duration( indent, tkhd->duration, 0 ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[0] ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", tkhd->reserved2[1] ); + isom_iprintf( indent, "layer = %"PRId16"\n", tkhd->layer ); + isom_iprintf( indent, "alternate_group = %"PRId16"\n", tkhd->alternate_group ); + isom_iprintf( indent, "volume = %f\n", isom_fixed2double( tkhd->volume, 8 ) ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", tkhd->reserved3 ); + isom_iprintf( indent, "transformation matrix\n" ); + isom_iprint_matrix( indent + 1, tkhd->matrix ); + isom_iprintf( indent, "width = %f\n", isom_fixed2double( tkhd->width, 16 ) ); + isom_iprintf( indent, "height = %f\n", isom_fixed2double( tkhd->height, 16 ) ); + return 0; +} + +static int isom_print_tapt( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Track Aperture Mode Dimensions Box" ); +} + +static int isom_print_clef( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_clef_t *clef = (isom_clef_t *)box; + int indent = level; + isom_iprintf( indent++, "[clef: Track Clean Aperture Dimensions Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", clef->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", clef->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", clef->flags ); + isom_iprintf( indent, "width = %f\n", isom_fixed2double( clef->width, 16 ) ); + isom_iprintf( indent, "height = %f\n", isom_fixed2double( clef->height, 16 ) ); + return 0; +} + +static int isom_print_prof( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_prof_t *prof = (isom_prof_t *)box; + int indent = level; + isom_iprintf( indent++, "[prof: Track Production Aperture Dimensions Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", prof->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", prof->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", prof->flags ); + isom_iprintf( indent, "width = %f\n", isom_fixed2double( prof->width, 16 ) ); + isom_iprintf( indent, "height = %f\n", isom_fixed2double( prof->height, 16 ) ); + return 0; +} + +static int isom_print_enof( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_enof_t *enof = (isom_enof_t *)box; + int indent = level; + isom_iprintf( indent++, "[enof: Track Encoded Pixels Dimensions Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", enof->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", enof->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", enof->flags ); + isom_iprintf( indent, "width = %f\n", isom_fixed2double( enof->width, 16 ) ); + isom_iprintf( indent, "height = %f\n", isom_fixed2double( enof->height, 16 ) ); + return 0; +} + +static int isom_print_edts( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Edit Box" ); +} + +static int isom_print_elst( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_elst_t *elst = (isom_elst_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[elst: Edit List Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", elst->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", elst->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", elst->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", 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; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "segment_duration = %"PRIu64"\n", data->segment_duration ); + isom_iprintf( indent, "media_time = %"PRId64"\n", data->media_time ); + isom_iprintf( indent--, "media_rate = %f\n", isom_fixed2double( data->media_rate, 16 ) ); + } + return 0; +} + +static int isom_print_tref( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Track Reference Box" ); +} + +static int isom_print_track_reference_type( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_tref_type_t *ref = (isom_tref_type_t *)box; + int indent = level; + isom_iprintf( indent++, "[%s: Track Reference Type Box]\n", isom_4cc2str( ref->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", ref->size ); + for( uint32_t i = 0; i < ref->ref_count; i++ ) + isom_iprintf( indent, "track_ID[%"PRIu32"] = %"PRIu32"\n", i, ref->track_ID[i] ); + return 0; + + + return isom_print_simple( root, box, level, "Track Reference Type Box" ); +} + +static int isom_print_mdia( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Media Box" ); +} + +static int isom_print_mdhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_mdhd_t *mdhd = (isom_mdhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[mdhd: Media Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", mdhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", mdhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", mdhd->flags ); + isom_iprintf( indent, "creation_time = %s", isom_mp4time2utc( mdhd->creation_time ) ); + isom_iprintf( indent, "modification_time = %s", isom_mp4time2utc( mdhd->modification_time ) ); + isom_iprintf( indent, "timescale = %"PRIu32"\n", mdhd->timescale ); + isom_iprintf_duration( indent, mdhd->duration, mdhd->timescale ); + if( mdhd->language >= 0x800 ) + isom_iprintf( indent, "language = %s\n", isom_unpack_iso_language( mdhd->language ) ); + else + isom_iprintf( indent, "language = %"PRIu16"\n", mdhd->language ); + if( root->qt_compatible ) + isom_iprintf( indent, "quality = %"PRId16"\n", mdhd->quality ); + else + isom_iprintf( indent, "pre_defined = 0x%04"PRIx16"\n", mdhd->quality ); + return 0; +} + +static int isom_print_hdlr( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_hdlr_t *hdlr = (isom_hdlr_t *)box; + int indent = level; + char str[hdlr->componentName_length + 1]; + memcpy( str, hdlr->componentName, hdlr->componentName_length ); + str[hdlr->componentName_length] = 0; + isom_iprintf( indent++, "[hdlr: Handler Reference Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", hdlr->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", hdlr->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", hdlr->flags ); + if( root->qt_compatible ) + { + isom_iprintf( indent, "componentType = %s\n", isom_4cc2str( hdlr->componentType ) ); + isom_iprintf( indent, "componentSubtype = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); + isom_iprintf( indent, "componentManufacturer = %s\n", isom_4cc2str( hdlr->componentManufacturer ) ); + isom_iprintf( indent, "componentFlags = 0x%08"PRIx32"\n", hdlr->componentFlags ); + isom_iprintf( indent, "componentFlagsMask = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); + if( hdlr->componentName_length ) + isom_iprintf( indent, "componentName = %s\n", &str[1] ); + } + else + { + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", hdlr->componentType ); + isom_iprintf( indent, "handler_type = %s\n", isom_4cc2str( hdlr->componentSubtype ) ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentManufacturer ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlags ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", hdlr->componentFlagsMask ); + isom_iprintf( indent, "name = %s\n", str ); + } + return 0; +} + +static int isom_print_minf( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Media Information Box" ); +} + +static int isom_print_vmhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_vmhd_t *vmhd = (isom_vmhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[vmhd: Video Media Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", vmhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", vmhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", vmhd->flags ); + isom_iprintf( indent, "graphicsmode = %"PRIu16"\n", vmhd->graphicsmode ); + isom_iprintf( indent, "opcolor\n" ); + isom_iprint_rgb_color( indent + 1, vmhd->opcolor ); + return 0; +} + +static int isom_print_smhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_smhd_t *smhd = (isom_smhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[smhd: Sound Media Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", smhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", smhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", smhd->flags ); + isom_iprintf( indent, "balance = %f\n", isom_fixed2double( smhd->balance, 8 ) ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", smhd->reserved ); + return 0; +} + +static int isom_print_hmhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_hmhd_t *hmhd = (isom_hmhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[hmhd: Hint Media Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", hmhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", hmhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", hmhd->flags ); + isom_iprintf( indent, "maxPDUsize = %"PRIu16"\n", hmhd->maxPDUsize ); + isom_iprintf( indent, "avgPDUsize = %"PRIu16"\n", hmhd->avgPDUsize ); + isom_iprintf( indent, "maxbitrate = %"PRIu32"\n", hmhd->maxbitrate ); + isom_iprintf( indent, "avgbitrate = %"PRIu32"\n", hmhd->avgbitrate ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", hmhd->reserved ); + return 0; +} + +static int isom_print_nmhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_nmhd_t *nmhd = (isom_nmhd_t *)box; + int indent = level; + isom_iprintf( indent++, "[nmhd: Null Media Header Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", nmhd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", nmhd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", nmhd->flags ); + return 0; +} + +static int isom_print_gmhd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Generic Media Information Header Box" ); +} + +static int isom_print_gmin( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_gmin_t *gmin = (isom_gmin_t *)box; + int indent = level; + isom_iprintf( indent++, "[gmin: Generic Media Information Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", gmin->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", gmin->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", gmin->flags ); + isom_iprintf( indent, "graphicsmode = %"PRIu16"\n", gmin->graphicsmode ); + isom_iprintf( indent, "opcolor\n" ); + isom_iprint_rgb_color( indent + 1, gmin->opcolor ); + isom_iprintf( indent, "balance = %f\n", isom_fixed2double( gmin->balance, 8 ) ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", gmin->reserved ); + return 0; +} + +static int isom_print_text( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_text_t *text = (isom_text_t *)box; + int indent = level; + isom_iprintf( indent++, "[text]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", text->size ); + isom_iprintf( indent, "Unknown matrix\n" ); + isom_iprint_matrix( indent + 1, text->matrix ); + return 0; +} + +static int isom_print_dinf( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Data Information Box" ); +} + +static int isom_print_dref( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_dref_t *dref = (isom_dref_t *)box; + int indent = level; + isom_iprintf( indent++, "[dref: Data Reference Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", dref->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", dref->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", dref->flags ); + isom_iprintf( indent, "entry_count = %"PRIu16"\n", dref->list->entry_count ); + return 0; +} + +static int isom_print_url( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_dref_entry_t *url = (isom_dref_entry_t *)box; + int indent = level; + isom_iprintf( indent++, "[url : Data Entry Url Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", url->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", url->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", url->flags ); + if( url->flags & 0x000001 ) + isom_iprintf( indent, "location = in the same file\n" ); + else + isom_iprintf( indent, "location = %s\n", url->location ); + return 0; +} + +static int isom_print_stbl( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Sample Table Box" ); +} + +static int isom_print_stsd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stsd_t *)box)->list ) + return -1; + isom_stsd_t *stsd = (isom_stsd_t *)box; + int indent = level; + isom_iprintf( indent++, "[stsd: Sample Description Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stsd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stsd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stsd->flags ); + isom_iprintf( indent, "entry_count = %"PRIu16"\n", stsd->list->entry_count ); + return 0; +} + +static int isom_print_visual_description( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_visual_entry_t *visual = (isom_visual_entry_t *)box; + int indent = level; + uint64_t reserved = visual->reserved[0] | visual->reserved[1] | visual->reserved[2] + | visual->reserved[3] | visual->reserved[4] | visual->reserved[5]; + isom_iprintf( indent++, "[%s: Visual Description]\n", isom_4cc2str( visual->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", visual->size ); + isom_iprintf( indent, "reserved = 0x%016"PRIx64"\n", reserved ); + isom_iprintf( indent, "data_reference_index = %"PRIu16"\n", visual->data_reference_index ); + if( root->qt_compatible ) + { + isom_iprintf( indent, "version = %"PRId16"\n", visual->version ); + isom_iprintf( indent, "revision_level = %"PRId16"\n", visual->revision_level ); + isom_iprintf( indent, "vendor = %s\n", isom_4cc2str( visual->vendor ) ); + isom_iprintf( indent, "temporalQuality = %"PRIu32"\n", visual->temporalQuality ); + isom_iprintf( indent, "spatialQuality = %"PRIu32"\n", visual->spatialQuality ); + isom_iprintf( indent, "width = %"PRIu16"\n", visual->width ); + isom_iprintf( indent, "height = %"PRIu16"\n", visual->height ); + isom_iprintf( indent, "horizresolution = %f\n", isom_fixed2double( visual->horizresolution, 16 ) ); + isom_iprintf( indent, "vertresolution = %f\n", isom_fixed2double( visual->vertresolution, 16 ) ); + isom_iprintf( indent, "dataSize = %"PRIu32"\n", visual->dataSize ); + isom_iprintf( indent, "frame_count = %"PRIu16"\n", visual->frame_count ); + isom_iprintf( indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); + isom_iprintf( indent, "compressorname = %s\n", visual->compressorname + 1 ); + isom_iprintf( indent, "depth = 0x%04"PRIx16, visual->depth ); + if( visual->depth == 32 ) + printf( " (colour with alpha)\n" ); + else if( visual->depth >= 33 && visual->depth <= 40 ) + printf( " (grayscale with no alpha)\n" ); + else + printf( "\n" ); + isom_iprintf( indent, "color_table_ID = %"PRId16"\n", visual->color_table_ID ); + } + else + { + isom_iprintf( indent, "pre_defined = 0x%04"PRIx16"\n", visual->version ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", visual->revision_level ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", visual->vendor ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", visual->temporalQuality ); + isom_iprintf( indent, "pre_defined = 0x%08"PRIx32"\n", visual->spatialQuality ); + isom_iprintf( indent, "width = %"PRIu16"\n", visual->width ); + isom_iprintf( indent, "height = %"PRIu16"\n", visual->height ); + isom_iprintf( indent, "horizresolution = %f\n", isom_fixed2double( visual->horizresolution, 16 ) ); + isom_iprintf( indent, "vertresolution = %f\n", isom_fixed2double( visual->vertresolution, 16 ) ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", visual->dataSize ); + isom_iprintf( indent, "frame_count = %"PRIu16"\n", visual->frame_count ); + isom_iprintf( indent, "compressorname_length = %"PRIu8"\n", visual->compressorname[0] ); + isom_iprintf( indent, "compressorname = %s\n", visual->compressorname + 1 ); + isom_iprintf( indent, "depth = 0x%04"PRIx16, visual->depth ); + if( visual->depth == 0x0018 ) + printf( " (colour with no alpha)\n" ); + else if( visual->depth == 0x0028 ) + printf( " (grayscale with no alpha)\n" ); + else if( visual->depth == 0x0020 ) + printf( " (gray or colour with alpha)\n" ); + else + printf( "\n" ); + isom_iprintf( indent, "pre_defined = 0x%04"PRIx16"\n", visual->color_table_ID ); + } + return 0; +} + +static int isom_print_btrt( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_btrt_t *btrt = (isom_btrt_t *)box; + int indent = level; + isom_iprintf( indent++, "[btrt: Bit Rate Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", btrt->size ); + isom_iprintf( indent, "bufferSizeDB = %"PRIu32"\n", btrt->bufferSizeDB ); + isom_iprintf( indent, "maxBitrate = %"PRIu32"\n", btrt->maxBitrate ); + isom_iprintf( indent, "avgBitrate = %"PRIu32"\n", btrt->avgBitrate ); + return 0; +} + +static int isom_print_clap( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_clap_t *clap = (isom_clap_t *)box; + int indent = level; + isom_iprintf( indent++, "[clap: Clean Aperture Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", clap->size ); + isom_iprintf( indent, "cleanApertureWidthN = %"PRIu32"\n", clap->cleanApertureWidthN ); + isom_iprintf( indent, "cleanApertureWidthD = %"PRIu32"\n", clap->cleanApertureWidthD ); + isom_iprintf( indent, "cleanApertureHeightN = %"PRIu32"\n", clap->cleanApertureHeightN ); + isom_iprintf( indent, "cleanApertureHeightD = %"PRIu32"\n", clap->cleanApertureHeightD ); + isom_iprintf( indent, "horizOffN = %"PRId32"\n", clap->horizOffN ); + isom_iprintf( indent, "horizOffD = %"PRIu32"\n", clap->horizOffD ); + isom_iprintf( indent, "vertOffN = %"PRId32"\n", clap->vertOffN ); + isom_iprintf( indent, "vertOffD = %"PRIu32"\n", clap->vertOffD ); + return 0; +} + +static int isom_print_pasp( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_pasp_t *pasp = (isom_pasp_t *)box; + int indent = level; + isom_iprintf( indent++, "[pasp: Pixel Aspect Ratio Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", pasp->size ); + isom_iprintf( indent, "hSpacing = %"PRIu32"\n", pasp->hSpacing ); + isom_iprintf( indent, "vSpacing = %"PRIu32"\n", pasp->vSpacing ); + return 0; +} + +static int isom_print_colr( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_colr_t *colr = (isom_colr_t *)box; + int indent = level; + isom_iprintf( indent++, "[colr: Color Parameter Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", colr->size ); + isom_iprintf( indent, "color_parameter_type = %s\n", isom_4cc2str( colr->color_parameter_type ) ); + if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC ) + { + isom_iprintf( indent, "primaries_index = %"PRIu16"\n", colr->primaries_index ); + isom_iprintf( indent, "transfer_function_index = %"PRIu16"\n", colr->transfer_function_index ); + isom_iprintf( indent, "matrix_index = %"PRIu16"\n", colr->matrix_index ); + } + return 0; +} + +static int isom_print_stsl( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_stsl_t *stsl = (isom_stsl_t *)box; + int indent = level; + isom_iprintf( indent++, "[stsl: Sample Scale Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stsl->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stsl->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stsl->flags ); + isom_iprintf( indent, "constraint_flag = %s\n", (stsl->constraint_flag & 0x01) ? "on" : "off" ); + isom_iprintf( indent, "scale_method = " ); + if( stsl->scale_method == ISOM_SCALING_METHOD_FILL ) + printf( "'fill'\n" ); + else if( stsl->scale_method == ISOM_SCALING_METHOD_HIDDEN ) + printf( "'hidden'\n" ); + else if( stsl->scale_method == ISOM_SCALING_METHOD_MEET ) + printf( "'meet'\n" ); + else if( stsl->scale_method == ISOM_SCALING_METHOD_SLICE_X ) + printf( "'slice' in the x-coodinate\n" ); + else if( stsl->scale_method == ISOM_SCALING_METHOD_SLICE_Y ) + printf( "'slice' in the y-coodinate\n" ); + isom_iprintf( indent, "display_center_x = %"PRIu16"\n", stsl->display_center_x ); + isom_iprintf( indent, "display_center_y = %"PRIu16"\n", stsl->display_center_y ); + return 0; +} + +static int isom_print_avcC( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_avcC_t *avcC = (isom_avcC_t *)box; + int indent = level; + isom_iprintf( indent++, "[avcC: AVCDecoderConfigurationRecord]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", avcC->size ); + isom_iprintf( indent, "configurationVersion = %"PRIu8"\n", avcC->configurationVersion ); + isom_iprintf( indent, "AVCProfileIndication = %"PRIu8"\n", avcC->AVCProfileIndication ); + isom_iprintf( indent, "profile_compatibility = 0x%02"PRIu8"\n", avcC->profile_compatibility ); + isom_iprintf( indent, "AVCLevelIndication = %"PRIu8"\n", avcC->AVCLevelIndication ); + isom_iprintf( indent, "lengthSizeMinusOne = %"PRIu8"\n", avcC->lengthSizeMinusOne & 0x03 ); + isom_iprintf( indent, "numOfSequenceParameterSets = %"PRIu8"\n", avcC->numOfSequenceParameterSets & 0x1f ); + isom_iprintf( indent, "numOfPictureParameterSets = %"PRIu8"\n", avcC->numOfPictureParameterSets ); + if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) ) + { + isom_iprintf( indent, "chroma_format = %"PRIu8"\n", avcC->chroma_format & 0x03 ); + isom_iprintf( indent, "bit_depth_luma_minus8 = %"PRIu8"\n", avcC->bit_depth_luma_minus8 & 0x7 ); + isom_iprintf( indent, "bit_depth_chroma_minus8 = %"PRIu8"\n", avcC->bit_depth_chroma_minus8 & 0x7 ); + isom_iprintf( indent, "numOfSequenceParameterSetExt = %"PRIu8"\n", avcC->numOfSequenceParameterSetExt ); + } + return 0; +} + +static int isom_print_audio_description( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_audio_entry_t *audio = (isom_audio_entry_t *)box; + int indent = level; + uint64_t reserved = audio->reserved[0] | audio->reserved[1] | audio->reserved[2] + | audio->reserved[3] | audio->reserved[4] | audio->reserved[5]; + isom_iprintf( indent++, "[%s: Audio Description]\n", isom_4cc2str( audio->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", audio->size ); + isom_iprintf( indent, "reserved = 0x%016"PRIx64"\n", reserved ); + isom_iprintf( indent, "data_reference_index = %"PRIu16"\n", audio->data_reference_index ); + if( root->qt_compatible ) + { + isom_iprintf( indent, "version = %"PRId16"\n", audio->version ); + isom_iprintf( indent, "revision_level = %"PRId16"\n", audio->revision_level ); + isom_iprintf( indent, "vendor = %s\n", isom_4cc2str( audio->vendor ) ); + isom_iprintf( indent, "channelcount = %"PRIu16"\n", audio->channelcount ); + isom_iprintf( indent, "samplesize = %"PRIu16"\n", audio->samplesize ); + isom_iprintf( indent, "compression_ID = %"PRId16"\n", audio->compression_ID ); + isom_iprintf( indent, "packet_size = %"PRIu16"\n", audio->packet_size ); + } + else + { + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", audio->version ); + isom_iprintf( indent, "reserved = 0x%04"PRIx16"\n", audio->revision_level ); + isom_iprintf( indent, "reserved = 0x%08"PRIx32"\n", audio->vendor ); + isom_iprintf( indent, "channelcount = %"PRIu16"\n", audio->channelcount ); + isom_iprintf( indent, "samplesize = %"PRIu16"\n", audio->samplesize ); + isom_iprintf( indent, "pre_defined = %"PRId16"\n", audio->compression_ID ); + isom_iprintf( indent, "reserved = %"PRIu16"\n", audio->packet_size ); + } + isom_iprintf( indent, "samplerate = %f\n", isom_fixed2double( audio->samplerate, 16 ) ); + if( audio->version == 1 ) + { + isom_iprintf( indent, "samplesPerPacket = %"PRIu32"\n", audio->samplesPerPacket ); + isom_iprintf( indent, "bytesPerPacket = %"PRIu32"\n", audio->bytesPerPacket ); + isom_iprintf( indent, "bytesPerFrame = %"PRIu32"\n", audio->bytesPerFrame ); + isom_iprintf( indent, "bytesPerSample = %"PRIu32"\n", audio->bytesPerSample ); + } + else if( audio->version == 2 ) + { + isom_iprintf( indent, "sizeOfStructOnly = %"PRIu32"\n", audio->sizeOfStructOnly ); + isom_iprintf( indent, "audioSampleRate = %lf\n", isom_int2float64( audio->audioSampleRate ) ); + isom_iprintf( indent, "numAudioChannels = %"PRIu32"\n", audio->numAudioChannels ); + isom_iprintf( indent, "always7F000000 = 0x%08"PRIx32"\n", audio->always7F000000 ); + isom_iprintf( indent, "constBitsPerChannel = %"PRIu32"\n", audio->constBitsPerChannel ); + isom_iprintf( indent++, "formatSpecificFlags = 0x%08"PRIx32"\n", audio->formatSpecificFlags ); + switch( audio->type ) + { + case QT_CODEC_TYPE_LPCM_AUDIO_CASE : + isom_iprintf( indent, "sample format: " ); + if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_FLOAT ) + printf( "floating point\n" ); + else + { + printf( "integer\n" ); + isom_iprintf( indent, "signedness: " ); + printf( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER ? "signed\n" : "unsigned\n" ); + } + if( audio->constBytesPerAudioPacket != 1 ) + { + isom_iprintf( indent, "endianness: " ); + printf( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_BIG_ENDIAN ? "big\n" : "little\n" ); + } + isom_iprintf( indent, "packed: " ); + if( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_PACKED ) + printf( "yes\n" ); + else + { + printf( "no\n" ); + isom_iprintf( indent, "alignment: " ); + printf( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH ? "high\n" : "low\n" ); + } + if( audio->numAudioChannels > 1 ) + { + isom_iprintf( indent, "interleved: " ); + printf( audio->formatSpecificFlags & QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED ? "no\n" : "yes\n" ); + } + break; + default : + break; + } + isom_iprintf( --indent, "constBytesPerAudioPacket = %"PRIu32"\n", audio->constBytesPerAudioPacket ); + isom_iprintf( indent, "constLPCMFramesPerAudioPacket = %"PRIu32"\n", audio->constLPCMFramesPerAudioPacket ); + } + return 0; +} + +static int isom_print_wave( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "Sound Information Decompression Parameters Box" ); +} + +static int isom_print_frma( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_frma_t *frma = (isom_frma_t *)box; + int indent = level; + isom_iprintf( indent++, "[frma: Format Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", frma->size ); + isom_iprintf( indent, "data_format = %s\n", isom_4cc2str( frma->data_format ) ); + return 0; +} + +static int isom_print_terminator( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_terminator_t *terminator = (isom_terminator_t *)box; + int indent = level; + isom_iprintf( indent++, "[0x00000000: Terminator Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", terminator->size ); + return 0; +} + +static int isom_print_chan( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_chan_t *chan = (isom_chan_t *)box; + int indent = level; + isom_iprintf( indent++, "[chan: Channel Compositor Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", chan->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", chan->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", chan->flags ); + isom_iprintf( indent, "channelLayoutTag = 0x%08"PRIx32"\n", chan->channelLayoutTag ); + isom_iprintf( indent, "channelBitmap = 0x%08"PRIx32"\n", chan->channelBitmap ); + isom_iprintf( indent, "numberChannelDescriptions = %"PRIu32"\n", chan->numberChannelDescriptions ); + if( chan->numberChannelDescriptions ) + { + isom_channel_description_t *desc = chan->channelDescriptions; + for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) + { + isom_iprintf( indent++, "ChannelDescriptions[%"PRIu32"]\n", i ); + isom_iprintf( indent, "channelLabel = 0x%08"PRIx32"\n", desc->channelLabel ); + isom_iprintf( indent, "channelFlags = 0x%08"PRIx32"\n", desc->channelFlags ); + for( int j = 0; j < 3; j++ ) + isom_iprintf( indent, "coordinates[%d] = %f\n", j, isom_int2float32( desc->coordinates[j] ) ); + --indent; + } + } + return 0; +} + +static int isom_print_text_description( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_text_entry_t *text = (isom_text_entry_t *)box; + int indent = level; + isom_iprintf( indent++, "[%s: QuickTime Text Description]\n", isom_4cc2str( text->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", text->size ); + isom_iprintf( indent, "data_reference_index = %"PRIu16"\n", text->data_reference_index ); + isom_iprintf( indent, "displayFlags = 0x%08"PRId32"\n", text->displayFlags ); + isom_iprintf( indent, "textJustification = %"PRId32"\n", text->textJustification ); + isom_iprintf( indent, "bgColor\n" ); + isom_iprint_rgb_color( indent + 1, text->bgColor ); + isom_iprintf( indent, "top = %"PRId16"\n", text->top ); + isom_iprintf( indent, "left = %"PRId16"\n", text->left ); + isom_iprintf( indent, "bottom = %"PRId16"\n", text->bottom ); + isom_iprintf( indent, "right = %"PRId16"\n", text->right ); + isom_iprintf( indent, "scrpStartChar = %"PRId32"\n", text->scrpStartChar ); + isom_iprintf( indent, "scrpHeight = %"PRId16"\n", text->scrpHeight ); + isom_iprintf( indent, "scrpAscent = %"PRId16"\n", text->scrpAscent ); + isom_iprintf( indent, "scrpFont = %"PRId16"\n", text->scrpFont ); + isom_iprintf( indent, "scrpFace = %"PRIu16"\n", text->scrpFace ); + isom_iprintf( indent, "scrpSize = %"PRId16"\n", text->scrpSize ); + isom_iprintf( indent, "scrpColor\n" ); + isom_iprint_rgb_color( indent + 1, text->scrpColor ); + if( text->font_name_length ) + isom_iprintf( indent, "font_name = %s\n", text->font_name ); + return 0; +} + +static int isom_print_tx3g_description( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)box; + int indent = level; + isom_iprintf( indent++, "[%s: Timed Text Description]\n", isom_4cc2str( tx3g->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", tx3g->size ); + isom_iprintf( indent, "data_reference_index = %"PRIu16"\n", tx3g->data_reference_index ); + isom_iprintf( indent, "displayFlags = 0x%08"PRId32"\n", tx3g->displayFlags ); + isom_iprintf( indent, "horizontal_justification = %"PRId8"\n", tx3g->horizontal_justification ); + isom_iprintf( indent, "vertical_justification = %"PRId8"\n", tx3g->vertical_justification ); + isom_iprintf( indent, "background_color_rgba\n" ); + isom_iprint_rgba_color( indent + 1, tx3g->background_color_rgba ); + isom_iprintf( indent, "top = %"PRId16"\n", tx3g->top ); + isom_iprintf( indent, "left = %"PRId16"\n", tx3g->left ); + isom_iprintf( indent, "bottom = %"PRId16"\n", tx3g->bottom ); + isom_iprintf( indent, "right = %"PRId16"\n", tx3g->right ); + isom_iprintf( indent, "startChar = %"PRIu16"\n", tx3g->startChar ); + isom_iprintf( indent, "endChar = %"PRIu16"\n", tx3g->endChar ); + isom_iprintf( indent, "font_ID = %"PRIu16"\n", tx3g->font_ID ); + isom_iprintf( indent, "face_style_flags = %"PRIu8"\n", tx3g->face_style_flags ); + isom_iprintf( indent, "font_size = %"PRIu8"\n", tx3g->font_size ); + isom_iprintf( indent, "text_color_rgba\n" ); + isom_iprint_rgba_color( indent + 1, tx3g->text_color_rgba ); + return 0; +} + +static int isom_print_ftab( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_ftab_t *)box)->list ) + return -1; + isom_ftab_t *ftab = (isom_ftab_t *)box; + int indent = level; + uint16_t i = 0; + isom_iprintf( indent++, "[ftab: Font Table Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", ftab->size ); + isom_iprintf( indent, "entry_count = %"PRIu16"\n", 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; + isom_iprintf( indent++, "entry[%"PRIu16"]\n", i++ ); + isom_iprintf( indent, "font_ID = %"PRIu16"\n", data->font_ID ); + if( data->font_name_length ) + isom_iprintf( indent, "font_name = %s\n", data->font_name ); + --indent; + } + return 0; +} + +static int isom_print_stts( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stts_t *)box)->list ) + return -1; + isom_stts_t *stts = (isom_stts_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[stts: Decoding Time to Sample Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stts->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stts->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stts->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", 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; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "sample_count = %"PRIu32"\n", data->sample_count ); + isom_iprintf( indent--, "sample_delta = %"PRIu32"\n", data->sample_delta ); + } + return 0; +} + +static int isom_print_ctts( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_ctts_t *)box)->list ) + return -1; + isom_ctts_t *ctts = (isom_ctts_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[ctts: Composition Time to Sample Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", ctts->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", ctts->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", ctts->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", ctts->list->entry_count ); + if( root->qt_compatible || ctts->version == 1 ) + for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) + { + isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "sample_count = %"PRIu32"\n", data->sample_count ); + isom_iprintf( indent--, "sample_offset = %"PRId32"\n", (union {uint32_t ui; int32_t si;}){data->sample_offset}.si ); + } + else + for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) + { + isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "sample_count = %"PRIu32"\n", data->sample_count ); + isom_iprintf( indent--, "sample_offset = %"PRIu32"\n", data->sample_offset ); + } + return 0; +} + +static int isom_print_cslg( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_cslg_t *cslg = (isom_cslg_t *)box; + int indent = level; + if( root->qt_compatible ) + { + isom_iprintf( indent++, "[cslg: Composition Shift Least Greatest Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", cslg->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", cslg->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", cslg->flags ); + isom_iprintf( indent, "compositionOffsetToDTDDeltaShift = %"PRId32"\n", cslg->compositionToDTSShift ); + isom_iprintf( indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); + isom_iprintf( indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); + isom_iprintf( indent, "displayStartTime = %"PRId32"\n", cslg->compositionStartTime ); + isom_iprintf( indent, "displayEndTime = %"PRId32"\n", cslg->compositionEndTime ); + } + else + { + isom_iprintf( indent++, "[cslg: Composition to Decode Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", cslg->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", cslg->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", cslg->flags ); + isom_iprintf( indent, "compositionToDTSShift = %"PRId32"\n", cslg->compositionToDTSShift ); + isom_iprintf( indent, "leastDecodeToDisplayDelta = %"PRId32"\n", cslg->leastDecodeToDisplayDelta ); + isom_iprintf( indent, "greatestDecodeToDisplayDelta = %"PRId32"\n", cslg->greatestDecodeToDisplayDelta ); + isom_iprintf( indent, "compositionStartTime = %"PRId32"\n", cslg->compositionStartTime ); + isom_iprintf( indent, "compositionEndTime = %"PRId32"\n", cslg->compositionEndTime ); + } + return 0; +} + +static int isom_print_stss( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stss_t *)box)->list ) + return -1; + isom_stss_t *stss = (isom_stss_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[stss: Sync Sample Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stss->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stss->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stss->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", stss->list->entry_count ); + for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next ) + isom_iprintf( indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stss_entry_t *)entry->data)->sample_number ); + return 0; +} + +static int isom_print_stps( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stps_t *)box)->list ) + return -1; + isom_stps_t *stps = (isom_stps_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[stps: Partial Sync Sample Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stps->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stps->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stps->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", stps->list->entry_count ); + for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next ) + isom_iprintf( indent, "sample_number[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stps_entry_t *)entry->data)->sample_number ); + return 0; +} + +static int isom_print_sdtp( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_sdtp_t *)box)->list ) + return -1; + isom_sdtp_t *sdtp = (isom_sdtp_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[sdtp: Independent and Disposable Samples Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", sdtp->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", sdtp->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", sdtp->flags ); + for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next ) + { + isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + if( data->is_leading || data->sample_depends_on || data->sample_is_depended_on || data->sample_has_redundancy ) + { + if( root->avc_extensions ) + { + if( data->is_leading & ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) + isom_iprintf( indent, "undecodable leading\n" ); + else if( data->is_leading & ISOM_SAMPLE_IS_NOT_LEADING ) + isom_iprintf( indent, "non-leading\n" ); + else if( data->is_leading & ISOM_SAMPLE_IS_DECODABLE_LEADING ) + isom_iprintf( indent, "decodable leading\n" ); + } + else if( data->is_leading & QT_SAMPLE_EARLIER_PTS_ALLOWED ) + isom_iprintf( indent, "early display times allowed\n" ); + if( data->sample_depends_on & ISOM_SAMPLE_IS_INDEPENDENT ) + isom_iprintf( indent, "independent\n" ); + else if( data->sample_depends_on & ISOM_SAMPLE_IS_NOT_INDEPENDENT ) + isom_iprintf( indent, "dependent\n" ); + if( data->sample_is_depended_on & ISOM_SAMPLE_IS_NOT_DISPOSABLE ) + isom_iprintf( indent, "non-disposable\n" ); + else if( data->sample_is_depended_on & ISOM_SAMPLE_IS_DISPOSABLE ) + isom_iprintf( indent, "disposable\n" ); + if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_REDUNDANCY ) + isom_iprintf( indent, "redundant\n" ); + else if( data->sample_has_redundancy & ISOM_SAMPLE_HAS_NO_REDUNDANCY ) + isom_iprintf( indent, "non-redundant\n" ); + } + else + isom_iprintf( indent, "no description\n" ); + --indent; + } + return 0; +} + +static int isom_print_stsc( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stsc_t *)box)->list ) + return -1; + isom_stsc_t *stsc = (isom_stsc_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[stsc: Sample To Chunk Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stsc->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stsc->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stsc->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", 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; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "first_chunk = %"PRIu32"\n", data->first_chunk ); + isom_iprintf( indent, "samples_per_chunk = %"PRIu32"\n", data->samples_per_chunk ); + isom_iprintf( indent--, "sample_description_index = %"PRIu32"\n", data->sample_description_index ); + } + return 0; +} + +static int isom_print_stsz( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_stsz_t *stsz = (isom_stsz_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[stsz: Sample Size Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", stsz->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stsz->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stsz->flags ); + if( !stsz->sample_size ) + isom_iprintf( indent, "sample_size = 0 (variable)\n" ); + else + isom_iprintf( indent, "sample_size = %"PRIu32" (constant)\n", stsz->sample_size ); + isom_iprintf( indent, "sample_count = %"PRIu32"\n", stsz->sample_count ); + if( !stsz->sample_size && 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; + isom_iprintf( indent, "entry_size[%"PRIu32"] = %"PRIu32"\n", i++, data->entry_size ); + } + return 0; +} + +static int isom_print_stco( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_stco_t *)box)->list ) + return -1; + isom_stco_t *stco = (isom_stco_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[%s: Chunk Offset Box]\n", isom_4cc2str( stco->type ) ); + isom_iprintf( indent, "size = %"PRIu64"\n", stco->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", stco->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", stco->flags ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", stco->list->entry_count ); + if( stco->type == ISOM_BOX_TYPE_STCO ) + { + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + isom_iprintf( indent, "chunk_offset[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_stco_entry_t *)entry->data)->chunk_offset ); + } + else + { + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + isom_iprintf( indent, "chunk_offset[%"PRIu32"] = %"PRIu64"\n", i++, ((isom_co64_entry_t *)entry->data)->chunk_offset ); + } + return 0; +} + +static int isom_print_sgpd( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_sgpd_t *)box)->list ) + return -1; + isom_sgpd_t *sgpd = (isom_sgpd_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[sgpd: Sample Group Description Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", sgpd->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", sgpd->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", sgpd->flags ); + isom_iprintf( indent, "grouping_type = %s\n", isom_4cc2str( sgpd->grouping_type ) ); + if( sgpd->version == 1 ) + { + isom_iprintf( indent, "default_length = %"PRIu32, sgpd->default_length ); + printf( " %s\n", sgpd->default_length ? "(constant)" : "(variable)" ); + } + isom_iprintf( indent, "entry_count = %"PRIu32"\n", sgpd->list->entry_count ); + if( sgpd->grouping_type == ISOM_GROUP_TYPE_ROLL ) + for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) + { + if( sgpd->version == 1 && !sgpd->default_length ) + isom_iprintf( indent, "description_length[%"PRIu32"] = %"PRIu32"\n", i++, ((isom_roll_entry_t *)entry->data)->description_length ); + else + isom_iprintf( indent, "roll_distance[%"PRIu32"] = %"PRIu16"\n", i++, ((isom_roll_entry_t *)entry->data)->roll_distance ); + } + return 0; +} + +static int isom_print_sbgp( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box || !((isom_sbgp_t *)box)->list ) + return -1; + isom_sbgp_t *sbgp = (isom_sbgp_t *)box; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[sbgp: Sample to Group Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", sbgp->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", sbgp->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", sbgp->flags ); + isom_iprintf( indent, "grouping_type = %s\n", isom_4cc2str( sbgp->grouping_type ) ); + if( sbgp->version == 1 ) + isom_iprintf( indent, "grouping_type_parameter = %s\n", isom_4cc2str( sbgp->grouping_type_parameter ) ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", sbgp->list->entry_count ); + for( lsmash_entry_t *entry = sbgp->list->head; entry; entry = entry->next ) + { + isom_sbgp_entry_t *data = (isom_sbgp_entry_t *)entry->data; + isom_iprintf( indent++, "entry[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "sample_count = %"PRIu32"\n", data->sample_count ); + isom_iprintf( indent--, "group_description_index = %"PRIu32, data->group_description_index ); + if( !data->group_description_index ) + printf( " (not in this grouping type)\n" ); + else + printf( "\n" ); + } + return 0; +} + +static int isom_print_udta( lsmash_root_t *root, isom_box_t *box, int level ) +{ + return isom_print_simple( root, box, level, "User Data Box" ); +} + +static int isom_print_chpl( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_chpl_t *chpl = (isom_chpl_t *)box; + uint32_t timescale; + if( !chpl->version ) + { + if( !root->moov && !root->moov->mvhd ) + return -1; + timescale = root->moov->mvhd->timescale; + } + else + timescale = 10000000; + int indent = level; + uint32_t i = 0; + isom_iprintf( indent++, "[chpl: Chapter List Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", chpl->size ); + isom_iprintf( indent, "version = %"PRIu8"\n", chpl->version ); + isom_iprintf( indent, "flags = 0x%06"PRIx32"\n", chpl->flags ); + if( chpl->version == 1 ) + { + isom_iprintf( indent, "unknown = 0x%02"PRIx8"\n", chpl->unknown ); + isom_iprintf( indent, "entry_count = %"PRIu32"\n", chpl->list->entry_count ); + } + else + isom_iprintf( indent, "entry_count = %"PRIu8"\n", (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; + int64_t start_time = data->start_time / timescale; + int hh = start_time / 3600; + int mm = (start_time / 60) % 60; + int ss = start_time % 60; + int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5; + char str[256]; + memset( str, 0, sizeof(str) ); + memcpy( str, data->chapter_name, data->chapter_name_length ); + isom_iprintf( indent++, "chapter[%"PRIu32"]\n", i++ ); + isom_iprintf( indent, "start_time = %02d:%02d:%02d.%03d\n", hh, mm, ss, ms ); + isom_iprintf( indent--, "chapter_name = %s\n", data->chapter_name ); + } + return 0; +} + +static int isom_print_free( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_free_t *skip = (isom_free_t *)box; + int indent = level; + isom_iprintf( indent++, "[free: Free Space Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", skip->size ); + return 0; +} + +static int isom_print_mdat( lsmash_root_t *root, isom_box_t *box, int level ) +{ + if( !box ) + return -1; + isom_mdat_t *mdat = (isom_mdat_t *)box; + int indent = level; + isom_iprintf( indent++, "[mdat: Media Data Box]\n" ); + isom_iprintf( indent, "size = %"PRIu64"\n", mdat->size ); + return 0; +} + +int lsmash_print_movie( lsmash_root_t *root ) +{ + if( !root || !root->print || !(root->flags & LSMASH_FILE_MODE_DUMP) ) + return -1; + printf( "[ROOT]\n" ); + printf( " size = %"PRIu64"\n", root->size ); + for( lsmash_entry_t *entry = root->print->head; entry; entry = entry->next ) + { + isom_print_entry_t *data = (isom_print_entry_t *)entry->data; + if( !data || data->func( root, data->box, data->level ) ) + return -1; + } + return 0; +} + +static isom_print_box_t isom_select_print_func( isom_box_t *box ) +{ + if( box->manager & 0x01 ) + return isom_print_unknown; + if( box->parent ) + { + if( box->parent->type == ISOM_BOX_TYPE_STSD ) + switch( box->type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_MP4V_VIDEO : + case ISOM_CODEC_TYPE_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_SVC1_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + return isom_print_visual_description; + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + 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_DTSE_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_MP4A_AUDIO : + //case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_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 : + case QT_CODEC_TYPE_23NI_AUDIO : + case QT_CODEC_TYPE_MAC3_AUDIO : + case QT_CODEC_TYPE_MAC6_AUDIO : + case QT_CODEC_TYPE_NONE_AUDIO : + case QT_CODEC_TYPE_QDM2_AUDIO : + case QT_CODEC_TYPE_QDMC_AUDIO : + case QT_CODEC_TYPE_QCLP_AUDIO : + case QT_CODEC_TYPE_AGSM_AUDIO : + case QT_CODEC_TYPE_ALAW_AUDIO : + case QT_CODEC_TYPE_CDX2_AUDIO : + case QT_CODEC_TYPE_CDX4_AUDIO : + case QT_CODEC_TYPE_DVCA_AUDIO : + case QT_CODEC_TYPE_DVI_AUDIO : + case QT_CODEC_TYPE_FL32_AUDIO : + case QT_CODEC_TYPE_FL64_AUDIO : + case QT_CODEC_TYPE_IMA4_AUDIO : + case QT_CODEC_TYPE_IN24_AUDIO : + case QT_CODEC_TYPE_IN32_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_ULAW_AUDIO : + case QT_CODEC_TYPE_VDVA_AUDIO : + case QT_CODEC_TYPE_FULLMP3_AUDIO : + case QT_CODEC_TYPE_MP3_AUDIO : + case QT_CODEC_TYPE_ADPCM2_AUDIO : + case QT_CODEC_TYPE_ADPCM17_AUDIO : + case QT_CODEC_TYPE_GSM49_AUDIO : + case QT_CODEC_TYPE_NOT_SPECIFIED : + return isom_print_audio_description; + case QT_CODEC_TYPE_TEXT_TEXT : + return isom_print_text_description; + case ISOM_CODEC_TYPE_TX3G_TEXT : + return isom_print_tx3g_description; + default : + return isom_print_unknown; + } + if( box->parent->type == QT_BOX_TYPE_WAVE ) + switch( box->type ) + { + case QT_BOX_TYPE_FRMA : + return isom_print_frma; + case ISOM_BOX_TYPE_ESDS : + return isom_print_esds; + case QT_BOX_TYPE_TERMINATOR : + return isom_print_terminator; + default : + return isom_print_unknown; + } + if( box->parent->type == ISOM_BOX_TYPE_TREF ) + return isom_print_track_reference_type; + } + switch( box->type ) + { + case ISOM_BOX_TYPE_FTYP : + return isom_print_ftyp; + case ISOM_BOX_TYPE_MOOV : + return isom_print_moov; + case ISOM_BOX_TYPE_MVHD : + return isom_print_mvhd; + case ISOM_BOX_TYPE_IODS : + return isom_print_iods; + case ISOM_BOX_TYPE_ESDS : + return isom_print_esds; + case ISOM_BOX_TYPE_TRAK : + return isom_print_trak; + case ISOM_BOX_TYPE_TKHD : + return isom_print_tkhd; + case QT_BOX_TYPE_TAPT : + return isom_print_tapt; + case QT_BOX_TYPE_CLEF : + return isom_print_clef; + case QT_BOX_TYPE_PROF : + return isom_print_prof; + case QT_BOX_TYPE_ENOF : + return isom_print_enof; + case ISOM_BOX_TYPE_EDTS : + return isom_print_edts; + case ISOM_BOX_TYPE_ELST : + return isom_print_elst; + case ISOM_BOX_TYPE_TREF : + return isom_print_tref; + case ISOM_BOX_TYPE_MDIA : + return isom_print_mdia; + case ISOM_BOX_TYPE_MDHD : + return isom_print_mdhd; + case ISOM_BOX_TYPE_HDLR : + return isom_print_hdlr; + case ISOM_BOX_TYPE_MINF : + return isom_print_minf; + case ISOM_BOX_TYPE_VMHD : + return isom_print_vmhd; + case ISOM_BOX_TYPE_SMHD : + return isom_print_smhd; + case ISOM_BOX_TYPE_HMHD : + return isom_print_hmhd; + case ISOM_BOX_TYPE_NMHD : + return isom_print_nmhd; + case QT_BOX_TYPE_GMHD : + return isom_print_gmhd; + case QT_BOX_TYPE_GMIN : + return isom_print_gmin; + case QT_BOX_TYPE_TEXT : + return isom_print_text; + case ISOM_BOX_TYPE_DINF : + return isom_print_dinf; + case ISOM_BOX_TYPE_DREF : + return isom_print_dref; + case ISOM_BOX_TYPE_URL : + return isom_print_url; + case ISOM_BOX_TYPE_STBL : + return isom_print_stbl; + case ISOM_BOX_TYPE_STSD : + return isom_print_stsd; + case ISOM_BOX_TYPE_BTRT : + return isom_print_btrt; + case ISOM_BOX_TYPE_CLAP : + return isom_print_clap; + case ISOM_BOX_TYPE_PASP : + return isom_print_pasp; + case QT_BOX_TYPE_COLR : + return isom_print_colr; + case ISOM_BOX_TYPE_STSL : + return isom_print_stsl; + case ISOM_BOX_TYPE_AVCC : + return isom_print_avcC; + case QT_BOX_TYPE_WAVE : + return isom_print_wave; + case QT_BOX_TYPE_CHAN : + return isom_print_chan; + case ISOM_BOX_TYPE_FTAB : + return isom_print_ftab; + case ISOM_BOX_TYPE_STTS : + return isom_print_stts; + case ISOM_BOX_TYPE_CTTS : + return isom_print_ctts; + case ISOM_BOX_TYPE_CSLG : + return isom_print_cslg; + case ISOM_BOX_TYPE_STSS : + return isom_print_stss; + case QT_BOX_TYPE_STPS : + return isom_print_stps; + case ISOM_BOX_TYPE_SDTP : + return isom_print_sdtp; + case ISOM_BOX_TYPE_STSC : + return isom_print_stsc; + case ISOM_BOX_TYPE_STSZ : + return isom_print_stsz; + case ISOM_BOX_TYPE_STCO : + case ISOM_BOX_TYPE_CO64 : + return isom_print_stco; + case ISOM_BOX_TYPE_SGPD : + return isom_print_sgpd; + case ISOM_BOX_TYPE_SBGP : + return isom_print_sbgp; + case ISOM_BOX_TYPE_UDTA : + return isom_print_udta; + case ISOM_BOX_TYPE_CHPL : + return isom_print_chpl; + case ISOM_BOX_TYPE_FREE : + case ISOM_BOX_TYPE_SKIP : + return isom_print_free; + case ISOM_BOX_TYPE_MDAT : + return isom_print_mdat; + default : + return isom_print_unknown; + } +} + +static int isom_add_print_func( lsmash_root_t *root, void *box, int level ) +{ + if( !(root->flags & LSMASH_FILE_MODE_DUMP) ) + return 0; + isom_print_entry_t *data = malloc( sizeof(isom_print_entry_t) ); + if( !data ) + return -1; + data->level = level; + data->box = (isom_box_t *)box; + data->func = isom_select_print_func( (isom_box_t *)box ); + if( !data->func || lsmash_add_entry( root->print, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static void isom_remove_print_func( isom_print_entry_t *data ) +{ + if( !data || !data->box ) + return; + if( data->box->manager & 0x02 ) + free( data->box ); /* free flagged box */ + free( data ); +} + +static void isom_remove_print_funcs( lsmash_root_t *root ) +{ + lsmash_remove_list( root->print, isom_remove_print_func ); + root->print = NULL; +} + +static int isom_read_box( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ); + +static int isom_bs_read_box_common( lsmash_bs_t *bs, isom_box_t *box ) +{ + /* read size and type */ + if( lsmash_bs_read_data( bs, ISOM_DEFAULT_BOX_HEADER_SIZE ) ) + return -1; + if( feof( bs->stream ) ) + return 1; + box->size = lsmash_bs_get_be32( bs ); + box->type = lsmash_bs_get_be32( bs ); + if( box->size == 1 ) + { + if( lsmash_bs_read_data( bs, sizeof(uint64_t) ) ) + return -1; + box->size = lsmash_bs_get_be64( bs ); + } + if( !box->size ) + box->size = UINT64_MAX; + /* read version and flags */ + switch( box->type ) + { + case ISOM_FULLBOX_CASE : + if( lsmash_bs_read_data( bs, sizeof(uint32_t) ) ) + return -1; + box->version = lsmash_bs_get_byte( bs ); + box->flags = lsmash_bs_get_be24( bs ); + break; + default : + break; + } + return 0; +} + +static void isom_basebox_common_copy( isom_box_t *dst, isom_box_t *src ) +{ + dst->parent = src->parent; + dst->manager = src->manager; + dst->size = src->size; + dst->type = src->type; + dst->usertype = src->usertype; +} + +static void isom_fullbox_common_copy( isom_box_t *dst, isom_box_t *src ) +{ + dst->parent = src->parent; + dst->manager = src->manager; + dst->size = src->size; + dst->type = src->type; + dst->usertype = src->usertype; + dst->version = src->version; + dst->flags = src->flags; +} + +static void isom_box_common_copy( void *dst, void *src ) +{ + if( src && ((isom_box_t *)src)->type == ISOM_BOX_TYPE_STSD ) + { + isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); + return; + } + switch( ((isom_box_t *)src)->type ) + { + case ISOM_FULLBOX_CASE : + isom_fullbox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); + break; + default : + isom_basebox_common_copy( (isom_box_t *)dst, (isom_box_t *)src ); + break; + } +} + +static void isom_read_box_rest( lsmash_bs_t *bs, isom_box_t *box ) +{ + if( lsmash_bs_read_data( bs, box->size - lsmash_bs_get_pos( bs ) ) ) + return; + if( box->size != bs->store ) /* not match size */ + bs->error = 1; +} + +static void isom_skip_box_rest( lsmash_bs_t *bs, isom_box_t *box ) +{ + fseek( bs->stream, box->size - lsmash_bs_get_pos( bs ), SEEK_CUR ); +} + +static int isom_read_children( lsmash_root_t *root, isom_box_t *box, void *parent, int level ) +{ + int ret; + lsmash_bs_t *bs = root->bs; + isom_box_t *parent_box = (isom_box_t *)parent; + uint64_t parent_pos = lsmash_bs_get_pos( bs ); + while( !(ret = isom_read_box( root, box, parent_box, parent_pos, level )) ) + { + parent_pos += box->size; + if( parent_box->size <= parent_pos || bs->error ) + break; + } + box->size = parent_pos; /* for ROOT size */ + return ret; +} + +static int isom_read_unknown_box( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_box_t *unknown = malloc( sizeof(isom_box_t) ); + if( !unknown ) + return -1; + memset( unknown, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + unknown->parent = parent; + unknown->size = box->size; + unknown->type = box->type; + unknown->manager = 0x03; /* add unknown box flag + free flag */ + if( isom_add_print_func( root, unknown, level ) ) + { + free( unknown ); + return -1; + } + return 0; +} + +static int isom_read_ftyp( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !!parent->type ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( ftyp, parent, box->type ); + ((lsmash_root_t *)parent)->ftyp = ftyp; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + ftyp->major_brand = lsmash_bs_get_be32( bs ); + ftyp->minor_version = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_get_pos( bs ); + ftyp->brand_count = box->size > pos ? (box->size - pos) / sizeof(uint32_t) : 0; + ftyp->compatible_brands = ftyp->brand_count ? malloc( ftyp->brand_count * sizeof(uint32_t) ) : NULL; + if( !ftyp->compatible_brands ) + return -1; + for( uint32_t i = 0; i < ftyp->brand_count; i++ ) + ftyp->compatible_brands[i] = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( ftyp, box ); + return isom_add_print_func( root, ftyp, level ); +} + +static int isom_read_moov( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !!parent->type ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( moov, parent, box->type ); + ((lsmash_root_t *)parent)->moov = moov; + isom_box_common_copy( moov, box ); + if( isom_add_print_func( root, moov, level ) ) + return -1; + return isom_read_children( root, box, moov, level ); +} + +static int isom_read_mvhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MOOV ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( mvhd, parent, box->type ); + ((isom_moov_t *)parent)->mvhd = mvhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + if( box->version ) + { + mvhd->creation_time = lsmash_bs_get_be64( bs ); + mvhd->modification_time = lsmash_bs_get_be64( bs ); + mvhd->timescale = lsmash_bs_get_be32( bs ); + mvhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + mvhd->creation_time = lsmash_bs_get_be32( bs ); + mvhd->modification_time = lsmash_bs_get_be32( bs ); + mvhd->timescale = lsmash_bs_get_be32( bs ); + mvhd->duration = lsmash_bs_get_be32( bs ); + } + mvhd->rate = lsmash_bs_get_be32( bs ); + mvhd->volume = lsmash_bs_get_be16( bs ); + mvhd->reserved = lsmash_bs_get_be16( bs ); + mvhd->preferredLong[0] = lsmash_bs_get_be32( bs ); + mvhd->preferredLong[1] = lsmash_bs_get_be32( bs ); + for( int i = 0; i < 9; i++ ) + mvhd->matrix[i] = lsmash_bs_get_be32( bs ); + mvhd->previewTime = lsmash_bs_get_be32( bs ); + mvhd->previewDuration = lsmash_bs_get_be32( bs ); + mvhd->posterTime = lsmash_bs_get_be32( bs ); + mvhd->selectionTime = lsmash_bs_get_be32( bs ); + mvhd->selectionDuration = lsmash_bs_get_be32( bs ); + mvhd->currentTime = lsmash_bs_get_be32( bs ); + mvhd->next_track_ID = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( mvhd, box ); + return isom_add_print_func( root, mvhd, level ); +} + +static int isom_read_iods( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MOOV ) + return isom_read_unknown_box( root, box, parent, level ); + isom_box_t *iods = malloc( sizeof(isom_box_t) ); + if( !iods ) + return -1; + memset( iods, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + box->manager |= 0x02; /* add free flag */ + isom_box_common_copy( iods, box ); + if( isom_add_print_func( root, iods, level ) ) + { + free( iods ); + return -1; + } + return 0; +} + +static int isom_read_esds( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_box_t *esds = malloc( sizeof(isom_box_t) ); + if( !esds ) + return -1; + memset( esds, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + box->manager |= 0x02; /* add free flag */ + isom_box_common_copy( esds, box ); + if( isom_add_print_func( root, esds, level ) ) + { + free( esds ); + return -1; + } + return 0; +} + +static int isom_read_trak( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MOOV ) + return isom_read_unknown_box( root, box, parent, level ); + lsmash_entry_list_t *list = ((isom_moov_t *)parent)->trak_list; + if( !list ) + { + list = lsmash_create_entry_list(); + if( !list ) + return -1; + } + isom_trak_entry_t *trak = malloc( sizeof(isom_trak_entry_t) ); + if( !trak ) + return -1; + memset( trak, 0, sizeof(isom_trak_entry_t) ); + isom_cache_t *cache = malloc( sizeof(isom_cache_t) ); + if( !cache ) + { + free( trak ); + return -1; + } + memset( cache, 0, sizeof(isom_chunk_t) ); + trak->root = root; + trak->cache = cache; + if( lsmash_add_entry( list, trak ) ) + { + free( trak->cache ); + free( trak ); + return -1; + } + box->parent = parent; + isom_box_common_copy( trak, box ); + if( isom_add_print_func( root, trak, level ) ) + return -1; + return isom_read_children( root, box, trak, level ); +} + +static int isom_read_tkhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( tkhd, parent, box->type ); + ((isom_trak_entry_t *)parent)->tkhd = tkhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + if( box->version ) + { + tkhd->creation_time = lsmash_bs_get_be64( bs ); + tkhd->modification_time = lsmash_bs_get_be64( bs ); + tkhd->track_ID = lsmash_bs_get_be32( bs ); + tkhd->reserved1 = lsmash_bs_get_be32( bs ); + tkhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + tkhd->creation_time = lsmash_bs_get_be32( bs ); + tkhd->modification_time = lsmash_bs_get_be32( bs ); + tkhd->track_ID = lsmash_bs_get_be32( bs ); + tkhd->reserved1 = lsmash_bs_get_be32( bs ); + tkhd->duration = lsmash_bs_get_be32( bs ); + } + tkhd->reserved2[0] = lsmash_bs_get_be32( bs ); + tkhd->reserved2[1] = lsmash_bs_get_be32( bs ); + tkhd->layer = lsmash_bs_get_be16( bs ); + tkhd->alternate_group = lsmash_bs_get_be16( bs ); + tkhd->volume = lsmash_bs_get_be16( bs ); + tkhd->reserved3 = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 9; i++ ) + tkhd->matrix[i] = lsmash_bs_get_be32( bs ); + tkhd->width = lsmash_bs_get_be32( bs ); + tkhd->height = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( tkhd, box ); + return isom_add_print_func( root, tkhd, level ); +} + +static int isom_read_tapt( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( tapt, parent, box->type ); + ((isom_trak_entry_t *)parent)->tapt = tapt; + isom_box_common_copy( tapt, box ); + if( isom_add_print_func( root, tapt, level ) ) + return -1; + return isom_read_children( root, box, tapt, level ); +} + +static int isom_read_clef( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_TAPT ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( clef, parent, box->type ); + ((isom_tapt_t *)parent)->clef = clef; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + clef->width = lsmash_bs_get_be32( bs ); + clef->height = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( clef, box ); + return isom_add_print_func( root, clef, level ); +} + +static int isom_read_prof( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_TAPT ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( prof, parent, box->type ); + ((isom_tapt_t *)parent)->prof = prof; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + prof->width = lsmash_bs_get_be32( bs ); + prof->height = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( prof, box ); + return isom_add_print_func( root, prof, level ); +} + +static int isom_read_enof( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_TAPT ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( enof, parent, box->type ); + ((isom_tapt_t *)parent)->enof = enof; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + enof->width = lsmash_bs_get_be32( bs ); + enof->height = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( enof, box ); + return isom_add_print_func( root, enof, level ); +} + +static int isom_read_edts( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( edts, parent, box->type ); + ((isom_trak_entry_t *)parent)->edts = edts; + isom_box_common_copy( edts, box ); + if( isom_add_print_func( root, edts, level ) ) + return -1; + return isom_read_children( root, box, edts, level ); +} + +static int isom_read_elst( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_EDTS ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( elst, parent, box->type ); + ((isom_edts_t *)parent)->elst = elst; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) ); + if( !data || lsmash_add_entry( elst->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_elst_entry_t) ); + if( box->version == 1 ) + { + data->segment_duration = lsmash_bs_get_be64( bs ); + data->media_time = lsmash_bs_get_be64( bs ); + } + else + { + data->segment_duration = lsmash_bs_get_be32( bs ); + data->media_time = lsmash_bs_get_be32( bs ); + } + data->media_rate = lsmash_bs_get_be32( bs ); + } + if( entry_count != elst->list->entry_count || box->size < pos ) + printf( "[elst] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( elst, box ); + return isom_add_print_func( root, elst, level ); +} + +static int isom_read_tref( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( tref, parent, box->type ); + ((isom_trak_entry_t *)parent)->tref = tref; + isom_box_common_copy( tref, box ); + if( isom_add_print_func( root, tref, level ) ) + return -1; + return isom_read_children( root, box, tref, level ); +} + +static int isom_read_track_reference_type( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TREF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_tref_type_t *ref = NULL; + isom_tref_t *tref = (isom_tref_t *)parent; + if( !tref->ref && !tref->type_count ) + ref = malloc( sizeof(isom_tref_type_t) ); + else + ref = realloc( tref->ref, (tref->type_count + 1) * sizeof(isom_tref_type_t) ); + if( !ref ) + return -1; + tref->ref = ref; + ref += tref->type_count++; /* move the newest ref and update type_count */ + memset( ref, 0, sizeof(isom_tref_type_t) ); + lsmash_bs_t *bs = root->bs; + ref->ref_count = (box->size - lsmash_bs_get_pos( bs ) ) / sizeof(uint32_t); + ref->track_ID = malloc( ref->ref_count * sizeof(uint32_t) ); + if( !ref->track_ID ) + { + free( tref->ref ); + tref->ref = NULL; + return -1; + } + isom_read_box_rest( bs, box ); + for( uint32_t i = 0; i < ref->ref_count; i++ ) + ref->track_ID[i] = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_get_pos( bs ); + if( box->size != pos ) + printf( "[%s] box has extra bytes: %"PRId64"\n", isom_4cc2str( box->type ), box->size - pos ); + box->size = pos; + isom_box_common_copy( ref, box ); + return isom_add_print_func( root, ref, level ); +} + +static int isom_read_mdia( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( mdia, parent, box->type ); + ((isom_trak_entry_t *)parent)->mdia = mdia; + isom_box_common_copy( mdia, box ); + if( isom_add_print_func( root, mdia, level ) ) + return -1; + return isom_read_children( root, box, mdia, level ); +} + +static int isom_read_mdhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MDIA ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( mdhd, parent, box->type ); + ((isom_mdia_t *)parent)->mdhd = mdhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + if( box->version ) + { + mdhd->creation_time = lsmash_bs_get_be64( bs ); + mdhd->modification_time = lsmash_bs_get_be64( bs ); + mdhd->timescale = lsmash_bs_get_be32( bs ); + mdhd->duration = lsmash_bs_get_be64( bs ); + } + else + { + mdhd->creation_time = lsmash_bs_get_be32( bs ); + mdhd->modification_time = lsmash_bs_get_be32( bs ); + mdhd->timescale = lsmash_bs_get_be32( bs ); + mdhd->duration = lsmash_bs_get_be32( bs ); + } + mdhd->language = lsmash_bs_get_be16( bs ); + mdhd->quality = lsmash_bs_get_be16( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( mdhd, box ); + return isom_add_print_func( root, mdhd, level ); +} + +static int isom_read_hdlr( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MDIA && + /*parent->type != ISOM_BOX_TYPE_META &&*/ + parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( hdlr, parent, box->type ); + if( parent->type == ISOM_BOX_TYPE_MDIA ) + ((isom_mdia_t *)parent)->hdlr = hdlr; + else + ((isom_minf_t *)parent)->hdlr = hdlr; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + hdlr->componentType = lsmash_bs_get_be32( bs ); + hdlr->componentSubtype = lsmash_bs_get_be32( bs ); + hdlr->componentManufacturer = lsmash_bs_get_be32( bs ); + hdlr->componentFlags = lsmash_bs_get_be32( bs ); + hdlr->componentFlagsMask = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_get_pos( bs ); + hdlr->componentName_length = box->size - pos; + if( hdlr->componentName_length ) + { + hdlr->componentName = malloc( hdlr->componentName_length ); + if( !hdlr->componentName ) + return -1; + for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + hdlr->componentName[i++] = lsmash_bs_get_byte( bs ); + } + box->size = pos; + isom_box_common_copy( hdlr, box ); + return isom_add_print_func( root, hdlr, level ); +} + +static int isom_read_minf( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MDIA ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( minf, parent, box->type ); + ((isom_mdia_t *)parent)->minf = minf; + isom_box_common_copy( minf, box ); + if( isom_add_print_func( root, minf, level ) ) + return -1; + return isom_read_children( root, box, (isom_box_t *)minf, level ); +} + +static int isom_read_vmhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( vmhd, parent, box->type ); + ((isom_minf_t *)parent)->vmhd = vmhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + vmhd->graphicsmode = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + vmhd->opcolor[i] = lsmash_bs_get_be16( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( vmhd, box ); + return isom_add_print_func( root, vmhd, level ); +} + +static int isom_read_smhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( smhd, parent, box->type ); + ((isom_minf_t *)parent)->smhd = smhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + smhd->balance = lsmash_bs_get_be16( bs ); + smhd->reserved = lsmash_bs_get_be16( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( smhd, box ); + return isom_add_print_func( root, smhd, level ); +} + +static int isom_read_hmhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( hmhd, parent, box->type ); + ((isom_minf_t *)parent)->hmhd = hmhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + hmhd->maxPDUsize = lsmash_bs_get_be16( bs ); + hmhd->avgPDUsize = lsmash_bs_get_be16( bs ); + hmhd->maxbitrate = lsmash_bs_get_be32( bs ); + hmhd->avgbitrate = lsmash_bs_get_be32( bs ); + hmhd->reserved = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( hmhd, box ); + return isom_add_print_func( root, hmhd, level ); +} + +static int isom_read_nmhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( nmhd, parent, box->type ); + ((isom_minf_t *)parent)->nmhd = nmhd; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( nmhd, box ); + return isom_add_print_func( root, nmhd, level ); +} + +static int isom_read_gmhd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( gmhd, parent, box->type ); + ((isom_minf_t *)parent)->gmhd = gmhd; + isom_box_common_copy( gmhd, box ); + if( isom_add_print_func( root, gmhd, level ) ) + return -1; + return isom_read_children( root, box, gmhd, level ); +} + +static int isom_read_gmin( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_GMHD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( gmin, parent, box->type ); + ((isom_gmhd_t *)parent)->gmin = gmin; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + gmin->graphicsmode = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + gmin->opcolor[i] = lsmash_bs_get_be16( bs ); + gmin->balance = lsmash_bs_get_be16( bs ); + gmin->reserved = lsmash_bs_get_be16( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( gmin, box ); + return isom_add_print_func( root, gmin, level ); +} + +static int isom_read_text( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_GMHD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( text, parent, box->type ); + ((isom_gmhd_t *)parent)->text = text; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + for( int i = 0; i < 9; i++ ) + text->matrix[i] = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( text, box ); + return isom_add_print_func( root, text, level ); +} + +static int isom_read_dinf( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( dinf, parent, box->type ); + ((isom_minf_t *)parent)->dinf = dinf; + isom_box_common_copy( dinf, box ); + if( isom_add_print_func( root, dinf, level ) ) + return -1; + return isom_read_children( root, box, (isom_box_t *)dinf, level ); +} + +static int isom_read_dref( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_DINF /*&& parent->type != ISOM_BOX_TYPE_META*/ ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( dref, parent, box->type ); + ((isom_dinf_t *)parent)->dref = dref; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, sizeof(uint32_t) ) ) + return -1; + dref->list->entry_count = lsmash_bs_get_be32( bs ); + isom_box_common_copy( dref, box ); + if( isom_add_print_func( root, dref, level ) ) + return -1; + return isom_read_children( root, box, dref, level ); +} + +static int isom_read_url( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_DREF ) + return isom_read_unknown_box( root, box, parent, level ); + lsmash_entry_list_t *list = ((isom_dref_t *)parent)->list; + if( !list ) + return -1; + isom_dref_entry_t *url = malloc( sizeof(isom_dref_entry_t) ); + if( !url ) + return -1; + memset( url, 0, sizeof(isom_dref_entry_t) ); + if( !list->head ) + list->entry_count = 0; /* discard entry_count gotten from the file */ + if( lsmash_add_entry( list, url ) ) + { + free( url ); + return -1; + } + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint64_t pos = lsmash_bs_get_pos( bs ); + url->location_length = box->size - pos; + if( url->location_length ) + { + url->location = malloc( url->location_length ); + if( !url->location ) + return -1; + for( uint32_t i = 0; pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + url->location[i++] = lsmash_bs_get_byte( bs ); + } + box->size = pos; + box->parent = parent; + isom_box_common_copy( url, box ); + return isom_add_print_func( root, url, level ); +} + +static int isom_read_stbl( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MINF ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( stbl, parent, box->type ); + ((isom_minf_t *)parent)->stbl = stbl; + isom_box_common_copy( stbl, box ); + if( isom_add_print_func( root, stbl, level ) ) + return -1; + return isom_read_children( root, box, stbl, level ); +} + +static int isom_read_stsd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stsd, parent, box->type ); + ((isom_stbl_t *)parent)->stsd = stsd; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, sizeof(uint32_t) ) ) + return -1; + stsd->list->entry_count = lsmash_bs_get_be32( bs ); + isom_box_common_copy( stsd, box ); + if( isom_add_print_func( root, stsd, level ) ) + return -1; + return isom_read_children( root, box, (isom_box_t *)stsd, level ); +} + +static void *isom_sample_description_alloc( uint32_t sample_type ) +{ + 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_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + return malloc( sizeof(isom_avc_entry_t) ); + case ISOM_CODEC_TYPE_MP4V_VIDEO : + return malloc( sizeof(isom_mp4v_entry_t) ); + 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_SVC1_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + return malloc( sizeof(isom_visual_entry_t) ); + case ISOM_CODEC_TYPE_MP4A_AUDIO : + return malloc( sizeof(isom_mp4a_entry_t) ); + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + 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_DTSE_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_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_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 : + case QT_CODEC_TYPE_23NI_AUDIO : + case QT_CODEC_TYPE_MAC3_AUDIO : + case QT_CODEC_TYPE_MAC6_AUDIO : + case QT_CODEC_TYPE_NONE_AUDIO : + case QT_CODEC_TYPE_QDM2_AUDIO : + case QT_CODEC_TYPE_QDMC_AUDIO : + case QT_CODEC_TYPE_QCLP_AUDIO : + case QT_CODEC_TYPE_AGSM_AUDIO : + case QT_CODEC_TYPE_ALAW_AUDIO : + case QT_CODEC_TYPE_CDX2_AUDIO : + case QT_CODEC_TYPE_CDX4_AUDIO : + case QT_CODEC_TYPE_DVCA_AUDIO : + case QT_CODEC_TYPE_DVI_AUDIO : + case QT_CODEC_TYPE_FL32_AUDIO : + case QT_CODEC_TYPE_FL64_AUDIO : + case QT_CODEC_TYPE_IMA4_AUDIO : + case QT_CODEC_TYPE_IN24_AUDIO : + case QT_CODEC_TYPE_IN32_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_ULAW_AUDIO : + case QT_CODEC_TYPE_VDVA_AUDIO : + case QT_CODEC_TYPE_FULLMP3_AUDIO : + case QT_CODEC_TYPE_MP3_AUDIO : + case QT_CODEC_TYPE_ADPCM2_AUDIO : + case QT_CODEC_TYPE_ADPCM17_AUDIO : + case QT_CODEC_TYPE_GSM49_AUDIO : + case QT_CODEC_TYPE_NOT_SPECIFIED : + return malloc( sizeof(isom_audio_entry_t) ); + case ISOM_CODEC_TYPE_TX3G_TEXT : + return malloc( sizeof(isom_tx3g_entry_t) ); + case QT_CODEC_TYPE_TEXT_TEXT : + return malloc( sizeof(isom_text_entry_t) ); + default : + return NULL; + } +} + +static void isom_sample_description_init( void *sample, uint32_t sample_type ) +{ + 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_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + memset( sample, 0, sizeof(isom_avc_entry_t) ); + break; + case ISOM_CODEC_TYPE_MP4V_VIDEO : + memset( sample, 0, sizeof(isom_mp4v_entry_t) ); + break; + 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_SVC1_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + memset( sample, 0, sizeof(isom_visual_entry_t) ); + break; + case ISOM_CODEC_TYPE_MP4A_AUDIO : + memset( sample, 0, sizeof(isom_mp4a_entry_t) ); + break; + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + 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_DTSE_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_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_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 : + case QT_CODEC_TYPE_23NI_AUDIO : + case QT_CODEC_TYPE_MAC3_AUDIO : + case QT_CODEC_TYPE_MAC6_AUDIO : + case QT_CODEC_TYPE_NONE_AUDIO : + case QT_CODEC_TYPE_QDM2_AUDIO : + case QT_CODEC_TYPE_QDMC_AUDIO : + case QT_CODEC_TYPE_QCLP_AUDIO : + case QT_CODEC_TYPE_AGSM_AUDIO : + case QT_CODEC_TYPE_ALAW_AUDIO : + case QT_CODEC_TYPE_CDX2_AUDIO : + case QT_CODEC_TYPE_CDX4_AUDIO : + case QT_CODEC_TYPE_DVCA_AUDIO : + case QT_CODEC_TYPE_DVI_AUDIO : + case QT_CODEC_TYPE_FL32_AUDIO : + case QT_CODEC_TYPE_FL64_AUDIO : + case QT_CODEC_TYPE_IMA4_AUDIO : + case QT_CODEC_TYPE_IN24_AUDIO : + case QT_CODEC_TYPE_IN32_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_ULAW_AUDIO : + case QT_CODEC_TYPE_VDVA_AUDIO : + case QT_CODEC_TYPE_FULLMP3_AUDIO : + case QT_CODEC_TYPE_MP3_AUDIO : + case QT_CODEC_TYPE_ADPCM2_AUDIO : + case QT_CODEC_TYPE_ADPCM17_AUDIO : + case QT_CODEC_TYPE_GSM49_AUDIO : + case QT_CODEC_TYPE_NOT_SPECIFIED : + memset( sample, 0, sizeof(isom_audio_entry_t) ); + break; + case ISOM_CODEC_TYPE_TX3G_TEXT : + memset( sample, 0, sizeof(isom_tx3g_entry_t) ); + break; + case QT_CODEC_TYPE_TEXT_TEXT : + memset( sample, 0, sizeof(isom_text_entry_t) ); + break; + default : + break; + } +} + +static void *isom_add_description( uint32_t sample_type, lsmash_entry_list_t *list ) +{ + if( !list ) + return NULL; + void *sample = isom_sample_description_alloc( sample_type ); + if( !sample ) + return NULL; + isom_sample_description_init( sample, sample_type ); + if( !list->head ) + list->entry_count = 0; /* discard entry_count gotten from the file */ + if( lsmash_add_entry( list, sample ) ) + { + free( sample ); + return NULL; + } + return sample; +} + +static int isom_read_visual_description( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STSD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_visual_entry_t *visual = (isom_visual_entry_t *)isom_add_description( box->type, ((isom_stsd_t *)parent)->list ); + if( !visual ) + return -1; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, 78 ) ) + return -1; + for( int i = 0; i < 6; i++ ) + visual->reserved[i] = lsmash_bs_get_byte( bs ); + visual->data_reference_index = lsmash_bs_get_be16( bs ); + visual->version = lsmash_bs_get_be16( bs ); + visual->revision_level = lsmash_bs_get_be16( bs ); + visual->vendor = lsmash_bs_get_be32( bs ); + visual->temporalQuality = lsmash_bs_get_be32( bs ); + visual->spatialQuality = lsmash_bs_get_be32( bs ); + visual->width = lsmash_bs_get_be16( bs ); + visual->height = lsmash_bs_get_be16( bs ); + visual->horizresolution = lsmash_bs_get_be32( bs ); + visual->vertresolution = lsmash_bs_get_be32( bs ); + visual->dataSize = lsmash_bs_get_be32( bs ); + visual->frame_count = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 32; i++ ) + visual->compressorname[i] = lsmash_bs_get_byte( bs ); + visual->depth = lsmash_bs_get_be16( bs ); + visual->color_table_ID = lsmash_bs_get_be16( bs ); + box->parent = parent; + isom_box_common_copy( visual, box ); + if( isom_add_print_func( root, visual, level ) ) + return -1; + return isom_read_children( root, box, visual, level ); +} + +static int isom_read_btrt( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( btrt, parent, box->type ); + ((isom_avc_entry_t *)parent)->btrt = btrt; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + btrt->bufferSizeDB = lsmash_bs_get_be32( bs ); + btrt->maxBitrate = lsmash_bs_get_be32( bs ); + btrt->avgBitrate = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( btrt, box ); + return isom_add_print_func( root, btrt, level ); +} + +static int isom_read_clap( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( clap, parent, box->type ); + ((isom_visual_entry_t *)parent)->clap = clap; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + clap->cleanApertureWidthN = lsmash_bs_get_be32( bs ); + clap->cleanApertureWidthD = lsmash_bs_get_be32( bs ); + clap->cleanApertureHeightN = lsmash_bs_get_be32( bs ); + clap->cleanApertureHeightD = lsmash_bs_get_be32( bs ); + clap->horizOffN = lsmash_bs_get_be32( bs ); + clap->horizOffD = lsmash_bs_get_be32( bs ); + clap->vertOffN = lsmash_bs_get_be32( bs ); + clap->vertOffD = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( clap, box ); + return isom_add_print_func( root, clap, level ); +} + +static int isom_read_pasp( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( pasp, parent, box->type ); + ((isom_visual_entry_t *)parent)->pasp = pasp; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + pasp->hSpacing = lsmash_bs_get_be32( bs ); + pasp->vSpacing = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( pasp, box ); + return isom_add_print_func( root, pasp, level ); +} + +static int isom_read_colr( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( colr, parent, box->type ); + ((isom_visual_entry_t *)parent)->colr = colr; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + colr->color_parameter_type = lsmash_bs_get_be32( bs ); + if( colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_NCLC ) + { + colr->primaries_index = lsmash_bs_get_be16( bs ); + colr->transfer_function_index = lsmash_bs_get_be16( bs ); + colr->matrix_index = lsmash_bs_get_be16( bs ); + } + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( colr, box ); + return isom_add_print_func( root, colr, level ); +} + +static int isom_read_stsl( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( stsl, parent, box->type ); + ((isom_visual_entry_t *)parent)->stsl = stsl; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + stsl->constraint_flag = lsmash_bs_get_byte( bs ); + stsl->scale_method = lsmash_bs_get_byte( bs ); + stsl->display_center_x = lsmash_bs_get_be16( bs ); + stsl->display_center_y = lsmash_bs_get_be16( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( stsl, box ); + return isom_add_print_func( root, stsl, level ); +} + +static int isom_read_avcC_ps( lsmash_bs_t *bs, lsmash_entry_list_t *list, uint8_t entry_count ) +{ + if( !list ) + return -1; + for( uint8_t i = 0; i < entry_count; i++ ) + { + isom_avcC_ps_entry_t *data = malloc( sizeof(isom_avcC_ps_entry_t) ); + if( !data || lsmash_add_entry( list, data ) ) + return -1; /* don't free list, here */ + data->parameterSetLength = lsmash_bs_get_be16( bs ); + data->parameterSetNALUnit = lsmash_bs_get_bytes( bs, data->parameterSetLength ); + if( !data->parameterSetNALUnit ) + return -1; /* don't free list, here */ + } + return 0; +} + +static int isom_read_avcC( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( avcC, parent, box->type ); + isom_avc_entry_t *avc = (isom_avc_entry_t *)parent; + avc->avcC = avcC; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + avcC->configurationVersion = lsmash_bs_get_byte( bs ); + avcC->AVCProfileIndication = lsmash_bs_get_byte( bs ); + avcC->profile_compatibility = lsmash_bs_get_byte( bs ); + avcC->AVCLevelIndication = lsmash_bs_get_byte( bs ); + avcC->lengthSizeMinusOne = lsmash_bs_get_byte( bs ); + avcC->numOfSequenceParameterSets = lsmash_bs_get_byte( bs ); + if( avcC->numOfSequenceParameterSets & 0x1f ) + { + avcC->sequenceParameterSets = lsmash_create_entry_list(); + if( !avcC->sequenceParameterSets || + isom_read_avcC_ps( bs, avcC->sequenceParameterSets, avcC->numOfSequenceParameterSets & 0x1f ) ) + goto fail; + } + avcC->numOfPictureParameterSets = lsmash_bs_get_byte( bs ); + if( avcC->numOfPictureParameterSets ) + { + avcC->pictureParameterSets = lsmash_create_entry_list(); + if( !avcC->pictureParameterSets || + isom_read_avcC_ps( bs, avcC->pictureParameterSets, avcC->numOfPictureParameterSets ) ) + goto fail; + } + /* Note: there are too many files, in the world, that don't contain the following fields.*/ + if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) && lsmash_bs_get_pos( bs ) < box->size ) + { + avcC->chroma_format = lsmash_bs_get_byte( bs ); + avcC->bit_depth_luma_minus8 = lsmash_bs_get_byte( bs ); + avcC->bit_depth_chroma_minus8 = lsmash_bs_get_byte( bs ); + avcC->numOfSequenceParameterSetExt = lsmash_bs_get_byte( bs ); + if( avcC->numOfSequenceParameterSetExt ) + { + avcC->sequenceParameterSetExt = lsmash_create_entry_list(); + if( !avcC->sequenceParameterSetExt || + isom_read_avcC_ps( bs, avcC->sequenceParameterSetExt, avcC->numOfSequenceParameterSetExt ) ) + goto fail; + } + } + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( avcC, box ); + return isom_add_print_func( root, avcC, level ); +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 ); + free( avcC ); + avc->avcC = NULL; + return -1; +} + +static int isom_read_audio_description( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STSD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_audio_entry_t *audio = (isom_audio_entry_t *)isom_add_description( box->type, ((isom_stsd_t *)parent)->list ); + if( !audio ) + return -1; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, 28 ) ) + return -1; + for( int i = 0; i < 6; i++ ) + audio->reserved[i] = lsmash_bs_get_byte( bs ); + audio->data_reference_index = lsmash_bs_get_be16( bs ); + audio->version = lsmash_bs_get_be16( bs ); + audio->revision_level = lsmash_bs_get_be16( bs ); + audio->vendor = lsmash_bs_get_be32( bs ); + audio->channelcount = lsmash_bs_get_be16( bs ); + audio->samplesize = lsmash_bs_get_be16( bs ); + audio->compression_ID = lsmash_bs_get_be16( bs ); + audio->packet_size = lsmash_bs_get_be16( bs ); + audio->samplerate = lsmash_bs_get_be32( bs ); + if( audio->version == 1 ) + { + if( lsmash_bs_read_data( bs, 16 ) ) + return -1; + audio->samplesPerPacket = lsmash_bs_get_be32( bs ); + audio->bytesPerPacket = lsmash_bs_get_be32( bs ); + audio->bytesPerFrame = lsmash_bs_get_be32( bs ); + audio->bytesPerSample = lsmash_bs_get_be32( bs ); + } + else if( audio->version == 2 ) + { + if( lsmash_bs_read_data( bs, 36 ) ) + return -1; + audio->sizeOfStructOnly = lsmash_bs_get_be32( bs ); + audio->audioSampleRate = lsmash_bs_get_be64( bs ); + audio->numAudioChannels = lsmash_bs_get_be32( bs ); + audio->always7F000000 = lsmash_bs_get_be32( bs ); + audio->constBitsPerChannel = lsmash_bs_get_be32( bs ); + audio->formatSpecificFlags = lsmash_bs_get_be32( bs ); + audio->constBytesPerAudioPacket = lsmash_bs_get_be32( bs ); + audio->constLPCMFramesPerAudioPacket = lsmash_bs_get_be32( bs ); + } + box->parent = parent; + isom_box_common_copy( audio, box ); + if( isom_add_print_func( root, audio, level ) ) + return -1; + return isom_read_children( root, box, audio, level ); +} + +static int isom_read_wave( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( wave, parent, box->type ); + ((isom_audio_entry_t *)parent)->wave = wave; + isom_box_common_copy( wave, box ); + if( isom_add_print_func( root, wave, level ) ) + return -1; + return isom_read_children( root, box, wave, level ); +} + +static int isom_read_frma( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_WAVE ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( frma, parent, box->type ); + ((isom_wave_t *)parent)->frma = frma; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + frma->data_format = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( frma, box ); + return isom_add_print_func( root, frma, level ); +} + +static int isom_read_audio_specific( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_WAVE ) + return isom_read_unknown_box( root, box, parent, level ); + isom_box_t *specific = malloc( sizeof(isom_box_t) ); + if( !specific ) + return -1; + memset( specific, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + box->manager |= 0x02; /* add free flag */ + isom_box_common_copy( specific, box ); + if( isom_add_print_func( root, specific, level ) ) + { + free( specific ); + return -1; + } + return 0; +} + +static int isom_read_terminator( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != QT_BOX_TYPE_WAVE ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( terminator, parent, box->type ); + ((isom_wave_t *)parent)->terminator = terminator; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( terminator, box ); + return isom_add_print_func( root, terminator, level ); +} + +static int isom_read_chan( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_create_box( chan, parent, box->type ); + isom_audio_entry_t *audio = (isom_audio_entry_t *)parent; + audio->chan = chan; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + chan->channelLayoutTag = lsmash_bs_get_be32( bs ); + chan->channelBitmap = lsmash_bs_get_be32( bs ); + chan->numberChannelDescriptions = lsmash_bs_get_be32( bs ); + if( chan->numberChannelDescriptions ) + { + isom_channel_description_t *desc = malloc( chan->numberChannelDescriptions * sizeof(isom_channel_description_t) ); + if( !desc ) + { + free( chan ); + audio->chan = NULL; + return -1; + } + chan->channelDescriptions = desc; + for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) + { + desc->channelLabel = lsmash_bs_get_be32( bs ); + desc->channelFlags = lsmash_bs_get_be32( bs ); + for( int j = 0; j < 3; j++ ) + desc->coordinates[j] = lsmash_bs_get_be32( bs ); + } + } + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( chan, box ); + return isom_add_print_func( root, chan, level ); +} + +static int isom_read_text_description( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STSD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_text_entry_t *text = (isom_text_entry_t *)isom_add_description( box->type, ((isom_stsd_t *)parent)->list ); + if( !text ) + return -1; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, 51 ) ) + return -1; + for( int i = 0; i < 6; i++ ) + text->reserved[i] = lsmash_bs_get_byte( bs ); + text->data_reference_index = lsmash_bs_get_be16( bs ); + text->displayFlags = lsmash_bs_get_be32( bs ); + text->textJustification = lsmash_bs_get_be32( bs ); + for( int i = 0; i < 3; i++ ) + text->bgColor[i] = lsmash_bs_get_be16( bs ); + text->top = lsmash_bs_get_be16( bs ); + text->left = lsmash_bs_get_be16( bs ); + text->bottom = lsmash_bs_get_be16( bs ); + text->right = lsmash_bs_get_be16( bs ); + text->scrpStartChar = lsmash_bs_get_be32( bs ); + text->scrpHeight = lsmash_bs_get_be16( bs ); + text->scrpAscent = lsmash_bs_get_be16( bs ); + text->scrpFont = lsmash_bs_get_be16( bs ); + text->scrpFace = lsmash_bs_get_be16( bs ); + text->scrpSize = lsmash_bs_get_be16( bs ); + for( int i = 0; i < 3; i++ ) + text->scrpColor[i] = lsmash_bs_get_be16( bs ); + text->font_name_length = lsmash_bs_get_byte( bs ); + if( text->font_name_length ) + { + if( lsmash_bs_read_data( bs, text->font_name_length ) ) + return -1; + text->font_name = malloc( text->font_name_length + 1 ); + if( !text->font_name ) + return -1; + for( uint8_t i = 0; i < text->font_name_length; i++ ) + text->font_name[i] = lsmash_bs_get_byte( bs ); + text->font_name[text->font_name_length] = '\0'; + } + box->parent = parent; + isom_box_common_copy( text, box ); + if( isom_add_print_func( root, text, level ) ) + return -1; + return isom_read_children( root, box, text, level ); +} + +static int isom_read_tx3g_description( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STSD ) + return isom_read_unknown_box( root, box, parent, level ); + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)isom_add_description( box->type, ((isom_stsd_t *)parent)->list ); + if( !tx3g ) + return -1; + lsmash_bs_t *bs = root->bs; + if( lsmash_bs_read_data( bs, 38 ) ) + return -1; + for( int i = 0; i < 6; i++ ) + tx3g->reserved[i] = lsmash_bs_get_byte( bs ); + tx3g->data_reference_index = lsmash_bs_get_be16( bs ); + tx3g->displayFlags = lsmash_bs_get_be32( bs ); + tx3g->horizontal_justification = lsmash_bs_get_byte( bs ); + tx3g->vertical_justification = lsmash_bs_get_byte( bs ); + for( int i = 0; i < 4; i++ ) + tx3g->background_color_rgba[i] = lsmash_bs_get_byte( bs ); + tx3g->top = lsmash_bs_get_be16( bs ); + tx3g->left = lsmash_bs_get_be16( bs ); + tx3g->bottom = lsmash_bs_get_be16( bs ); + tx3g->right = lsmash_bs_get_be16( bs ); + tx3g->startChar = lsmash_bs_get_be16( bs ); + tx3g->endChar = lsmash_bs_get_be16( bs ); + tx3g->font_ID = lsmash_bs_get_be16( bs ); + tx3g->face_style_flags = lsmash_bs_get_byte( bs ); + tx3g->font_size = lsmash_bs_get_byte( bs ); + for( int i = 0; i < 4; i++ ) + tx3g->text_color_rgba[i] = lsmash_bs_get_byte( bs ); + box->parent = parent; + isom_box_common_copy( tx3g, box ); + if( isom_add_print_func( root, tx3g, level ) ) + return -1; + return isom_read_children( root, box, tx3g, level ); +} + +static int isom_read_ftab( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_CODEC_TYPE_TX3G_TEXT ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( ftab, parent, box->type ); + ((isom_tx3g_entry_t *)parent)->ftab = ftab; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be16( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_font_record_t *data = malloc( sizeof(isom_font_record_t) ); + if( !data || lsmash_add_entry( ftab->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_font_record_t) ); + data->font_ID = lsmash_bs_get_be16( bs ); + data->font_name_length = lsmash_bs_get_byte( bs ); + if( data->font_name_length ) + { + data->font_name = malloc( data->font_name_length + 1 ); + if( !data->font_name ) + return -1; + for( uint8_t i = 0; i < data->font_name_length; i++ ) + data->font_name[i] = lsmash_bs_get_byte( bs ); + data->font_name[data->font_name_length] = '\0'; + } + } + if( entry_count != ftab->list->entry_count || box->size < pos ) + printf( "[ftab] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( ftab, box ); + return isom_add_print_func( root, ftab, level ); +} + +static int isom_read_stts( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stts, parent, box->type ); + ((isom_stbl_t *)parent)->stts = stts; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) ); + if( !data || lsmash_add_entry( stts->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stts_entry_t) ); + data->sample_count = lsmash_bs_get_be32( bs ); + data->sample_delta = lsmash_bs_get_be32( bs ); + } + if( entry_count != stts->list->entry_count || box->size < pos ) + printf( "[stts] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( stts, box ); + return isom_add_print_func( root, stts, level ); +} + +static int isom_read_ctts( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( ctts, parent, box->type ); + ((isom_stbl_t *)parent)->ctts = ctts; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) ); + if( !data || lsmash_add_entry( ctts->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_ctts_entry_t) ); + data->sample_count = lsmash_bs_get_be32( bs ); + data->sample_offset = lsmash_bs_get_be32( bs ); + } + if( entry_count != ctts->list->entry_count || box->size < pos ) + printf( "[ctts] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( ctts, box ); + return isom_add_print_func( root, ctts, level ); +} + +static int isom_read_cslg( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( cslg, parent, box->type ); + ((isom_stbl_t *)parent)->cslg = cslg; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + cslg->compositionToDTSShift = lsmash_bs_get_be32( bs ); + cslg->leastDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); + cslg->greatestDecodeToDisplayDelta = lsmash_bs_get_be32( bs ); + cslg->compositionStartTime = lsmash_bs_get_be32( bs ); + cslg->compositionEndTime = lsmash_bs_get_be32( bs ); + box->size = lsmash_bs_get_pos( bs ); + isom_box_common_copy( cslg, box ); + return isom_add_print_func( root, cslg, level ); +} + +static int isom_read_stss( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stss, parent, box->type ); + ((isom_stbl_t *)parent)->stss = stss; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) ); + if( !data || lsmash_add_entry( stss->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stss_entry_t) ); + data->sample_number = lsmash_bs_get_be32( bs ); + } + if( entry_count != stss->list->entry_count || box->size < pos ) + printf( "[stss] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( stss, box ); + return isom_add_print_func( root, stss, level ); +} + +static int isom_read_stps( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stps, parent, box->type ); + ((isom_stbl_t *)parent)->stps = stps; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) ); + if( !data || lsmash_add_entry( stps->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stps_entry_t) ); + data->sample_number = lsmash_bs_get_be32( bs ); + } + if( entry_count != stps->list->entry_count || box->size < pos ) + printf( "[stps] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( stps, box ); + return isom_add_print_func( root, stps, level ); +} + +static int isom_read_sdtp( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( sdtp, parent, box->type ); + ((isom_stbl_t *)parent)->sdtp = sdtp; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) ); + if( !data || lsmash_add_entry( sdtp->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_sdtp_entry_t) ); + uint8_t temp = lsmash_bs_get_byte( bs ); + data->is_leading = (temp >> 6) & 0x3; + data->sample_depends_on = (temp >> 4) & 0x3; + data->sample_is_depended_on = (temp >> 2) & 0x3; + data->sample_has_redundancy = temp & 0x3; + } + box->size = pos; + isom_box_common_copy( sdtp, box ); + return isom_add_print_func( root, sdtp, level ); +} + +static int isom_read_stsc( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stsc, parent, box->type ); + ((isom_stbl_t *)parent)->stsc = stsc; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) ); + if( !data || lsmash_add_entry( stsc->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stsc_entry_t) ); + data->first_chunk = lsmash_bs_get_be32( bs ); + data->samples_per_chunk = lsmash_bs_get_be32( bs ); + data->sample_description_index = lsmash_bs_get_be32( bs ); + } + if( entry_count != stsc->list->entry_count || box->size < pos ) + printf( "[stsc] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( stsc, box ); + return isom_add_print_func( root, stsc, level ); +} + +static int isom_read_stsz( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( stsz, parent, box->type ); + ((isom_stbl_t *)parent)->stsz = stsz; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + stsz->sample_size = lsmash_bs_get_be32( bs ); + stsz->sample_count = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_get_pos( bs ); + if( pos < box->size ) + { + stsz->list = lsmash_create_entry_list(); + if( !stsz->list ) + return -1; + for( ; pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) ); + if( !data || lsmash_add_entry( stsz->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stsz_entry_t) ); + data->entry_size = lsmash_bs_get_be32( bs ); + } + } + if( (stsz->list && stsz->sample_count != stsz->list->entry_count) || box->size < pos ) + printf( "[stsz] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( stsz, box ); + return isom_add_print_func( root, stsz, level ); +} + +static int isom_read_stco( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( stco, parent, box->type ); + ((isom_stbl_t *)parent)->stco = stco; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + if( box->type == ISOM_BOX_TYPE_STCO ) + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) ); + if( !data || lsmash_add_entry( stco->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_stco_entry_t) ); + data->chunk_offset = lsmash_bs_get_be32( bs ); + } + else + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) ); + if( !data || lsmash_add_entry( stco->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_co64_entry_t) ); + data->chunk_offset = lsmash_bs_get_be64( bs ); + } + if( entry_count != stco->list->entry_count || box->size < pos ) + printf( "[%s] box has extra bytes: %"PRId64"\n", isom_4cc2str( box->type ), pos - box->size ); + box->size = pos; + isom_box_common_copy( stco, box ); + return isom_add_print_func( root, stco, level ); +} + +static int isom_read_sgpd( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + + isom_sgpd_t *sgpd = NULL; + isom_stbl_t *stbl = (isom_stbl_t *)parent; + if( !stbl->sgpd && !stbl->sgpd_count ) + sgpd = malloc( sizeof(isom_sgpd_t) ); + else + sgpd = realloc( stbl->sgpd, (stbl->sgpd_count + 1) * sizeof(isom_sgpd_t) ); + if( !sgpd ) + return -1; + stbl->sgpd = sgpd; + sgpd += stbl->sgpd_count++; /* move the newest sgpd and update sgpd_count */ + memset( sgpd, 0, sizeof(isom_sgpd_t) ); + sgpd->list = lsmash_create_entry_list(); + if( !sgpd->list ) + { + free( stbl->sgpd ); + stbl->sgpd = NULL; + return -1; + } + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + sgpd->grouping_type = lsmash_bs_get_be32( bs ); + if( box->version == 1 ) + sgpd->default_length = lsmash_bs_get_be32( bs ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + if( sgpd->grouping_type == ISOM_GROUP_TYPE_ROLL ) + { + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) ); + if( !data || lsmash_add_entry( sgpd->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_roll_entry_t) ); + /* We don't know roll_recovery decided by variable description length. If encountering, skip getting of bytes of it. */ + if( box->version == 1 && !sgpd->default_length ) + data->description_length = lsmash_bs_get_be32( bs ); + else + data->roll_distance = lsmash_bs_get_be16( bs ); + } + if( entry_count != sgpd->list->entry_count || box->size < pos ) + printf( "[sgpd] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + } + isom_box_common_copy( sgpd, box ); + return isom_add_print_func( root, sgpd, level ); +} + +static int isom_read_sbgp( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_STBL ) + return isom_read_unknown_box( root, box, parent, level ); + + isom_sbgp_t *sbgp = NULL; + isom_stbl_t *stbl = (isom_stbl_t *)parent; + if( !stbl->sbgp && !stbl->sbgp_count ) + sbgp = malloc( sizeof(isom_sbgp_t) ); + else + sbgp = realloc( stbl->sbgp, (stbl->sbgp_count + 1) * sizeof(isom_sbgp_t) ); + if( !sbgp ) + return -1; + stbl->sbgp = sbgp; + sbgp += stbl->sbgp_count++; /* move the newest sbgp and update sbgp_count */ + memset( sbgp, 0, sizeof(isom_sbgp_t) ); + sbgp->list = lsmash_create_entry_list(); + if( !sbgp->list ) + { + free( stbl->sbgp ); + stbl->sbgp = NULL; + return -1; + } + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + sbgp->grouping_type = lsmash_bs_get_be32( bs ); + if( sbgp->version == 1 ) + sbgp->grouping_type_parameter = lsmash_bs_get_be32( bs ); + uint32_t entry_count = lsmash_bs_get_be32( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_sbgp_entry_t *data = malloc( sizeof(isom_sbgp_entry_t) ); + if( !data || lsmash_add_entry( sbgp->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_sbgp_entry_t) ); + data->sample_count = lsmash_bs_get_be32( bs ); + data->group_description_index = lsmash_bs_get_be32( bs ); + } + if( entry_count != sbgp->list->entry_count || box->size < pos ) + printf( "[sbgp] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( sbgp, box ); + return isom_add_print_func( root, sbgp, level ); +} + +static int isom_read_udta( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_MOOV && parent->type != ISOM_BOX_TYPE_TRAK ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_box( udta, parent, box->type ); + if( parent->type == ISOM_BOX_TYPE_MOOV ) + ((isom_moov_t *)parent)->udta = udta; + else + ((isom_trak_entry_t *)parent)->udta = udta; + isom_box_common_copy( udta, box ); + if( isom_add_print_func( root, udta, level ) ) + return -1; + return isom_read_children( root, box, udta, level ); +} + +static int isom_read_chpl( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( parent->type != ISOM_BOX_TYPE_UDTA ) + return isom_read_unknown_box( root, box, parent, level ); + isom_create_list_box( chpl, parent, box->type ); + ((isom_udta_t *)parent)->chpl = chpl; + lsmash_bs_t *bs = root->bs; + isom_read_box_rest( bs, box ); + uint32_t entry_count; + if( box->version == 1 ) + { + chpl->unknown = lsmash_bs_get_byte( bs ); + entry_count = lsmash_bs_get_be32( bs ); + } + else + entry_count = lsmash_bs_get_byte( bs ); + uint64_t pos; + for( pos = lsmash_bs_get_pos( bs ); pos < box->size; pos = lsmash_bs_get_pos( bs ) ) + { + isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) ); + if( !data || lsmash_add_entry( chpl->list, data ) ) + { + if( data ) + free( data ); + return -1; + } + memset( data, 0, sizeof(isom_chpl_entry_t) ); + data->start_time = lsmash_bs_get_be64( bs ); + data->chapter_name_length = lsmash_bs_get_byte( bs ); + data->chapter_name = malloc( data->chapter_name_length + 1 ); + if( !data->chapter_name ) + { + free( data ); + return -1; + } + for( uint8_t i = 0; i < data->chapter_name_length; i++ ) + data->chapter_name[i] = lsmash_bs_get_byte( bs ); + data->chapter_name[data->chapter_name_length] = '\0'; + } + if( entry_count != chpl->list->entry_count || box->size < pos ) + printf( "[chpl] box has extra bytes: %"PRId64"\n", pos - box->size ); + box->size = pos; + isom_box_common_copy( chpl, box ); + return isom_add_print_func( root, chpl, level ); +} + +static int isom_read_free( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + isom_box_t *skip = malloc( sizeof(isom_box_t) ); + if( !skip ) + return -1; + memset( skip, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + box->manager |= 0x02; /* add free flag */ + isom_box_common_copy( skip, box ); + if( isom_add_print_func( root, skip, level ) ) + { + free( skip ); + return -1; + } + return 0; +} + +static int isom_read_mdat( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !!parent->type ) + return isom_read_unknown_box( root, box, parent, level ); + isom_box_t *mdat = malloc( sizeof(isom_box_t) ); + if( !mdat ) + return -1; + memset( mdat, 0, sizeof(isom_box_t) ); + lsmash_bs_t *bs = root->bs; + isom_skip_box_rest( bs, box ); + box->manager |= 0x02; /* add free flag */ + isom_box_common_copy( mdat, box ); + if( isom_add_print_func( root, mdat, level ) ) + { + free( mdat ); + return -1; + } + return 0; +} + +static int isom_read_box( lsmash_root_t *root, isom_box_t *box, isom_box_t *parent, uint64_t parent_pos, int level ) +{ + lsmash_bs_t *bs = root->bs; + memset( box, 0, sizeof(isom_box_t) ); + if( !parent ) + return -1; + box->parent = parent; + if( parent->size < parent_pos + ISOM_DEFAULT_BOX_HEADER_SIZE ) + { + /* skip extra bytes */ + uint64_t rest_size = parent->size - parent_pos; + fseek( bs->stream, rest_size, SEEK_CUR ); + box->size = rest_size; + return 0; + } + lsmash_bs_empty( bs ); + int ret = -1; + if( !!(ret = isom_bs_read_box_common( bs, box )) ) + return ret; /* return if reached EOF */ + ++level; + if( parent->type == ISOM_BOX_TYPE_STSD ) + switch( box->type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_MP4V_VIDEO : + case ISOM_CODEC_TYPE_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_SVC1_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + return isom_read_visual_description( root, box, parent, level ); + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + 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_DTSE_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_MP4A_AUDIO : + //case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_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 : + case QT_CODEC_TYPE_23NI_AUDIO : + case QT_CODEC_TYPE_MAC3_AUDIO : + case QT_CODEC_TYPE_MAC6_AUDIO : + case QT_CODEC_TYPE_NONE_AUDIO : + case QT_CODEC_TYPE_QDM2_AUDIO : + case QT_CODEC_TYPE_QDMC_AUDIO : + case QT_CODEC_TYPE_QCLP_AUDIO : + case QT_CODEC_TYPE_AGSM_AUDIO : + case QT_CODEC_TYPE_ALAW_AUDIO : + case QT_CODEC_TYPE_CDX2_AUDIO : + case QT_CODEC_TYPE_CDX4_AUDIO : + case QT_CODEC_TYPE_DVCA_AUDIO : + case QT_CODEC_TYPE_DVI_AUDIO : + case QT_CODEC_TYPE_FL32_AUDIO : + case QT_CODEC_TYPE_FL64_AUDIO : + case QT_CODEC_TYPE_IMA4_AUDIO : + case QT_CODEC_TYPE_IN24_AUDIO : + case QT_CODEC_TYPE_IN32_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_ULAW_AUDIO : + case QT_CODEC_TYPE_VDVA_AUDIO : + case QT_CODEC_TYPE_FULLMP3_AUDIO : + case QT_CODEC_TYPE_MP3_AUDIO : + case QT_CODEC_TYPE_ADPCM2_AUDIO : + case QT_CODEC_TYPE_ADPCM17_AUDIO : + case QT_CODEC_TYPE_GSM49_AUDIO : + case QT_CODEC_TYPE_NOT_SPECIFIED : + return isom_read_audio_description( root, box, parent, level ); + case QT_CODEC_TYPE_TEXT_TEXT : + return isom_read_text_description( root, box, parent, level ); + case ISOM_CODEC_TYPE_TX3G_TEXT : + return isom_read_tx3g_description( root, box, parent, level ); + default : + return isom_read_unknown_box( root, box, parent, level ); + } + if( parent->type == QT_BOX_TYPE_WAVE ) + switch( box->type ) + { + case QT_BOX_TYPE_FRMA : + return isom_read_frma( root, box, parent, level ); + case ISOM_BOX_TYPE_ESDS : + return isom_read_esds( root, box, parent, level ); + case QT_BOX_TYPE_TERMINATOR : + return isom_read_terminator( root, box, parent, level ); + default : + return isom_read_audio_specific( root, box, parent, level ); + } + if( parent->type == ISOM_BOX_TYPE_TREF ) + return isom_read_track_reference_type( root, box, parent, level ); + switch( box->type ) + { + case ISOM_BOX_TYPE_FTYP : + return isom_read_ftyp( root, box, parent, level ); + case ISOM_BOX_TYPE_MOOV : + return isom_read_moov( root, box, parent, level ); + case ISOM_BOX_TYPE_MVHD : + return isom_read_mvhd( root, box, parent, level ); + case ISOM_BOX_TYPE_IODS : + return isom_read_iods( root, box, parent, level ); + case ISOM_BOX_TYPE_ESDS : + return isom_read_esds( root, box, parent, level ); + case ISOM_BOX_TYPE_TRAK : + return isom_read_trak( root, box, parent, level ); + case ISOM_BOX_TYPE_TKHD : + return isom_read_tkhd( root, box, parent, level ); + case QT_BOX_TYPE_TAPT : + return isom_read_tapt( root, box, parent, level ); + case QT_BOX_TYPE_CLEF : + return isom_read_clef( root, box, parent, level ); + case QT_BOX_TYPE_PROF : + return isom_read_prof( root, box, parent, level ); + case QT_BOX_TYPE_ENOF : + return isom_read_enof( root, box, parent, level ); + case ISOM_BOX_TYPE_EDTS : + return isom_read_edts( root, box, parent, level ); + case ISOM_BOX_TYPE_ELST : + return isom_read_elst( root, box, parent, level ); + case ISOM_BOX_TYPE_TREF : + return isom_read_tref( root, box, parent, level ); + case ISOM_BOX_TYPE_MDIA : + return isom_read_mdia( root, box, parent, level ); + case ISOM_BOX_TYPE_MDHD : + return isom_read_mdhd( root, box, parent, level ); + case ISOM_BOX_TYPE_HDLR : + return isom_read_hdlr( root, box, parent, level ); + case ISOM_BOX_TYPE_MINF : + return isom_read_minf( root, box, parent, level ); + case ISOM_BOX_TYPE_VMHD : + return isom_read_vmhd( root, box, parent, level ); + case ISOM_BOX_TYPE_SMHD : + return isom_read_smhd( root, box, parent, level ); + case ISOM_BOX_TYPE_HMHD : + return isom_read_hmhd( root, box, parent, level ); + case ISOM_BOX_TYPE_NMHD : + return isom_read_nmhd( root, box, parent, level ); + case QT_BOX_TYPE_GMHD : + return isom_read_gmhd( root, box, parent, level ); + case QT_BOX_TYPE_GMIN : + return isom_read_gmin( root, box, parent, level ); + case QT_BOX_TYPE_TEXT : + return isom_read_text( root, box, parent, level ); + case ISOM_BOX_TYPE_DINF : + return isom_read_dinf( root, box, parent, level ); + case ISOM_BOX_TYPE_DREF : + return isom_read_dref( root, box, parent, level ); + case ISOM_BOX_TYPE_URL : + return isom_read_url ( root, box, parent, level ); + case ISOM_BOX_TYPE_STBL : + return isom_read_stbl( root, box, parent, level ); + case ISOM_BOX_TYPE_STSD : + return isom_read_stsd( root, box, parent, level ); + case ISOM_BOX_TYPE_BTRT : + return isom_read_btrt( root, box, parent, level ); + case ISOM_BOX_TYPE_CLAP : + return isom_read_clap( root, box, parent, level ); + case ISOM_BOX_TYPE_PASP : + return isom_read_pasp( root, box, parent, level ); + case QT_BOX_TYPE_COLR : + return isom_read_colr( root, box, parent, level ); + case ISOM_BOX_TYPE_STSL : + return isom_read_stsl( root, box, parent, level ); + case ISOM_BOX_TYPE_AVCC : + return isom_read_avcC( root, box, parent, level ); + case QT_BOX_TYPE_WAVE : + return isom_read_wave( root, box, parent, level ); + case QT_BOX_TYPE_CHAN : + return isom_read_chan( root, box, parent, level ); + case ISOM_BOX_TYPE_FTAB : + return isom_read_ftab( root, box, parent, level ); + case ISOM_BOX_TYPE_STTS : + return isom_read_stts( root, box, parent, level ); + case ISOM_BOX_TYPE_CTTS : + return isom_read_ctts( root, box, parent, level ); + case ISOM_BOX_TYPE_CSLG : + return isom_read_cslg( root, box, parent, level ); + case ISOM_BOX_TYPE_STSS : + return isom_read_stss( root, box, parent, level ); + case QT_BOX_TYPE_STPS : + return isom_read_stps( root, box, parent, level ); + case ISOM_BOX_TYPE_SDTP : + return isom_read_sdtp( root, box, parent, level ); + case ISOM_BOX_TYPE_STSC : + return isom_read_stsc( root, box, parent, level ); + case ISOM_BOX_TYPE_STSZ : + return isom_read_stsz( root, box, parent, level ); + case ISOM_BOX_TYPE_STCO : + case ISOM_BOX_TYPE_CO64 : + return isom_read_stco( root, box, parent, level ); + case ISOM_BOX_TYPE_SGPD : + return isom_read_sgpd( root, box, parent, level ); + case ISOM_BOX_TYPE_SBGP : + return isom_read_sbgp( root, box, parent, level ); + case ISOM_BOX_TYPE_UDTA : + return isom_read_udta( root, box, parent, level ); + case ISOM_BOX_TYPE_CHPL : + return isom_read_chpl( root, box, parent, level ); + case ISOM_BOX_TYPE_FREE : + case ISOM_BOX_TYPE_SKIP : + return isom_read_free( root, box, parent, level ); + case ISOM_BOX_TYPE_MDAT : + return isom_read_mdat( root, box, parent, level ); + default : + return isom_read_unknown_box( root, box, parent, level ); + } +} + +static int isom_read_root( lsmash_root_t *root ) +{ + lsmash_bs_t *bs = root->bs; + if( !bs ) + return -1; + isom_box_t box; + if( root->flags & LSMASH_FILE_MODE_DUMP ) + { + root->print = lsmash_create_entry_list(); + if( !root->print ) + return -1; + } + root->size = UINT64_MAX; + int ret = isom_read_children( root, &box, root, 0 ); + root->size = box.size; + lsmash_bs_empty( bs ); + if( ret < 0 ) + return ret; + return isom_check_compatibility( root ); +} + +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; + for( uint32_t j = 0; j < data->sample_count; j++ ) + dts += data->sample_delta; + i += data->sample_count; + } + if( !entry ) + return 0; + while( i++ < sample_number ) + dts += data->sample_delta; + 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->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl || + !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list || trak->mdia->minf->stbl->stts->list->entry_count == 0 ) + return -1; + 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 == 0 ) + return -1; + /* 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; + 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 = dts + ctts_data->sample_offset; + min_cts = LSMASH_MIN( min_cts, cts ); + max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset ); + min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset ); + 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( !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 mdhd duration, update stts and mdhd relatively. */ + if( mdhd->duration > dts ) + last_sample_delta = mdhd->duration - dts; + else + mdhd->duration = dts + last_sample_delta; /* mdhd duration must not less than last dts. */ + if( isom_replace_last_sample_delta( stbl, last_sample_delta ) ) + return -1; + /* Explicit composition information and DTS shifting */ + if( cslg || trak->root->qt_compatible ) + { + if( (min_offset <= UINT32_MAX) && (max_offset <= UINT32_MAX) && + (min_cts <= UINT32_MAX) && (2 * max_cts - max2_cts <= UINT32_MAX) ) + { + if( !cslg ) + { + if( isom_add_cslg( trak->mdia->minf->stbl ) ) + return -1; + cslg = stbl->cslg; + } + cslg->compositionToDTSShift = 0; /* We don't consider DTS shifting at present. */ + cslg->leastDecodeToDisplayDelta = min_offset; + cslg->greatestDecodeToDisplayDelta = max_offset; + cslg->compositionStartTime = min_cts; + cslg->compositionEndTime = 2 * max_cts - max2_cts; + } + 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; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->duration = 0; + if( !trak->edts || !trak->edts->elst ) + { + if( !trak->mdia || !trak->mdia->mdhd || !trak->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)trak->root->moov->mvhd->timescale / trak->mdia->mdhd->timescale); + } + else + { + 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( !tkhd->duration ) + tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff; + return isom_update_mvhd_duration( trak->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 track already has a edit list, we don't change or update duration in tkhd and mvhd. */ + return trak->edts && trak->edts->elst ? 0 : isom_update_tkhd_duration( trak ); +} + +static int isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size ) +{ + if( !sample_size ) + return -1; + return isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ); +} + +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->sync_point ) /* no null check for prop */ + { + if( cache->all_sync ) + { + 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->partial_sync ) /* no null check for prop */ + 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_group_roll_recovery( isom_trak_entry_t *trak, lsmash_sample_property_t *prop ) +{ + if( !trak->root->avc_extensions ) + return 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_sbgp_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); + isom_sgpd_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( !pool->entry_count || prop->recovery.start_point ) + { + if( pool->entry_count ) + group->delimited = 1; + /* Create a new group. This group is not 'roll' yet, so we set 0 on its group_description_index. */ + group = malloc( sizeof(isom_roll_group_t) ); + if( !group ) + return -1; + memset( group, 0, sizeof(isom_roll_group_t) ); + group->first_sample = sample_count; + group->recovery_point = prop->recovery.complete; + group->sample_to_group = isom_add_sbgp_entry( sbgp, pool->entry_count ? 1 : sample_count, 0 ); + if( !group->sample_to_group || lsmash_add_entry( pool, group ) ) + { + free( group ); + return -1; + } + } + else + ++ group->sample_to_group->sample_count; + if( prop->sync_point ) + { + /* All recoveries are completed if encountered a sync sample. */ + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + group = (isom_roll_group_t *)entry->data; + if( !group ) + return -1; + group->described = 1; + } + return 0; + } + 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; + /* Be careful of consecutive undecodable leading samples after the partial sync sample (i.e. Open-GOP I-picture). + * These samples are not able to decode correctly from the recovery point specified in display order. + * In this case, therefore, roll_distance will be number of consecutive undecodable leading samples after the partial sync sample plus one. */ + if( group->roll_recovery ) + { + ++ group->roll_recovery->roll_distance; + if( prop->leading != ISOM_SAMPLE_IS_UNDECODABLE_LEADING ) + { + /* The intra picture that doesn't lead any pictures that depend on former groups is substantially an instantaneous decoding picture. + * Therefore, roll_distance of the group that includes this picture as the first sample is zero. + * However, this means this group cannot have random access point, since roll_distance = 0 must not be used. This is not preferable. + * So we treat this group as 'roll' and pick roll_distance = 1 though this treatment is over-estimation. */ + group->sample_to_group->group_description_index = sgpd->list->entry_count; + group->described = 1; + } + } + else if( prop->recovery.identifier == group->recovery_point ) + { + int16_t distance = sample_count - group->first_sample; + group->roll_recovery = isom_add_roll_group_entry( sgpd, distance ); + if( !group->roll_recovery ) + return -1; + group->described = distance || !(prop->independent || (prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING)); + if( distance ) + { + group->sample_to_group->group_description_index = sgpd->list->entry_count; + /* 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. */ + } + } + for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head ) + { + 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; +} + +/* 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_write_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; + isom_chunk_t *current = &trak->cache->chunk; + if( current->chunk_number == 0 ) + { + /* Very initial settings, just once per trak */ + current->pool = lsmash_create_entry_list(); + if( !current->pool ) + return -1; + current->chunk_number = 1; + current->sample_description_index = sample->index; + current->first_dts = 0; + } + if( sample->dts < current->first_dts ) + return -1; /* easy error check. */ + double chunk_duration = (double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale; + if( trak->root->max_chunk_duration >= chunk_duration && 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; + isom_stsc_t *stsc = stbl->stsc; + /* Add a new chunk sequence in this track if needed. */ + if( !stsc->list->tail || current->sample_description_index != sample->index || + current->pool->entry_count != ((isom_stsc_entry_t *)stsc->list->tail->data)->samples_per_chunk ) + { + if( isom_add_stsc_entry( stbl, current->chunk_number, current->pool->entry_count, current->sample_description_index ) ) + return -1; + } + /* Add a new chunk offset in this track here. */ + if( isom_add_stco_entry( stbl, trak->root->bs->written ) ) + 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 lsmash_write_sample() */ + return 1; +} + +static int isom_add_dts( isom_trak_entry_t *trak, uint64_t dts ) +{ + if( !trak->cache || !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; + if( !stts->list->entry_count ) + { + if( isom_add_stts_entry( stbl, dts ) ) + return -1; + trak->cache->timestamp.dts = dts; + return 0; + } + if( dts <= trak->cache->timestamp.dts ) + return -1; + uint32_t sample_delta = dts - trak->cache->timestamp.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 -1; + trak->cache->timestamp.dts = dts; + return 0; +} + +static int isom_add_cts( isom_trak_entry_t *trak, uint64_t cts ) +{ + if( !trak->cache ) + return -1; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_ctts_t *ctts = stbl->ctts; + if( !ctts ) + { + if( cts == trak->cache->timestamp.dts ) + { + trak->cache->timestamp.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; + uint32_t sample_count = isom_get_sample_count( trak ); + ctts = stbl->ctts; + isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data; + if( sample_count != 1 ) + { + data->sample_count = isom_get_sample_count( trak ) - 1; + if( isom_add_ctts_entry( stbl, cts - trak->cache->timestamp.dts ) ) + return -1; + } + else + data->sample_offset = cts; + trak->cache->timestamp.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 - trak->cache->timestamp.dts; + if( data->sample_offset == sample_offset ) + ++ data->sample_count; + else if( isom_add_ctts_entry( stbl, sample_offset ) ) + return -1; + trak->cache->timestamp.cts = cts; + return 0; +} + +static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts ) +{ + if( cts < dts ) + return -1; + if( isom_get_sample_count( trak ) > 1 && isom_add_dts( trak, dts ) ) + return -1; + return isom_add_cts( trak, cts ); +} + +static int lsmash_write_sample_data( lsmash_root_t *root, lsmash_sample_t *sample ) +{ + if( !root || !root->mdat || !root->bs || !root->bs->stream ) + return -1; + lsmash_bs_put_bytes( root->bs, sample->data, sample->length ); + if( lsmash_bs_write_data( root->bs ) ) + return -1; + root->mdat->size += sample->length; + return 0; +} + +static int isom_write_pooled_samples( isom_trak_entry_t *trak, lsmash_entry_list_t *pool ) +{ + if( !trak->root || !trak->cache ) + return -1; + for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next ) + { + lsmash_sample_t *data = (lsmash_sample_t *)entry->data; + if( !data || !data->data ) + return -1; + /* Add a sample_size and increment sample_count. */ + if( isom_add_size( trak, data->length ) ) + return -1; + /* Add a decoding timestamp and a composition timestamp. */ + if( isom_add_timestamp( trak, data->dts, data->cts ) ) + return -1; + /* Add a sync point if needed. */ + if( isom_add_sync_point( trak, isom_get_sample_count( trak ), &data->prop ) ) + return -1; + /* Add a partial sync point if needed. */ + if( isom_add_partial_sync( trak, isom_get_sample_count( trak ), &data->prop ) ) + return -1; + /* Add leading, independent, disposable and redundant information if needed. */ + if( isom_add_dependency_type( trak, &data->prop ) ) + return -1; + /* Group samples into roll recovery type if needed. */ + if( isom_group_roll_recovery( trak, &data->prop ) ) + return -1; + if( lsmash_write_sample_data( trak->root, data ) ) + return -1; + } + lsmash_remove_entries( pool, lsmash_delete_sample ); + return 0; +} + +static int isom_output_cache( lsmash_root_t *root, uint32_t track_ID ) +{ + 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; + isom_chunk_t *current = &trak->cache->chunk; + if( !current->pool ) + return 0; /* no caches */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stsc->list->tail || + current->pool->entry_count != ((isom_stsc_entry_t *)stbl->stsc->list->tail->data)->samples_per_chunk ) + if( isom_add_stsc_entry( stbl, current->chunk_number, current->pool->entry_count, current->sample_description_index ) ) + return -1; + if( isom_add_stco_entry( stbl, root->bs->written ) || + isom_write_pooled_samples( trak, current->pool ) ) + return -1; + isom_sgpd_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL ); + if( !sgpd ) + return 0; + for( lsmash_entry_t *entry = trak->cache->roll.pool->head; entry; entry = entry->next ) + { + isom_roll_group_t *group = (isom_roll_group_t *)entry->data; + if( !group ) + return -1; + if( group->described || !group->roll_recovery ) + continue; + /* roll_distance == 0 must not be used. */ + if( !group->roll_recovery->roll_distance ) + lsmash_remove_entry( sgpd->list, sgpd->list->entry_count, NULL ); + else + group->sample_to_group->group_description_index = sgpd->list->entry_count; + group->described = 1; + } + return 0; +} + +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_avc_entry_t *data = (isom_avc_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; +} + +int lsmash_update_bitrate_info( 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->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl || + !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list || !trak->mdia->minf->stbl->stsz || + !trak->mdia->minf->stbl->stts->list || !trak->mdia->minf->stbl->stts->list ) + return -1; + isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + 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 = trak->mdia->mdhd->timescale; + uint64_t dts = 0; + lsmash_entry_t *stts_entry = trak->mdia->minf->stbl->stts->list->head; + lsmash_entry_t *stsz_entry = trak->mdia->minf->stbl->stsz->list ? trak->mdia->minf->stbl->stsz->list->head : NULL; + isom_stts_entry_t *stts_data = NULL; + while( stts_entry ) + { + uint32_t size; + if( trak->mdia->minf->stbl->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 = trak->mdia->minf->stbl->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)trak->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_avc_entry_t *stsd_data = (isom_avc_entry_t *)sample_entry; + if( !stsd_data ) + return -1; + //isom_btrt_t *btrt = (isom_btrt_t *)stsd_data->btrt; + 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_MP4A_AUDIO : + { + isom_esds_t *esds = NULL; + if( ((isom_audio_entry_t *)sample_entry)->version ) + { + 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_mp4a_entry_t *stsd_data = (isom_mp4a_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 || alac->exdata_length < 36 || !alac->exdata ) + return -1; + 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; + } + 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 ) + 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->tkhd || !trak->mdia ) + return -1; + if( !trak->mdia->mdhd || !trak->mdia->hdlr || !trak->mdia->minf ) + return -1; + if( (root->qt_compatible && !trak->mdia->minf->hdlr) || !trak->mdia->minf->dinf || !trak->mdia->minf->dinf->dref ) + return -1; + if( !trak->mdia->minf->stbl ) + return -1; + if( !trak->mdia->minf->stbl->stsz ) + return -1; + if( !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list->head ) + return -1; + if( !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list->head ) + return -1; + if( !trak->mdia->minf->stbl->stco || !trak->mdia->minf->stbl->stco->list->head ) + return -1; + } + if( !root->moov->trak_list->head ) + 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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_BOX_HEADER_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 > UINT32_MAX ) + elst->version = 1; + } + elst->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_SIZE; + for( uint32_t i = 0; i < tref->type_count; i++ ) + { + isom_tref_type_t *ref = &tref->ref[i]; + ref->size = ISOM_DEFAULT_BOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_btrt_size( isom_btrt_t *btrt ) +{ + if( !btrt ) + return 0; + btrt->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 12; + CHECK_LARGESIZE( btrt->size ); + return btrt->size; +} + +static uint64_t isom_update_pasp_size( isom_pasp_t *pasp ) +{ + if( !pasp ) + return 0; + pasp->size = ISOM_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_FULLBOX_HEADER_SIZE + 6; + CHECK_LARGESIZE( stsl->size ); + return stsl->size; +} + +static uint64_t isom_update_visual_extension_size( isom_visual_entry_t *visual ) +{ + if( !visual ) + return 0; + return isom_update_clap_size( visual->clap ) + + isom_update_pasp_size( visual->pasp ) + + isom_update_colr_size( visual->colr ) + + isom_update_stsl_size( visual->stsl ); +} + +static uint64_t isom_update_avcC_size( isom_avcC_t *avcC ) +{ + if( !avcC || !avcC->sequenceParameterSets || !avcC->pictureParameterSets ) + return 0; + uint64_t size = ISOM_DEFAULT_BOX_HEADER_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_avc_entry_size( isom_avc_entry_t *avc ) +{ + if( !avc || + ((avc->type != ISOM_CODEC_TYPE_AVC1_VIDEO) && + (avc->type != ISOM_CODEC_TYPE_AVC2_VIDEO) && + (avc->type != ISOM_CODEC_TYPE_AVCP_VIDEO)) ) + return 0; + avc->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 78 + + isom_update_visual_extension_size( (isom_visual_entry_t *)avc ) + + isom_update_avcC_size( avc->avcC ) + + isom_update_btrt_size( avc->btrt ); + CHECK_LARGESIZE( avc->size ); + return avc->size; +} + +static uint64_t isom_update_esds_size( isom_esds_t *esds ) +{ + if( !esds ) + return 0; + esds->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES ); + CHECK_LARGESIZE( esds->size ); + return esds->size; +} + +static uint64_t isom_update_mp4a_entry_size( isom_mp4a_entry_t *mp4a ) +{ + if( !mp4a || mp4a->type != ISOM_CODEC_TYPE_MP4A_AUDIO ) + return 0; + mp4a->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 28 + isom_update_esds_size( mp4a->esds ); + CHECK_LARGESIZE( mp4a->size ); + return mp4a->size; +} + +#if 0 +static uint64_t isom_update_visual_entry_size( isom_visual_entry_t *visual ) +{ + if( !visual || visual->type != ISOM_CODEC_TYPE_visual_VIDEO ) + return 0; + visual->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 78 + isom_update_visual_extension_size( visual ); + CHECK_LARGESIZE( visual->size ); + return visual->size; +} + +static uint64_t isom_update_mp4v_entry_size( isom_mp4v_entry_t *mp4v ) +{ + if( !mp4v || mp4v->type != ISOM_CODEC_TYPE_MP4V_VIDEO ) + return 0; + mp4v->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 78 + + isom_update_visual_extension_size( (isom_visual_entry_t *)mp4v ) + + isom_update_esds_size( mp4v->esds ); + CHECK_LARGESIZE( mp4v->size ); + return mp4v->size; +} + +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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_SIZE + 4; + CHECK_LARGESIZE( frma->size ); + return frma->size; +} + +static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a ) +{ + if( !mp4a ) + return 0; + mp4a->size = ISOM_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_SIZE + + isom_update_frma_size( wave->frma ) + + isom_update_mp4a_size( wave->mp4a ) + + isom_update_esds_size( wave->esds ) + + isom_update_terminator_size( wave->terminator ); + 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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_BOX_HEADER_SIZE + 28 + + isom_update_chan_size( audio->chan ) + + (uint64_t)audio->exdata_length; + if( audio->version == 1 ) + audio->size += 16 + isom_update_wave_size( audio->wave ); + else if( audio->version == 2 ) + audio->size += 36 + isom_update_wave_size( audio->wave ); + 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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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 : +#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 + size += isom_update_avc_entry_size( (isom_avc_entry_t *)data ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4V_VIDEO : + size += isom_update_mp4v_entry_size( (isom_mp4v_entry_t *)data ); + break; +#endif + case ISOM_CODEC_TYPE_MP4A_AUDIO : + if( !((isom_audio_entry_t *)data)->version ) + size += isom_update_mp4a_entry_size( (isom_mp4a_entry_t *)data ); + else + size += isom_update_audio_entry_size( (isom_audio_entry_t *)data ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + size += isom_update_mp4s_entry_size( (isom_mp4s_entry_t *)data ); + break; + 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 : + size += isom_update_visual_entry_size( (isom_visual_entry_t *)data ); + break; +#endif + 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 : +#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_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 : +#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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_DEFAULT_LIST_FULLBOX_HEADER_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_t *sbgp ) +{ + if( !sbgp || !sbgp->list ) + return 0; + sbgp->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_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_t *sgpd ) +{ + if( !sgpd || !sgpd->list ) + return 0; + uint64_t size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_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_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_DEFAULT_BOX_HEADER_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 ); + for( uint32_t i = 0; i < stbl->sgpd_count; i++ ) + stbl->size += isom_update_sgpd_size( stbl->sgpd + i ); + for( uint32_t i = 0; i < stbl->sbgp_count; i++ ) + stbl->size += isom_update_sbgp_size( stbl->sbgp + i ); + 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_DEFAULT_BOX_HEADER_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_DEFAULT_BOX_HEADER_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_DEFAULT_FULLBOX_HEADER_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_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_DEFAULT_BOX_HEADER_SIZE + ( udta_moov ? isom_update_chpl_size( udta->chpl ) : 0 ); + 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_DEFAULT_BOX_HEADER_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 ); + CHECK_LARGESIZE( trak->size ); + return trak->size; +} + +static int isom_update_moov_size( isom_moov_t *moov ) +{ + if( !moov ) + return -1; + moov->size = ISOM_DEFAULT_BOX_HEADER_SIZE + + isom_update_mvhd_size( moov->mvhd ) + + isom_update_iods_size( moov->iods ) + + isom_update_udta_size( moov->udta, NULL ); + 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; +} + +/******************************* + 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, uint32_t 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, media_type, root ) ) + return 0; + if( root->qt_compatible && isom_add_hdlr( NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL, root ) ) + 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_audio ) + { + 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; +} + +int lsmash_set_media_handler( lsmash_root_t *root, uint32_t track_ID, lsmash_media_type_code media_type, char *handler_name ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia ) + return -1; + if( !trak->mdia->hdlr && isom_add_hdlr( trak->mdia, NULL, 0, 0 ) ) + return -1; + isom_hdlr_t *hdlr = trak->mdia->hdlr; + hdlr->componentType = QT_HANDLER_TYPE_MEDIA; + hdlr->componentSubtype = media_type; + if( handler_name ) + { + if( hdlr->componentName ) + free( hdlr->componentName ); + uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible; + uint8_t *name = NULL; + if( root->qt_compatible ) + { + name_length = LSMASH_MIN( name_length, 255 ); + name = malloc( name_length ); + name[0] = name_length & 0xff; + } + else + name = malloc( name_length ); + memcpy( name + root->qt_compatible, name, strlen( handler_name ) ); + if( root->isom_compatible ) + name[name_length - 1] = 0; + hdlr->componentName = name; + hdlr->componentName_length = name_length; + } + return 0; +} + +int lsmash_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; +} + +int lsmash_set_data_handler( lsmash_root_t *root, uint32_t track_ID, lsmash_data_reference_type_code reference_type, char *handler_name ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf ) + return -1; + if( !trak->mdia->minf->hdlr && isom_add_hdlr( NULL, trak->mdia->minf, 0, 0 ) ) + return -1; + isom_hdlr_t *hdlr = trak->mdia->minf->hdlr; + hdlr->componentType = QT_HANDLER_TYPE_DATA; + hdlr->componentSubtype = reference_type; + if( handler_name ) + { + if( hdlr->componentName ) + free( hdlr->componentName ); + uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible; + uint8_t *name = NULL; + if( root->qt_compatible ) + { + name_length = LSMASH_MIN( name_length, 255 ); + name = malloc( name_length ); + name[0] = name_length & 0xff; + } + else + name = malloc( name_length ); + memcpy( name + root->qt_compatible, name, strlen( handler_name ) ); + if( root->isom_compatible ) + name[name_length - 1] = 0; + hdlr->componentName = name; + hdlr->componentName_length = name_length; + } + return 0; +} + +int lsmash_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; +} + +int lsmash_set_media_timescale( lsmash_root_t *root, uint32_t track_ID, uint32_t timescale ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd ) + return -1; + trak->mdia->mdhd->timescale = timescale; + 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; +} + +int lsmash_set_track_mode( lsmash_root_t *root, uint32_t track_ID, lsmash_track_mode_code mode ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + trak->tkhd->flags = mode; + return 0; +} + +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; +} + +int lsmash_set_media_language( lsmash_root_t *root, uint32_t track_ID, char *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 && (strlen( ISO_language ) == 3) ) + language = ISOM_LANG( 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 && (strlen( ISO_language ) == 3) ) + { + if( isom_iso2mac_language( ISOM_LANG( ISO_language ), &language ) ) + return -1; + } + else + language = MAC_language; + } + else + return -1; + trak->mdia->mdhd->language = language; + return 0; +} + +int lsmash_set_track_ID( lsmash_root_t *root, uint32_t track_ID, uint32_t new_track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd || !root->moov->mvhd ) + return -1; + trak->tkhd->track_ID = new_track_ID; + /* Update next_track_ID if needed. */ + if( root->moov->mvhd->next_track_ID <= new_track_ID ) + root->moov->mvhd->next_track_ID = new_track_ID + 1; + return 0; +} + +int lsmash_set_track_volume( lsmash_root_t *root, uint32_t track_ID, int16_t volume ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + trak->tkhd->volume = volume; + return 0; +} + +int lsmash_set_track_presentation_size( lsmash_root_t *root, uint32_t track_ID, uint32_t width, uint32_t height ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + trak->tkhd->width = width; + trak->tkhd->height = height; + return 0; +} + +static int isom_confirm_visual_type( uint32_t sample_type ) +{ + 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 + break; + default : + return -1; + } + return 0; +} + +int lsmash_set_sample_resolution( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint16_t width, uint16_t height ) +{ + 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 || isom_confirm_visual_type( data->type ) ) + return -1; + data->width = width; + data->height = height; + return 0; +} + +int isom_set_sample_aspect_ratio( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint32_t hSpacing, uint32_t vSpacing ) +{ + 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 || isom_confirm_visual_type( data->type ) ) + return -1; + if( !data->pasp && isom_add_pasp( data ) ) + return -1; + isom_pasp_t *pasp = data->pasp; + pasp->hSpacing = hSpacing; + pasp->vSpacing = vSpacing; + return 0; +} + +int lsmash_set_color_parameter( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + lsmash_color_parameter primaries, lsmash_color_parameter transfer, lsmash_color_parameter matrix ) +{ + if( !root->qt_compatible ) + return -1; + 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 || isom_confirm_visual_type( data->type ) ) + return -1; + if( !data->colr && isom_add_colr( data ) ) + return -1; + isom_colr_t *colr = data->colr; + /* 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; + return 0; +} + +int lsmash_set_track_aperture_modes( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number ) +{ + if( !root->qt_compatible ) + return -1; + 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 || isom_confirm_visual_type( data->type ) ) + return -1; + uint32_t width = data->width << 16; + uint32_t height = data->height << 16; + 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; + if( !data->pasp && isom_add_pasp( data ) ) + return -1; + isom_pasp_t *pasp = data->pasp; + isom_clap_t *clap = data->clap; + if( !clap ) + { + if( isom_add_clap( data ) ) + return -1; + clap = data->clap; + clap->cleanApertureWidthN = data->width; + clap->cleanApertureHeightN = data->height; + } + if( !pasp->hSpacing || !pasp->vSpacing || + !clap->cleanApertureWidthN || !clap->cleanApertureWidthD || + !clap->cleanApertureHeightN || !clap->cleanApertureHeightD || + !clap->horizOffD || !clap->vertOffD ) + return -1; + double par = (double)pasp->hSpacing / pasp->vSpacing; + double clap_width = ((double)clap->cleanApertureWidthN / clap->cleanApertureWidthD) * (1<<16); + double clap_height = ((double)clap->cleanApertureHeightN / clap->cleanApertureHeightD) * (1<<16); + 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; +} + +int lsmash_set_scaling_method( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + lsmash_scaling_method_code scale_method, int16_t display_center_x, int16_t display_center_y ) +{ + if( !root || !track_ID || !entry_number || !scale_method ) + return -1; + 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 || isom_confirm_visual_type( data->type ) ) + return -1; + if( !data->stsl && isom_add_stsl( data ) ) + return -1; + isom_stsl_t *stsl = data->stsl; + stsl->constraint_flag = 1; + stsl->scale_method = scale_method; + stsl->display_center_x = display_center_x; + stsl->display_center_y = display_center_y; + return 0; +} + +int lsmash_set_channel_layout( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, lsmash_channel_layout_tag_code layout_tag, lsmash_channel_bitmap_code bitmap ) +{ + if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS ) + return -1; /* We don't support the feature of Channel Descriptions */ + if( !root || !track_ID || !entry_number ) + return -1; + 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_audio_entry_t *data = (isom_audio_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data ) + return -1; + if( !data->chan && isom_add_chan( data ) ) + return -1; + isom_chan_t *chan = data->chan; + if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!bitmap || bitmap > QT_CHANNEL_BIT_FULL) ) + { + chan->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN | data->numAudioChannels; + chan->channelBitmap = 0; + } + else + { + chan->channelLayoutTag = layout_tag; + chan->channelBitmap = bitmap; + } + return 0; +} + +int lsmash_set_sample_type( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint32_t sample_type ) +{ + 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_sample_entry_t *data = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data ) + return -1; + data->type = sample_type; + return 0; +} + +int lsmash_create_grouping( lsmash_root_t *root, uint32_t track_ID, lsmash_grouping_type_code grouping_type ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + if( isom_add_sgpd( trak->mdia->minf->stbl, grouping_type ) || + isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) ) + return -1; + return 0; +} + +/*---- movie manipulators ----*/ + +lsmash_root_t *lsmash_open_movie( const char *filename, uint32_t mode ) +{ + char open_mode[4] = { 0 }; + if( mode & LSMASH_FILE_MODE_WRITE ) + memcpy( open_mode, "w+b", 4 ); + else if( mode & LSMASH_FILE_MODE_READ ) + memcpy( open_mode, "rb", 3 ); + if( !open_mode[0] ) + return NULL; + lsmash_root_t *root = malloc( sizeof(lsmash_root_t) ); + if( !root ) + return NULL; + memset( root, 0, sizeof(lsmash_root_t) ); + root->bs = malloc( sizeof(lsmash_bs_t) ); + if( !root->bs ) + goto fail; + memset( root->bs, 0, sizeof(lsmash_bs_t) ); + root->bs->stream = fopen( filename, open_mode ); + if( !root->bs->stream ) + goto fail; + root->flags = mode; + if( (mode & LSMASH_FILE_MODE_WRITE) && (isom_add_moov( root ) || isom_add_mvhd( root->moov )) ) + goto fail; + if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) && isom_read_root( root ) ) + goto fail; + return root; +fail: + lsmash_destroy_root( root ); + return NULL; +} + +int lsmash_set_movie_timescale( lsmash_root_t *root, uint32_t timescale ) +{ + if( !root || !root->moov || !root->moov->mvhd ) + return -1; + root->moov->mvhd->timescale = timescale; + 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; +} + +int lsmash_set_brands( lsmash_root_t *root, lsmash_brand_type_code major_brand, uint32_t minor_version, lsmash_brand_type_code *brands, uint32_t brand_count ) +{ + if( !root ) + return -1; + if( !brand_count ) + { + /* Absence of ftyp 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; + ftyp->compatible_brands = malloc( brand_count * sizeof(uint32_t) ); + if( !ftyp->compatible_brands ) + return -1; + 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 ); +} + +int lsmash_set_max_chunk_duration( lsmash_root_t *root, double max_chunk_duration ) +{ + if( !root ) + return -1; + root->max_chunk_duration = max_chunk_duration; + return 0; +} + +int lsmash_write_ftyp( lsmash_root_t *root ) +{ + if( !root ) + return -1; + 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] ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_moov( lsmash_root_t *root ) +{ + if( !root || !root->moov ) + return -1; + isom_bs_put_box_common( root->bs, root->moov ); + if( lsmash_bs_write_data( root->bs ) ) + return -1; + if( isom_write_mvhd( root ) || + isom_write_iods( root ) ) + return -1; + if( root->moov->trak_list ) + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + if( isom_write_trak( root->bs, (isom_trak_entry_t *)entry->data ) ) + return -1; + return isom_write_udta( root->bs, root->moov, NULL ); +} + +/* If a mdat box already exists, flush a current one and start a new one. */ +int lsmash_add_mdat( lsmash_root_t *root ) +{ + if( !root ) + return 0; + if( root->mdat && lsmash_write_mdat_size( root ) ) /* flush a current mdat */ + return -1; + else + { + isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT ); + root->mdat = mdat; + } + return isom_write_mdat_header( root ); /* start a new mdat */ +} + +int lsmash_write_mdat_size( lsmash_root_t *root ) +{ + if( !root || !root->bs || !root->bs->stream || !root->mdat ) + return -1; + 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 = ftell( stream ); + if( large_flag ) + { + 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_DEFAULT_BOX_HEADER_SIZE ); + } + else + { + fseek( stream, mdat->placeholder_pos + ISOM_DEFAULT_BOX_HEADER_SIZE, SEEK_SET ); + lsmash_bs_put_be32( bs, mdat->size ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT ); + } + if( lsmash_bs_write_data( bs ) ) + return -1; + fseek( stream, current_pos, SEEK_SET ); + return 0; +} + +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 ----*/ + +int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux ) +{ + if( !root || !root->moov || !root->moov->trak_list ) + 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->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; + uint32_t track_mode = trak->is_chapter ? 0 : ISOM_TRACK_ENABLED; + track_mode |= ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW; + if( root->qt_compatible ) + track_mode |= QT_TRACK_IN_POSTER; + if( lsmash_set_track_mode( root, track_ID, track_mode ) ) + return -1; + 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( root->mp4_version1 == 1 && isom_add_iods( root->moov ) ) + return -1; + if( isom_check_mandatory_boxes( root ) || + isom_set_movie_creation_time( root ) || + isom_update_moov_size( root->moov ) ) + return -1; + + if( !remux ) + return isom_write_moov( root ); + + /* stco->co64 conversion, depending on last chunk's offset */ + for( lsmash_entry_t* entry = root->moov->trak_list->head; entry; ) + { + isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data; + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + isom_stco_t* stco = trak->mdia->minf->stbl->stco; + if( !stco || !stco->list || !stco->list->tail ) + return -1; + if( stco->large_presentation + || ((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + root->moov->size <= UINT32_MAX ) + { + entry = entry->next; + continue; /* no need to remux */ + } + /* stco->co64 conversion */ + if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) + || isom_update_moov_size( root->moov ) ) + return -1; + entry = root->moov->trak_list->head; /* whenever any conversion, re-check all traks */ + } + + /* now the amount of offset is fixed. */ + + /* buffer size must be at least sizeof(moov)*2 */ + remux->buffer_size = LSMASH_MAX( remux->buffer_size, root->moov->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 = root->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 += root->moov->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 += root->moov->size; + } + + FILE *stream = root->bs->stream; + isom_mdat_t *mdat = root->mdat; + uint64_t total = ftell( stream ) + root->moov->size; // FIXME: + uint64_t readnum; + /* backup starting area of mdat and write moov there instead */ + if( fseek( stream, mdat->placeholder_pos, SEEK_SET ) ) + goto fail; + readnum = fread( buf[0], 1, size, stream ); + uint64_t read_pos = ftell( stream ); + + /* write moov there instead */ + if( fseek( stream, mdat->placeholder_pos, SEEK_SET ) + || isom_write_moov( root ) ) + goto fail; + uint64_t write_pos = ftell( stream ); + + mdat->placeholder_pos += root->moov->size; /* update placeholder */ + + /* copy-pastan */ + int buf_switch = 1; + while( readnum == size ) + { + if( fseek( stream, read_pos, SEEK_SET ) ) + goto fail; + readnum = fread( buf[buf_switch], 1, size, stream ); + read_pos = ftell( stream ); + + buf_switch ^= 0x1; + + if( fseek( stream, write_pos, SEEK_SET ) + || fwrite( buf[buf_switch], 1, size, stream ) != size ) + goto fail; + write_pos = 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: + + free( buf[0] ); + return 0; + +fail: + free( buf[0] ); + return -1; +} + +int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t 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 ); +} + +int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) +{ + if( isom_output_cache( root, track_ID ) ) + return -1; + return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta ); +} + +void lsmash_destroy_root( lsmash_root_t *root ) +{ + if( !root ) + return; + isom_remove_ftyp( root->ftyp ); + isom_remove_moov( root ); + isom_remove_mdat( root->mdat ); + isom_remove_free( root->free ); + if( root->bs ) + { + if( root->bs->stream ) + fclose( root->bs->stream ); + if( root->bs->data ) + free( root->bs->data ); + free( root->bs ); + } + isom_remove_print_funcs( root ); + free( root ); +} + +/*---- timeline manipulator ----*/ + +int lsmash_modify_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_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, entry_number ); + if( !data ) + return -1; + data->segment_duration = segment_duration; + data->media_time = media_time; + data->media_rate = media_rate; + return isom_update_tkhd_duration( trak ) ? -1 : isom_update_mvhd_duration( root->moov ); +} + +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 ? segment_duration : + trak->tkhd->duration ? trak->tkhd->duration : + isom_update_tkhd_duration( trak ) ? 0 : trak->tkhd->duration; + if( isom_add_edts( trak ) ) + return -1; + if( isom_add_elst( trak->edts ) ) + return -1; + if( 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 ----*/ + +int lsmash_write_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) +{ + /* I myself 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 || !sample || !sample->data || root->max_chunk_duration == 0 ) + return -1; + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + + /* Add a chunk if needed. */ + /* + * FIXME: I think we have to implement "arbitrate chunk handling between tracks" system. + * Which means, even if a chunk of a trak has not exceeded max_chunk_duration yet, + * the chunk should be forced to be fixed and determined so that it shall be written into the file. + * Without that, for example, a video sample with frame rate of 0.01fps would not be written + * near the corresponding audio sample. + * As a result, players(demuxers) have to use fseek to playback that kind of mp4 in A/V sync. + * Note that even though we cannot help the case with random access (i.e. seek) even with this system, + * we should do it. + */ + int ret = isom_add_chunk( trak, sample ); + if( ret < 0 ) + return -1; + + /* ret == 1 means cached samples must be flushed. */ + isom_chunk_t *current = &trak->cache->chunk; + if( ret == 1 && isom_write_pooled_samples( trak, current->pool ) ) + return -1; + + /* anyway the current sample must be pooled. */ + return lsmash_add_entry( current->pool, sample ); +} + +lsmash_sample_t *lsmash_create_sample( uint32_t size ) +{ + lsmash_sample_t *sample = malloc( sizeof(lsmash_sample_t) ); + if( !sample ) + return NULL; + memset( sample, 0, sizeof(lsmash_sample_t) ); + sample->data = malloc( size ); + if( !sample->data ) + { + free( sample ); + return NULL; + } + sample->length = size; + return sample; +} + +void lsmash_delete_sample( lsmash_sample_t *sample ) +{ + if( !sample ) + return; + if( sample->data ) + free( sample->data ); + free( 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_audio) || !root->moov || !root->moov->mvhd ) + return -1; + FILE *chapter = NULL; /* shut up 'uninitialized' warning */ + /* Create Track Reference Type Box. */ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || isom_add_tref( trak ) ) + return -1; + isom_tref_t *tref = trak->tref; + isom_tref_type_t *ref = NULL, *chap = NULL; + if( !tref->ref ) + ref = (isom_tref_type_t *)malloc( sizeof(isom_tref_type_t) ); + else + ref = (isom_tref_type_t *)realloc( tref->ref, (tref->type_count + 1) * sizeof(isom_tref_type_t) ); + if( !ref ) + return -1; + chap = &ref[tref->type_count]; + tref->ref = ref; + tref->type_count += 1; + isom_init_box_common( chap, tref, QT_TREF_TYPE_CHAP ); + chap->track_ID = (uint32_t *)malloc( sizeof(uint32_t) ); + if( !chap->track_ID ) + return -1; + uint32_t chapter_track_ID = chap->track_ID[0] = root->moov->mvhd->next_track_ID; + chap->ref_count = 1; + /* Create reference chapter track. */ + if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) ) + return -1; + /* Copy media timescale. */ + uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID ); + if( !media_timescale || lsmash_set_media_timescale( root, chapter_track_ID, media_timescale ) ) + goto fail; + /* Set media language field. ISOM: undefined / QTFF: English */ + if( lsmash_set_media_language( root, chapter_track_ID, root->max_3gpp_version < 6 && !root->itunes_audio ? NULL : "und", 0 ) ) + goto fail; + /* Create sample description. */ + uint32_t sample_type = root->max_3gpp_version < 6 && !root->itunes_audio ? QT_CODEC_TYPE_TEXT_TEXT : ISOM_CODEC_TYPE_TX3G_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 ) + return -1; + /* 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 ) + 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. */ + 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.sync_point = 1; + sample->index = sample_entry; + if( lsmash_write_sample( root, chapter_track_ID, sample ) ) + 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 ); + if( data.chapter_name ) + free( data.chapter_name ); + free( chap->track_ID ); + chap->track_ID = NULL; + /* 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; +} + +void 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; + isom_remove_edts( trak->edts ); + trak->edts = NULL; +} + +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; +} diff --git a/output/mp4/lsmash.h b/output/mp4/lsmash.h new file mode 100644 index 0000000..0fde917 --- /dev/null +++ b/output/mp4/lsmash.h @@ -0,0 +1,1234 @@ +/***************************************************************************** + * lsmash.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * 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 + +#ifndef __MINGW32__ +#define _FILE_OFFSET_BITS 64 +#endif + +#include +#include +#include +#include +#include /* for chapter handling */ + +#ifdef __MINGW32__ +#define fseek fseeko64 +#define ftell ftello64 +#endif + + +/* public defines */ +#define LSMASH_FILE_MODE_WRITE 0x00000001 +#define LSMASH_FILE_MODE_READ 0x00000002 +#define LSMASH_FILE_MODE_DUMP 0x00000004 + +#define LSMASH_4CC( a, b, c, d ) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)) + + +/* public constants */ +typedef enum +{ + 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_DAC3 = LSMASH_4CC( 'd', 'a', 'c', '3' ), + ISOM_BOX_TYPE_DAMR = LSMASH_4CC( 'd', 'a', 'm', 'r' ), + + ISOM_BOX_TYPE_FTAB = LSMASH_4CC( 'f', 't', 'a', 'b' ), + + 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_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_KMAT = LSMASH_4CC( 'k', 'm', 'a', 't' ), + QT_BOX_TYPE_LOAD = LSMASH_4CC( 'l', 'o', 'a', 'd' ), + QT_BOX_TYPE_MATT = LSMASH_4CC( 'm', 'a', 't', 't' ), + 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_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_TERMINATOR = 0x00000000, +} lsmash_box_type_code; + +typedef enum +{ + QT_HANDLER_TYPE_DATA = LSMASH_4CC( 'd', 'h', 'l', 'r' ), + QT_HANDLER_TYPE_MEDIA = LSMASH_4CC( 'm', 'h', 'l', 'r' ), +} lsmash_handler_type_code; + +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_code; + +typedef enum +{ + 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', ' ' ), +} lsmash_data_reference_type_code; + +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_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_code; + +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_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_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_code; + +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. */ +} lsmash_track_reference_type_code; + +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_RASH = LSMASH_4CC( 'r', 'a', 's', 'h' ), /* Rate Share */ + ISOM_GROUP_TYPE_ROLL = LSMASH_4CC( 'r', 'o', 'l', 'l' ), /* Roll Recovery */ + 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 */ +} lsmash_grouping_type_code; + +#define ISOM_LANG_T( a, b, c ) ((((a-0x60)&0x1f)<<10) | (((b-0x60)&0x1f)<<5) | ((c-0x60)&0x1f)) + +typedef enum +{ + ISOM_LANGUAGE_CODE_ENGLISH = ISOM_LANG_T( 'e', 'n', 'g' ), + ISOM_LANGUAGE_CODE_FRENCH = ISOM_LANG_T( 'f', 'r', 'a' ), + ISOM_LANGUAGE_CODE_GERMAN = ISOM_LANG_T( 'd', 'e', 'u' ), + ISOM_LANGUAGE_CODE_ITALIAN = ISOM_LANG_T( 'i', 't', 'a' ), + ISOM_LANGUAGE_CODE_DUTCH_M = ISOM_LANG_T( 'd', 'u', 'm' ), + ISOM_LANGUAGE_CODE_SWEDISH = ISOM_LANG_T( 's', 'w', 'e' ), + ISOM_LANGUAGE_CODE_SPANISH = ISOM_LANG_T( 's', 'p', 'a' ), + ISOM_LANGUAGE_CODE_DANISH = ISOM_LANG_T( 'd', 'a', 'n' ), + ISOM_LANGUAGE_CODE_PORTUGUESE = ISOM_LANG_T( 'p', 'o', 'r' ), + ISOM_LANGUAGE_CODE_NORWEGIAN = ISOM_LANG_T( 'n', 'o', 'r' ), + ISOM_LANGUAGE_CODE_HEBREW = ISOM_LANG_T( 'h', 'e', 'b' ), + ISOM_LANGUAGE_CODE_JAPANESE = ISOM_LANG_T( 'j', 'p', 'n' ), + ISOM_LANGUAGE_CODE_ARABIC = ISOM_LANG_T( 'a', 'r', 'a' ), + ISOM_LANGUAGE_CODE_FINNISH = ISOM_LANG_T( 'f', 'i', 'n' ), + ISOM_LANGUAGE_CODE_GREEK = ISOM_LANG_T( 'e', 'l', 'l' ), + ISOM_LANGUAGE_CODE_ICELANDIC = ISOM_LANG_T( 'i', 's', 'l' ), + ISOM_LANGUAGE_CODE_MALTESE = ISOM_LANG_T( 'm', 'l', 't' ), + ISOM_LANGUAGE_CODE_TURKISH = ISOM_LANG_T( 't', 'u', 'r' ), + ISOM_LANGUAGE_CODE_CROATIAN = ISOM_LANG_T( 'h', 'r', 'v' ), + ISOM_LANGUAGE_CODE_CHINESE = ISOM_LANG_T( 'z', 'h', 'o' ), + ISOM_LANGUAGE_CODE_URDU = ISOM_LANG_T( 'u', 'r', 'd' ), + ISOM_LANGUAGE_CODE_HINDI = ISOM_LANG_T( 'h', 'i', 'n' ), + ISOM_LANGUAGE_CODE_THAI = ISOM_LANG_T( 't', 'h', 'a' ), + ISOM_LANGUAGE_CODE_KOREAN = ISOM_LANG_T( 'k', 'o', 'r' ), + ISOM_LANGUAGE_CODE_LITHUANIAN = ISOM_LANG_T( 'l', 'i', 't' ), + ISOM_LANGUAGE_CODE_POLISH = ISOM_LANG_T( 'p', 'o', 'l' ), + ISOM_LANGUAGE_CODE_HUNGARIAN = ISOM_LANG_T( 'h', 'u', 'n' ), + ISOM_LANGUAGE_CODE_ESTONIAN = ISOM_LANG_T( 'e', 's', 't' ), + ISOM_LANGUAGE_CODE_LATVIAN = ISOM_LANG_T( 'l', 'a', 'v' ), + ISOM_LANGUAGE_CODE_SAMI = ISOM_LANG_T( 's', 'm', 'i' ), + ISOM_LANGUAGE_CODE_FAROESE = ISOM_LANG_T( 'f', 'a', 'o' ), + ISOM_LANGUAGE_CODE_RUSSIAN = ISOM_LANG_T( 'r', 'u', 's' ), + ISOM_LANGUAGE_CODE_DUTCH = ISOM_LANG_T( 'n', 'l', 'd' ), + ISOM_LANGUAGE_CODE_IRISH = ISOM_LANG_T( 'g', 'l', 'e' ), + ISOM_LANGUAGE_CODE_ALBANIAN = ISOM_LANG_T( 's', 'q', 'i' ), + ISOM_LANGUAGE_CODE_ROMANIAN = ISOM_LANG_T( 'r', 'o', 'n' ), + ISOM_LANGUAGE_CODE_CZECH = ISOM_LANG_T( 'c', 'e', 's' ), + ISOM_LANGUAGE_CODE_SLOVAK = ISOM_LANG_T( 's', 'l', 'k' ), + ISOM_LANGUAGE_CODE_SLOVENIA = ISOM_LANG_T( 's', 'l', 'v' ), + ISOM_LANGUAGE_CODE_UNDEFINED = ISOM_LANG_T( 'u', 'n', 'd' ), +} lsmash_iso_language_code; + +#undef ISOM_LANG_T + +typedef enum +{ + 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 */ +} lsmash_color_patameter_type_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_code; + +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_code; + +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_code; + +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_code; + +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_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_code; + +typedef enum +{ + ISOM_TRACK_ENABLED = 0x000001, + ISOM_TRACK_IN_MOVIE = 0x000002, + ISOM_TRACK_IN_PREVIEW = 0x000004, + + QT_TRACK_IN_POSTER = 0x000008, +} lsmash_track_mode_code; + +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_code; + +typedef enum +{ + ISOM_EDIT_MODE_NORMAL = 1<<16, + ISOM_EDIT_MODE_DWELL = 0, + ISOM_EDIT_MODE_EMPTY = -1, +} lsmash_edit_mode_code; + +typedef enum +{ + /* allow_ealier */ + QT_SAMPLE_EARLIER_PTS_ALLOWED = 1, + /* leading */ + ISOM_SAMPLE_LEADING_UNKOWN = 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_property_code; + +/* 8.6.6.2 Semantics Table 6 - objectTypeIndication Values */ +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 (see Table 7), using + this object type value shall use the BIFSConfig specified in section 9.3.5.2.2 of this + specification. */ + MP4SYS_OBJECT_TYPE_Systems_ISO_14496_1_BIFSv2 = 0x02, /* Systems ISO/IEC 14496-1 */ + /* This object type shall be used, with StreamType=0x03 (see Table 7), for Scene + Description streams that use the BIFSv2Config specified in section 9.3.5.3.2 of this + specification. 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, Annex K. */ + + 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 subpart 1 subclause 6.2.1. */ + + 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; + +/* 8.6.6.2 Semantics Table 7 - streamType Values */ +typedef enum { + MP4SYS_STREAM_TYPE_Forbidden = 0x00, /* Forbidden */ + MP4SYS_STREAM_TYPE_ObjectDescriptorStream = 0x01, /* ObjectDescriptorStream (see 8.5) */ + MP4SYS_STREAM_TYPE_ClockReferenceStream = 0x02, /* ClockReferenceStream (see 10.2.5) */ + MP4SYS_STREAM_TYPE_SceneDescriptionStream = 0x03, /* SceneDescriptionStream (see 9.2.1) */ + MP4SYS_STREAM_TYPE_VisualStream = 0x04, /* VisualStream */ + MP4SYS_STREAM_TYPE_AudioStream = 0x05, /* AudioStream */ + MP4SYS_STREAM_TYPE_MPEG7Stream = 0x06, /* MPEG7Stream */ + MP4SYS_STREAM_TYPE_IPMPStream = 0x07, /* IPMPStream (see 8.3.2) */ + MP4SYS_STREAM_TYPE_ObjectContentInfoStream = 0x08, /* ObjectContentInfoStream (see 8.4.2) */ + 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; + +/* ISO/IEC 14496-3 1.6.2.2 Payloads, Table 1.15 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 1.6.5 Signaling of SBR, Table 1.22 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; + + +/* 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 recovery_point, then this sample is the recovery point. */ + uint8_t start_point; +} lsmash_recovery_t; + +typedef struct +{ + uint8_t sync_point; + uint8_t partial_sync; + uint8_t allow_earlier; + uint8_t leading; + uint8_t independent; + uint8_t disposable; + uint8_t redundant; + lsmash_recovery_t recovery; +} 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 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 */ +/* FIXME: I wonder whether this struct should blong to namespace of "isom" or not. */ +/* NOTE: For audio, currently assuming AAC-LC. */ + +#define LSMASH_BASE_SUMMARY \ + 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. */ + uint32_t sample_type; /* Audio codec type. */ + 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. */ + /* 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 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_code scaling_method; + lsmash_color_parameter primaries; + lsmash_color_parameter transfer; + lsmash_color_parameter matrix; +} lsmash_video_summary_t; + +typedef struct lsmash_root_tag lsmash_root_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_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_mdat( lsmash_root_t *root ); +int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length ); + +int lsmash_write_ftyp( lsmash_root_t *root ); +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_brands( lsmash_root_t *root, lsmash_brand_type_code major_brand, uint32_t minor_version, lsmash_brand_type_code *brands, uint32_t brand_count ); +int lsmash_set_max_chunk_duration( lsmash_root_t *root, double max_chunk_duration ); +int lsmash_set_media_handler( lsmash_root_t *root, uint32_t track_ID, lsmash_media_type_code media_type, char *name ); +int lsmash_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name ); +int lsmash_set_data_handler( lsmash_root_t *root, uint32_t track_ID, lsmash_data_reference_type_code reference_type, char *name ); +int lsmash_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name ); +int lsmash_set_movie_timescale( lsmash_root_t *root, uint32_t timescale ); +int lsmash_set_media_timescale( lsmash_root_t *root, uint32_t track_ID, uint32_t timescale ); +int lsmash_set_track_mode( lsmash_root_t *root, uint32_t track_ID, lsmash_track_mode_code mode ); +int lsmash_set_track_presentation_size( lsmash_root_t *root, uint32_t track_ID, uint32_t width, uint32_t height ); +int lsmash_set_track_volume( lsmash_root_t *root, uint32_t track_ID, int16_t volume ); +int lsmash_set_track_aperture_modes( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number ); +int lsmash_set_sample_resolution( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint16_t width, uint16_t height ); +int lsmash_set_sample_type( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint32_t sample_type ); +int lsmash_set_sample_aspect_ratio( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint32_t hSpacing, uint32_t vSpacing ); +int lsmash_set_color_parameter( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + lsmash_color_parameter primaries, lsmash_color_parameter transfer, lsmash_color_parameter matrix ); +int lsmash_set_scaling_method( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + lsmash_scaling_method_code scale_method, int16_t display_center_x, int16_t display_center_y ); +int lsmash_set_channel_layout( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, lsmash_channel_layout_tag_code layout_tag, lsmash_channel_bitmap_code bitmap ); +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_media_language( lsmash_root_t *root, uint32_t track_ID, char *ISO_language, uint16_t Mac_language ); +int lsmash_set_track_ID( lsmash_root_t *root, uint32_t track_ID, uint32_t new_track_ID ); +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_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_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name ); +int lsmash_create_grouping( lsmash_root_t *root, uint32_t track_ID, lsmash_grouping_type_code grouping_type ); +int lsmash_create_object_descriptor( lsmash_root_t *root ); + +int lsmash_modify_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_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 ); +int lsmash_update_bitrate_info( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number ); + + +lsmash_root_t *lsmash_open_movie( const char *filename, uint32_t mode ); +uint32_t lsmash_create_track( lsmash_root_t *root, uint32_t handler_type ); +lsmash_sample_t *lsmash_create_sample( uint32_t size ); +void lsmash_delete_sample( lsmash_sample_t *sample ); +int lsmash_write_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ); +int lsmash_write_mdat_size( lsmash_root_t *root ); +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_destroy_root( lsmash_root_t *root ); + +void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID ); +void lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ); +void lsmash_delete_tyrant_chapter( lsmash_root_t *root ); + +int lsmash_print_movie( lsmash_root_t *root ); + +#endif diff --git a/output/mp4/mp4a.c b/output/mp4/mp4a.c new file mode 100644 index 0000000..16fcded --- /dev/null +++ b/output/mp4/mp4a.c @@ -0,0 +1,640 @@ +/***************************************************************************** + * mp4a.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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. */ + +#define MP4A_INTERNAL +#include "mp4a.h" + +#include +#include + +/*************************************************************************** + implementation of part of ISO/IEC 14496-3 (ISO/IEC 14496-1 relevant) +***************************************************************************/ + +/* ISO/IEC 14496-3 1.6.3.4 samplingFrequencyIndex, Table 1.16 Sampling Frequency Index */ +/* ISO/IEC 14496-3 4.5 Overall data structure, Table 4.68 Sampling frequency mapping */ +const uint32_t mp4a_AAC_frequency_table[13][4] = { + /* threshold, exact, idx, idx_for_sbr */ + { 92017, 96000, 0x0, 0xF }, /* SBR is not allowed */ + { 75132, 88200, 0x1, 0xF }, /* SBR is not allowed */ + { 55426, 64000, 0x2, 0xF }, /* SBR is not allowed */ + { 46009, 48000, 0x3, 0x0 }, + { 37566, 44100, 0x4, 0x1 }, + { 27713, 32000, 0x5, 0x2 }, + { 23004, 24000, 0x6, 0x3 }, + { 18783, 22050, 0x7, 0x4 }, + { 13856, 16000, 0x8, 0x5 }, + { 11502, 12000, 0x9, 0x6 }, + { 9391, 11025, 0xA, 0x7 }, + { 8000, 8000, 0xB, 0x8 }, + { 0, 7350, 0xB, 0xF } /* samplingFrequencyIndex for GASpecificConfig is 0xB (same as 8000Hz). */ +}; + +/* ISO/IEC 14496-3 1.6 Interface to ISO/IEC 14496-1 (MPEG-4 Systems), Table 1.13 Syntax of AudioSpecificConfig(). */ +/* This structure is represent of regularized AudioSpecificConfig. */ +/* for actual definition, see Table 1.14 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 4.4.1 Decoder configuration (GASpecificConfig), Table 4.1 Syntax of GASpecificConfig() */ +/* ISO/IEC 14496-3 4.5.1.1 GASpecificConfig(), Table 4.68 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 9.2 MPEG_1_2_SpecificConfig, Table 9.1 MPEG_1_2_SpecificConfig() */ +typedef struct { + uint8_t extension; /* shall be 0. */ +} mp4a_MPEG_1_2_SpecificConfig_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*)malloc( sizeof(mp4a_GASpecificConfig_t) ); + if( !gasc ) + return NULL; + memset( gasc, 0, sizeof(mp4a_GASpecificConfig_t) ); + 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*)malloc( sizeof(mp4a_MPEG_1_2_SpecificConfig_t) ); + if( !mpeg_1_2_sc ) + return NULL; + memset( mpeg_1_2_sc, 0, sizeof(mp4a_MPEG_1_2_SpecificConfig_t) ); + mpeg_1_2_sc->extension = 0; /* shall be 0. */ + return mpeg_1_2_sc; +} + +/* 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 ) +{ + 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( 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*)malloc( sizeof(mp4a_AudioSpecificConfig_t) ); + if( !asc ) + return NULL; + memset( asc, 0, sizeof(mp4a_AudioSpecificConfig_t) ); + + asc->sbr_mode = sbr_mode; + asc->audioObjectType = aot; + asc->channelConfiguration = channelConfig; + + uint8_t samplingFrequencyIndex; + uint8_t i = 0x0; + while( frequency < mp4a_AAC_frequency_table[i][0] ) + i++; + asc->samplingFrequencyIndex = frequency == mp4a_AAC_frequency_table[i][1] ? i : 0xF; + asc->samplingFrequency = frequency; + samplingFrequencyIndex = mp4a_AAC_frequency_table[i][2]; + + /* SBR settings */ + if( sbr_mode != MP4A_AAC_SBR_NOT_SPECIFIED ) + { + /* SBR limitation */ + /* see ISO/IEC 14496-3 Table, 1.5.2.3 Levels within the profiles, 1.11 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_AAC_frequency_table[i][3]; + } + else + { + 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; + 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 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; + 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 ); +} + +/*************************************************************************** + 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->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: + 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, 1.5.2 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..1ba8914 --- /dev/null +++ b/output/mp4/mp4a.h @@ -0,0 +1,121 @@ +/***************************************************************************** + * mp4a.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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 "lsmash.h" +#include "utils.h" + +/*************************************************************************** + MPEG-4 Systems for MPEG-4 Audio +***************************************************************************/ + +/* 14496-3 1.5.2.4 audioProfileLevelIndication Table 1.12 audioProfileLevelIndication values */ +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 ); +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_AAC_frequency_table[13][4]; + +/* 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..629e013 --- /dev/null +++ b/output/mp4/mp4sys.c @@ -0,0 +1,637 @@ +/***************************************************************************** + * mp4sys.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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 "utils.h" + +#include +#include + +#define MP4SYS_INTERNAL +#include "mp4sys.h" +#include "importer.h" /* FIXME: to be replaced with lsmash.h or whatnot */ + +/*************************************************************************** + MPEG-4 Systems +***************************************************************************/ + +/* 8.2.1 Overview Table 1 - 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 3.1.3 Object Descriptors */ + +typedef struct { + uint32_t size; // 2^28 at most + mp4sys_descriptor_tag tag; +} mp4sys_descriptor_head_t; + +/* 8.6.7 DecoderSpecificInfo */ +/* contents varies depends on ObjectTypeIndication and StreamType. */ +typedef struct { + mp4sys_descriptor_head_t header; + uint8_t* data; +} mp4sys_DecoderSpecificInfo_t; + +/* 8.6.6 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=1; /* const bit(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 8.6.20, 8.6.19 ExtensionProfileLevelDescr, 8.7.3.2 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; + +/* 8.6.8 SLConfigDescriptor */ +typedef struct { + mp4sys_descriptor_head_t header; + uint8_t predefined; /* MP4 file that does not use URL_Flag shall have constant value 0x02 */ + /* custom values may be placed here if predefined == 0, but we need not care in MP4 files + without external references */ +} mp4sys_SLConfigDescriptor_t; + +/* 8.6.5 ES_Descriptor */ +typedef struct +{ + mp4sys_descriptor_head_t header; + uint16_t ES_ID; + // uint8_t ESD_flags; /* this 8bit value is always 0 in this muxer, see below for detail */ + /* + typedef struct TAG_ESD_flags{ + 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 + } ESD_flags; + // These are omitted according to ESD_flags == 0 + //if(streamDependenceFlag) + uint16_t dependsOn_ES_ID; // bit(16) + //if(URL_Flag) + uint8_t URLlength; // bit(8) + char URLstring[256]; // bit(8)[], UTF-8 encoded URL + //if(OCR_ES_Flag) + 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 3.1.3 Object Descriptors (ES_ID_Inc) */ +typedef struct { + mp4sys_descriptor_head_t header; + uint32_t Track_ID; +} mp4sys_ES_ID_Inc_t; + +/* 14496-1 8.6.3 ObjectDescriptor / 8.6.4 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*)malloc( sizeof(mp4sys_DecoderSpecificInfo_t) ); + if( !dsi ) + return -1; + memset( dsi, 0, sizeof(mp4sys_DecoderSpecificInfo_t) ); + dsi->header.tag = MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag; + dsi->data = malloc( dsi_payload_length ); + if( !dsi->data ) + { + free( dsi ); + return -1; + } + memcpy( dsi->data, dsi_payload, dsi_payload_length ); + 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*)malloc( sizeof(mp4sys_DecoderConfigDescriptor_t) ); + if( !dcd ) + return -1; + memset( dcd, 0, sizeof(mp4sys_DecoderConfigDescriptor_t) ); + dcd->header.tag = MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag; + dcd->objectTypeIndication = objectTypeIndication; + dcd->streamType = streamType; + 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*)malloc( sizeof(mp4sys_SLConfigDescriptor_t) ); + if( !slcd ) + return -1; + memset( slcd, 0, sizeof(mp4sys_SLConfigDescriptor_t) ); + 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*)malloc( sizeof(mp4sys_ES_Descriptor_t) ); + if( !esd ) + return NULL; + memset( esd, 0, sizeof(mp4sys_ES_Descriptor_t) ); + 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*)malloc( sizeof(mp4sys_ES_ID_Inc_t) ); + if( !es_id_inc ) + return -1; + memset( es_id_inc, 0, sizeof(mp4sys_ES_ID_Inc_t) ); + 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*)malloc( sizeof(mp4sys_ObjectDescriptor_t) ); + if( !od ) + return NULL; + memset( od, 0, sizeof(mp4sys_ObjectDescriptor_t) ); + 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 ) +{ + /* descriptor length will be split into 7bits + see 14496-1 16.3.3 Expandable classes and J.1 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 */ +} + +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; + esd->slConfigDescr->header.size = 1; + return mp4sys_get_descriptor_size( esd->slConfigDescr->header.size ); +} + +uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd ) + return 0; + uint32_t size = 3; + 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 16.3.3 Expandable classes and J.1 Length encoding of descriptors and commands */ + 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 ); + } + lsmash_bs_put_byte( bs, header->size ); + 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 ); + lsmash_bs_put_byte( bs, (dcd->streamType << 2) | 0x01 ); /* contains upStream<1> = 0, reserved<1> = 1 */ + 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 ); + 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 ); + lsmash_bs_put_byte( bs, 0 ); /* streamDependenceFlag<1>, URL_Flag<1>, OCRstreamFlag<1>, streamPriority<5> */ + /* 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 ); +} + +/**** 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..8ba3919 --- /dev/null +++ b/output/mp4/mp4sys.h @@ -0,0 +1,199 @@ +/***************************************************************************** + * mp4sys.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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 + +#include "lsmash.h" + +/*************************************************************************** + MPEG-4 Systems +***************************************************************************/ + +/* 8.6.4.2 Semantics Table 3 - ODProfileLevelIndication Values */ +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; + +/* 8.6.4.2 Semantics Table 4 - sceneProfileLevelIndication Values */ +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 Annex G 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 seeem 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; + +/* 8.6.4.2 Semantics Table 5 - graphicsProfileLevelIndication Values */ +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 +); + +/* 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..5e306f7 --- /dev/null +++ b/output/mp4/summary.c @@ -0,0 +1,103 @@ +/***************************************************************************** + * summary.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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 "summary.h" + +#include +#include + +/*************************************************************************** + summary and AudioSpecificConfig relative tools +***************************************************************************/ + +/* create AudioSpecificConfig as memory block from summary, and set it into that summary itself */ +int mp4sys_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 ); + 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 mp4sys_summary_add_exdata( lsmash_audio_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 = malloc( exdata_length ); + if( !new_exdata ) + return -1; + memcpy( new_exdata, exdata, exdata_length ); + summary->exdata_length = exdata_length; + } + else + summary->exdata_length = 0; + + if( summary->exdata ) + free( summary->exdata ); + summary->exdata = new_exdata; + return 0; +} + +void mp4sys_cleanup_audio_summary( lsmash_audio_summary_t* summary ) +{ + if( !summary ) + return; + if( summary->exdata ) + free( summary->exdata ); + free( summary ); +} + +mp4a_audioProfileLevelIndication mp4sys_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. */ + return mp4a_get_audioProfileLevelIndication( summary ); +} diff --git a/output/mp4/summary.h b/output/mp4/summary.h new file mode 100644 index 0000000..ae8caf5 --- /dev/null +++ b/output/mp4/summary.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * summary.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * 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_SUMMARY_H +#define LSMASH_SUMMARY_H + +#include "lsmash.h" +#include "mp4a.h" +#include "mp4sys.h" + +/* to facilitate to make exdata (typically DecoderSpecificInfo or AudioSpecificConfig). */ +int mp4sys_setup_AudioSpecificConfig( lsmash_audio_summary_t* summary ); +int mp4sys_summary_add_exdata( lsmash_audio_summary_t* summary, void* exdata, uint32_t exdata_length ); + +/* FIXME: these functions may change in the future. + I wonder these functions should be for generic (not limited to audio) summary. */ +void mp4sys_cleanup_audio_summary( lsmash_audio_summary_t* summary ); + +mp4a_audioProfileLevelIndication mp4sys_get_audioProfileLevelIndication( lsmash_audio_summary_t* summary ); + +#endif /* #ifndef LSMASH_SUMMARY_H */ diff --git a/output/mp4/utils.c b/output/mp4/utils.c new file mode 100644 index 0000000..cc76662 --- /dev/null +++ b/output/mp4/utils.c @@ -0,0 +1,532 @@ +/***************************************************************************** + * utils.c: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * 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 +#include + +#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->error ) + return; + uint64_t alloc = size + (1<<16); + uint8_t *data; + if( !bs->data ) + data = malloc( alloc ); + else if( bs->alloc < size ) + data = realloc( bs->data, alloc ); + else + return; + 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 ) + 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) ); +} + +int lsmash_bs_write_data( lsmash_bs_t *bs ) +{ + if( !bs ) + return -1; + if( !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 = malloc( sizeof(lsmash_bs_t) ); + if( !bs ) + return NULL; + memset( bs, 0, sizeof(lsmash_bs_t) ); + 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 = malloc( bs->store ); + if( !buf ) + return NULL; + memcpy( buf, bs->data, bs->store ); + 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; + uint8_t *value = malloc( size ); + if( !value || bs->pos + size > bs->store ) + { + lsmash_bs_free( bs ); + bs->error = 1; + return NULL; + } + memcpy( value, bs->data + bs->pos, size ); + 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_be16( bs ); + return (value<<8) | lsmash_bs_get_byte( 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 ); +} + +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; +} + +#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 ----*/ +lsmash_entry_list_t *lsmash_create_entry_list( void ) +{ + lsmash_entry_list_t *list = malloc( sizeof(lsmash_entry_list_t) ); + if( !list ) + return NULL; + list->entry_count = 0; + list->head = NULL; + list->tail = NULL; + 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 ); + 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; + } + list->entry_count = 0; + list->head = NULL; + list->tail = NULL; +} + +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; + lsmash_entry_t *entry; + for( entry = list->head; entry && --entry_number; entry = entry->next ); + 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; +} diff --git a/output/mp4/utils.h b/output/mp4/utils.h new file mode 100644 index 0000000..4fd7ae1 --- /dev/null +++ b/output/mp4/utils.h @@ -0,0 +1,126 @@ +/***************************************************************************** + * utils.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Yusuke Nakamura + * + * 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 + +#include +#include + +#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 ); +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 ); +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_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 +{ + uint32_t entry_count; + lsmash_entry_t *head; + lsmash_entry_t *tail; +} lsmash_entry_list_t; + +typedef void (*lsmash_entry_data_eliminator)(void* data); /* very same as free() of standard c lib; void free(void *); */ + +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 ); + +#endif diff --git a/x264.c b/x264.c index 92695ab..6a68019 100644 --- a/x264.c +++ b/x264.c @@ -115,9 +115,7 @@ static const char * const muxer_names[] = "raw", "mkv", "flv", -#if HAVE_GPAC "mp4", -#endif 0 }; @@ -362,7 +360,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" @@ -387,11 +388,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" ); @@ -969,9 +965,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 output = mp4_output; param->b_annexb = 0; param->b_repeat_headers = 0; @@ -980,10 +976,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.6.5.1.1367.gcd48