From 95a594ec40acb076727a36729b614eaa5c9f250c Mon Sep 17 00:00:00 2001 From: Anton Mitrofanov Date: Sun, 10 Jul 2011 15:17:56 +0400 Subject: [PATCH] Use L-SMASH for mp4 muxing --- Makefile | 7 +- configure | 31 +- output/mp4.c | 509 ++-- output/mp4/box.h | 1664 +++++++++ output/mp4/importer.c | 1047 ++++++ output/mp4/importer.h | 47 + output/mp4/internal.h | 37 + output/mp4/isom.c | 9218 +++++++++++++++++++++++++++++++++++++++++++++++++ output/mp4/lsmash.h | 1409 ++++++++ output/mp4/mp4a.c | 646 ++++ output/mp4/mp4a.h | 120 + output/mp4/mp4sys.c | 953 +++++ output/mp4/mp4sys.h | 201 ++ output/mp4/summary.c | 105 + output/mp4/utils.c | 664 ++++ output/mp4/utils.h | 142 + x264.c | 20 +- 17 files changed, 16561 insertions(+), 259 deletions(-) create mode 100644 output/mp4/box.h create mode 100644 output/mp4/importer.c create mode 100644 output/mp4/importer.h create mode 100644 output/mp4/internal.h create mode 100644 output/mp4/isom.c create mode 100644 output/mp4/lsmash.h create mode 100644 output/mp4/mp4a.c create mode 100644 output/mp4/mp4a.h create mode 100644 output/mp4/mp4sys.c create mode 100644 output/mp4/mp4sys.h create mode 100644 output/mp4/summary.c create mode 100644 output/mp4/utils.c create mode 100644 output/mp4/utils.h diff --git a/Makefile b/Makefile index 5831091..81d3eb8 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 9ce13bc..e57e17c 100755 --- a/configure +++ b/configure @@ -14,7 +14,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) @@ -87,7 +86,6 @@ icl_ldflags() { arg=${arg/pthreadGC/pthreadVC} [ "$arg" = avifil32.lib ] && arg=vfw32.lib - [ "$arg" = gpac_static.lib ] && arg=libgpac_static.lib [ -n "$arg" ] && echo -n "$arg " done @@ -209,7 +207,6 @@ static="no" avs="auto" lavf="auto" ffms="auto" -gpac="auto" gpl="yes" thread="auto" swscale="auto" @@ -233,7 +230,7 @@ cross_prefix="" EXE="" # list of all preprocessor HAVE values we can define -CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS GPAC GF_MALLOC AVS GPL VECTOREXT INTERLACED" +CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS AVS GPL VECTOREXT INTERLACED" # parse options @@ -282,9 +279,6 @@ for opt do --disable-ffms) ffms="no" ;; - --disable-gpac) - gpac="no" - ;; --disable-gpl) gpl="no" ;; @@ -797,28 +791,6 @@ if [ "$swscale" = "yes" ]; then fi fi -if [ "$gpac" = "auto" ] ; then - gpac="no" - cc_check "" -lz && GPAC_LIBS="-lgpac_static -lz" || GPAC_LIBS="-lgpac_static" - if [ "$SYS" = "WINDOWS" ] ; then - GPAC_LIBS="$GPAC_LIBS -lwinmm" - fi - if cc_check gpac/isomedia.h "$GPAC_LIBS" ; then - if cc_check gpac/isomedia.h "$GPAC_LIBS" "gf_isom_set_pixel_aspect_ratio(0,0,0,0,0);" ; then - gpac="yes" - else - echo "Warning: gpac is too old, update to 2007-06-21 UTC or later" - fi - fi -fi -if [ "$gpac" = "yes" ] ; then - define HAVE_GPAC - if cc_check gpac/isomedia.h "-Werror $GPAC_LIBS" "gf_malloc(1); gf_free(NULL);" ; then - define HAVE_GF_MALLOC - fi - LDFLAGSCLI="$GPAC_LIBS $LDFLAGSCLI" -fi - if [ "$avs" = "auto" ] ; then avs="no" # cygwin can use avisynth if it can use LoadLibrary @@ -1072,7 +1044,6 @@ interlaced: $interlaced avs: $avs lavf: $lavf ffms: $ffms -gpac: $gpac gpl: $gpl thread: $thread filters: $filters diff --git a/output/mp4.c b/output/mp4.c index c0fb685..1b16bb5 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,88 +28,84 @@ *****************************************************************************/ #include "output.h" -#include +#include "mp4/lsmash.h" +#include "mp4/importer.h" + +/*******************/ + +#define MP4_LOG_ERROR( ... ) x264_cli_log( "mp4", X264_LOG_ERROR, __VA_ARGS__ ) +#define MP4_LOG_WARNING( ... ) x264_cli_log( "mp4", X264_LOG_WARNING, __VA_ARGS__ ) +#define MP4_LOG_INFO( ... ) x264_cli_log( "mp4", X264_LOG_INFO, __VA_ARGS__ ) +//#define MP4_RETURN_IF_ERR( cond, ret, ... ) RETURN_IF_ERR( cond, "mp4", ret, __VA_ARGS__ ) +#define MP4_FAIL_IF_ERR( cond, ... ) FAIL_IF_ERR( cond, "mp4", __VA_ARGS__ ) + +/* For close_file() */ +#define MP4_LOG_IF_ERR( cond, ... )\ +if( cond )\ +{\ + MP4_LOG_ERROR( __VA_ARGS__ );\ +} + +/* For open_file() */ +#define MP4_FAIL_IF_ERR_EX( cond, ... )\ +if( cond )\ +{\ + remove_mp4_hnd( p_mp4 );\ + MP4_LOG_ERROR( __VA_ARGS__ );\ + return -1;\ +} -#if HAVE_GF_MALLOC -#undef malloc -#undef free -#undef realloc -#define malloc gf_malloc -#define free gf_free -#define realloc gf_realloc -#endif +/*******************/ typedef struct { - GF_ISOFile *p_file; - GF_AVCConfig *p_config; - GF_ISOSample *p_sample; - int i_track; - uint32_t i_descidx; - uint64_t i_time_res; - int64_t i_time_inc; - int64_t i_delay_time; - int64_t i_init_delta; + lsmash_root_t *p_root; + lsmash_brand_type_code major_brand; + int i_brand_3gpp; + int b_brand_qt; + int b_stdout; + uint32_t i_movie_timescale; + uint32_t i_video_timescale; + uint32_t i_track; + uint32_t i_sample_entry; + uint64_t i_time_inc; + int64_t i_start_offset; + uint64_t i_first_cts; + uint64_t i_prev_dts; + uint32_t i_sei_size; + uint8_t *p_sei_buffer; int i_numframe; + int64_t i_init_delta; int i_delay_frames; int b_dts_compress; int i_dts_compress_multiplier; - int i_data_size; + int b_use_recovery; + int b_no_pasp; + int b_fragments; } mp4_hnd_t; -static void recompute_bitrate_mp4( GF_ISOFile *p_file, int i_track ) -{ - u32 count, di, timescale, time_wnd, rate; - u64 offset; - Double br; - GF_ESD *esd; +/*******************/ - esd = gf_isom_get_esd( p_file, i_track, 1 ); - if( !esd ) +static void remove_mp4_hnd( hnd_t handle ) +{ + mp4_hnd_t *p_mp4 = handle; + if( !p_mp4 ) return; - - esd->decoderConfig->avgBitrate = 0; - esd->decoderConfig->maxBitrate = 0; - rate = time_wnd = 0; - - timescale = gf_isom_get_media_timescale( p_file, i_track ); - count = gf_isom_get_sample_count( p_file, i_track ); - for( u32 i = 0; i < count; i++ ) + if( p_mp4->p_sei_buffer ) { - GF_ISOSample *samp = gf_isom_get_sample_info( p_file, i_track, i+1, &di, &offset ); - if( !samp ) - { - x264_cli_log( "mp4", X264_LOG_ERROR, "failure reading back frame %u\n", i ); - break; - } - - if( esd->decoderConfig->bufferSizeDB < samp->dataLength ) - esd->decoderConfig->bufferSizeDB = samp->dataLength; - - esd->decoderConfig->avgBitrate += samp->dataLength; - rate += samp->dataLength; - if( samp->DTS > time_wnd + timescale ) - { - if( rate > esd->decoderConfig->maxBitrate ) - esd->decoderConfig->maxBitrate = rate; - time_wnd = samp->DTS; - rate = 0; - } - - gf_isom_sample_del( &samp ); + free( p_mp4->p_sei_buffer ); + p_mp4->p_sei_buffer = NULL; } - - br = (Double)(s64)gf_isom_get_media_duration( p_file, i_track ); - br /= timescale; - esd->decoderConfig->avgBitrate = (u32)(esd->decoderConfig->avgBitrate / br); - /*move to bps*/ - esd->decoderConfig->avgBitrate *= 8; - esd->decoderConfig->maxBitrate *= 8; - - gf_isom_change_mpeg4_description( p_file, i_track, 1, esd ); - gf_odf_desc_del( (GF_Descriptor*)esd ); + if( p_mp4->p_root ) + { + lsmash_destroy_root( p_mp4->p_root ); + p_mp4->p_root = NULL; + } + free( p_mp4 ); } +/*******************/ + static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts ) { mp4_hnd_t *p_mp4 = handle; @@ -114,56 +113,47 @@ static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest if( !p_mp4 ) return 0; - if( p_mp4->p_config ) - gf_odf_avc_cfg_del( p_mp4->p_config ); - - if( p_mp4->p_sample ) - { - if( p_mp4->p_sample->data ) - free( p_mp4->p_sample->data ); - - p_mp4->p_sample->dataLength = 0; - gf_isom_sample_del( &p_mp4->p_sample ); - } - - if( p_mp4->p_file ) + if( p_mp4->p_root ) { + double actual_duration = 0; /* FIXME: This may be inside block of "if( p_mp4->i_track )" if audio does not use this. */ if( p_mp4->i_track ) { - /* The mdhd duration is defined as CTS[final] - CTS[0] + duration of last frame. - * The mdhd duration (in seconds) should be able to be longer than the tkhd duration since the track is managed by edts. - * So, if mdhd duration is equal to the last DTS or less, we give the last composition time delta to the last sample duration. - * And then, the mdhd duration is updated, but it time-wise doesn't give the actual duration. - * The tkhd duration is the actual track duration. */ - uint64_t mdhd_duration = (2 * largest_pts - second_largest_pts) * p_mp4->i_time_inc; - if( mdhd_duration != gf_isom_get_media_duration( p_mp4->p_file, p_mp4->i_track ) ) - { - uint64_t last_dts = gf_isom_get_sample_dts( p_mp4->p_file, p_mp4->i_track, p_mp4->i_numframe ); - uint32_t last_duration = (uint32_t)( mdhd_duration > last_dts ? mdhd_duration - last_dts : (largest_pts - second_largest_pts) * p_mp4->i_time_inc ); - gf_isom_set_last_sample_duration( p_mp4->p_file, p_mp4->i_track, last_duration ); - } + /* Flush the rest of samples and add the last sample_delta. */ + uint32_t last_delta = largest_pts - second_largest_pts; + MP4_LOG_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, (last_delta ? last_delta : 1) * p_mp4->i_time_inc ), + "failed to flush the rest of samples.\n" ); + + if( p_mp4->i_movie_timescale != 0 && p_mp4->i_video_timescale != 0 ) /* avoid zero division */ + actual_duration = ((double)((largest_pts + last_delta) * p_mp4->i_time_inc) / p_mp4->i_video_timescale) * p_mp4->i_movie_timescale; + else + MP4_LOG_ERROR( "timescale is broken.\n" ); - /* Write an Edit Box if the first CTS offset is positive. - * A media_time is given by not the mvhd timescale but rather the mdhd timescale. - * The reason is that an Edit Box maps the presentation time-line to the media time-line. - * Any demuxers should follow the Edit Box if it exists. */ - GF_ISOSample *sample = gf_isom_get_sample_info( p_mp4->p_file, p_mp4->i_track, 1, NULL, NULL ); - if( sample && sample->CTS_Offset > 0 ) + if( !p_mp4->b_fragments ) { - uint32_t mvhd_timescale = gf_isom_get_timescale( p_mp4->p_file ); - uint64_t tkhd_duration = (uint64_t)( mdhd_duration * ( (double)mvhd_timescale / p_mp4->i_time_res ) ); - gf_isom_append_edit_segment( p_mp4->p_file, p_mp4->i_track, tkhd_duration, sample->CTS_Offset, GF_ISOM_EDIT_NORMAL ); + /* + * Declare the explicit time-line mapping. + * A segment_duration is given by movie timescale, while a media_time that is the start time of this segment + * is given by not the movie timescale but rather the media timescale. + * The reason is that ISO media have two time-lines, presentation and media time-line, + * and an edit maps the presentation time-line to the media time-line. + * According to QuickTime file format specification and the actual playback in QuickTime Player, + * if the Edit Box doesn't exist in the track, the ratio of the summation of sample durations and track's duration becomes + * the track's media_rate so that the entire media can be used by the track. + * So, we add Edit Box here to avoid this implicit media_rate could distort track's presentation timestamps slightly. + * Note: Any demuxers should follow the Edit List Box if it exists. + */ + MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, actual_duration, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ), + "failed to set timeline map for video.\n" ); } - gf_isom_sample_del( &sample ); - - recompute_bitrate_mp4( p_mp4->p_file, p_mp4->i_track ); + else if( !p_mp4->b_stdout ) + MP4_LOG_IF_ERR( lsmash_modify_timeline_map( p_mp4->p_root, p_mp4->i_track, 1, actual_duration, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ), + "failed to update timeline map for video.\n" ); } - gf_isom_set_pl_indication( p_mp4->p_file, GF_ISOM_PL_VISUAL, 0x15 ); - gf_isom_set_storage_mode( p_mp4->p_file, GF_ISOM_STORE_FLAT ); - gf_isom_close( p_mp4->p_file ); + + MP4_LOG_IF_ERR( lsmash_finish_movie( p_mp4->p_root, NULL ), "failed to finish movie.\n" ); } - free( p_mp4 ); + remove_mp4_hnd( p_mp4 ); /* including lsmash_destroy_root( p_mp4->p_root ); */ return 0; } @@ -173,27 +163,48 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt mp4_hnd_t *p_mp4; *p_handle = NULL; - FILE *fh = fopen( psz_filename, "w" ); - if( !fh ) - return -1; - FAIL_IF_ERR( !x264_is_regular_file( fh ), "mp4", "MP4 output is incompatible with non-regular file `%s'\n", psz_filename ) - fclose( fh ); - if( !(p_mp4 = malloc( sizeof(mp4_hnd_t) )) ) - return -1; + int b_regular = strcmp( psz_filename, "-" ); + b_regular = b_regular && x264_is_regular_file_path( psz_filename ); + if( b_regular ) + { + FILE *fh = fopen( psz_filename, "wb" ); + MP4_FAIL_IF_ERR( !fh, "cannot open output file `%s'.\n", psz_filename ); + b_regular = x264_is_regular_file( fh ); + fclose( fh ); + } + p_mp4 = malloc( sizeof(mp4_hnd_t) ); + MP4_FAIL_IF_ERR( !p_mp4, "failed to allocate memory for muxer information.\n" ); memset( p_mp4, 0, sizeof(mp4_hnd_t) ); - p_mp4->p_file = gf_isom_open( psz_filename, GF_ISOM_OPEN_WRITE, NULL ); p_mp4->b_dts_compress = opt->use_dts_compress; + p_mp4->b_use_recovery = 0; + p_mp4->b_no_pasp = 0; + p_mp4->b_fragments = !b_regular; + p_mp4->b_stdout = !strcmp( psz_filename, "-" ); - if( !(p_mp4->p_sample = gf_isom_sample_new()) ) + char* ext = get_filename_extension( psz_filename ); + if( !strcmp( ext, "mov" ) || !strcmp( ext, "qt" ) ) + { + p_mp4->major_brand = ISOM_BRAND_TYPE_QT; + p_mp4->b_brand_qt = 1; + } + else if( !strcmp( ext, "3gp" ) ) { - close_file( p_mp4, 0, 0 ); - return -1; + p_mp4->major_brand = ISOM_BRAND_TYPE_3GP6; + p_mp4->i_brand_3gpp = 1; } + else if( !strcmp( ext, "3g2" ) ) + { + p_mp4->major_brand = ISOM_BRAND_TYPE_3G2A; + p_mp4->i_brand_3gpp = 2; + } + else + p_mp4->major_brand = ISOM_BRAND_TYPE_MP42; - gf_isom_set_brand_info( p_mp4->p_file, GF_ISOM_BRAND_AVC1, 0 ); + p_mp4->p_root = lsmash_open_movie( psz_filename, p_mp4->b_fragments ? LSMASH_FILE_MODE_WRITE_FRAGMENTED : LSMASH_FILE_MODE_WRITE ); + MP4_FAIL_IF_ERR_EX( !p_mp4->p_root, "failed to create root.\n" ); *p_handle = p_mp4; @@ -203,111 +214,161 @@ static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt static int set_param( hnd_t handle, x264_param_t *p_param ) { mp4_hnd_t *p_mp4 = handle; + uint64_t i_media_timescale; p_mp4->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0; p_mp4->i_dts_compress_multiplier = p_mp4->b_dts_compress * p_mp4->i_delay_frames + 1; - p_mp4->i_time_res = p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier; + i_media_timescale = p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier; p_mp4->i_time_inc = p_param->i_timebase_num * p_mp4->i_dts_compress_multiplier; - FAIL_IF_ERR( p_mp4->i_time_res > UINT32_MAX, "mp4", "MP4 media timescale %"PRIu64" exceeds maximum\n", p_mp4->i_time_res ) - - p_mp4->i_track = gf_isom_new_track( p_mp4->p_file, 0, GF_ISOM_MEDIA_VISUAL, - p_mp4->i_time_res ); - - p_mp4->p_config = gf_odf_avc_cfg_new(); - gf_isom_avc_config_new( p_mp4->p_file, p_mp4->i_track, p_mp4->p_config, - NULL, NULL, &p_mp4->i_descidx ); + MP4_FAIL_IF_ERR( i_media_timescale > UINT32_MAX, "MP4 media timescale %"PRIu64" exceeds maximum\n", i_media_timescale ); - gf_isom_set_track_enabled( p_mp4->p_file, p_mp4->i_track, 1 ); - - gf_isom_set_visual_info( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, - p_param->i_width, p_param->i_height ); + /* Select brands. */ + lsmash_brand_type_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 sample grouping. */ + } + else + { + if( p_mp4->i_brand_3gpp >= 1 ) + brands[brand_count++] = ISOM_BRAND_TYPE_3GP6; + if( p_mp4->i_brand_3gpp == 2 ) + { + brands[brand_count++] = ISOM_BRAND_TYPE_3G2A; + minor_version = 0x00010000; + } + brands[brand_count++] = ISOM_BRAND_TYPE_MP42; + brands[brand_count++] = ISOM_BRAND_TYPE_MP41; + brands[brand_count++] = ISOM_BRAND_TYPE_ISOM; + if( p_mp4->b_use_recovery ) + { + brands[brand_count++] = ISOM_BRAND_TYPE_AVC1; /* sdtp/sgpd/sbgp/random access recovery point grouping */ + if( p_param->b_open_gop ) + { + brands[brand_count++] = ISOM_BRAND_TYPE_ISO6; /* cslg/random access point grouping */ + brands[brand_count++] = ISOM_BRAND_TYPE_QT; /* tapt/cslg/stps/sdtp */ + p_mp4->b_brand_qt = 1; + } + } + } + /* Set movie parameters. */ + lsmash_movie_parameters_t movie_param; + lsmash_initialize_movie_parameters( &movie_param ); + movie_param.major_brand = p_mp4->major_brand; + movie_param.brands = brands; + movie_param.number_of_brands = brand_count; + movie_param.minor_version = minor_version; + MP4_FAIL_IF_ERR( lsmash_set_movie_parameters( p_mp4->p_root, &movie_param ), + "failed to set movie parameters.\n" ); + p_mp4->i_movie_timescale = lsmash_get_movie_timescale( p_mp4->p_root ); + MP4_FAIL_IF_ERR( !p_mp4->i_movie_timescale, "movie timescale is broken.\n" ); + + /* Create a video track. */ + p_mp4->i_track = lsmash_create_track( p_mp4->p_root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ); + MP4_FAIL_IF_ERR( !p_mp4->i_track, "failed to create a video track.\n" ); + + lsmash_video_summary_t summary; + memset( &summary, 0, sizeof(lsmash_video_summary_t) ); + summary.width = p_param->i_width; + summary.height = p_param->i_height; + uint32_t i_display_width = p_param->i_width << 16; + uint32_t i_display_height = p_param->i_height << 16; if( p_param->vui.i_sar_width && p_param->vui.i_sar_height ) { - uint64_t dw = p_param->i_width << 16; - uint64_t dh = p_param->i_height << 16; double sar = (double)p_param->vui.i_sar_width / p_param->vui.i_sar_height; if( sar > 1.0 ) - dw *= sar ; + i_display_width *= sar; else - dh /= sar; - gf_isom_set_pixel_aspect_ratio( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_param->vui.i_sar_width, p_param->vui.i_sar_height ); - gf_isom_set_track_layout_info( p_mp4->p_file, p_mp4->i_track, dw, dh, 0, 0, 0 ); + i_display_height /= sar; + if( !p_mp4->b_no_pasp ) + { + summary.par_h = p_param->vui.i_sar_width; + summary.par_v = p_param->vui.i_sar_height; + if( p_mp4->major_brand != ISOM_BRAND_TYPE_QT ) + summary.scaling_method = ISOM_SCALING_METHOD_MEET; + } } - - p_mp4->i_data_size = p_param->i_width * p_param->i_height * 3 / 2; - p_mp4->p_sample->data = malloc( p_mp4->i_data_size ); - if( !p_mp4->p_sample->data ) + if( p_mp4->b_brand_qt ) { - p_mp4->i_data_size = 0; - return -1; + summary.primaries = p_param->vui.i_colorprim; + summary.transfer = p_param->vui.i_transfer; + summary.matrix = p_param->vui.i_colmatrix; } - return 0; -} - -static int check_buffer( mp4_hnd_t *p_mp4, int needed_size ) -{ - if( needed_size > p_mp4->i_data_size ) + /* Set video track parameters. */ + lsmash_track_parameters_t track_param; + lsmash_initialize_track_parameters( &track_param ); + lsmash_track_mode_code track_mode = ISOM_TRACK_ENABLED | ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW; + if( p_mp4->b_brand_qt ) + track_mode |= QT_TRACK_IN_POSTER; + track_param.mode = track_mode; + track_param.display_width = i_display_width; + track_param.display_height = i_display_height; + track_param.aperture_modes = p_mp4->b_brand_qt && !p_mp4->b_no_pasp; + MP4_FAIL_IF_ERR( lsmash_set_track_parameters( p_mp4->p_root, p_mp4->i_track, &track_param ), + "failed to set track parameters for video.\n" ); + + /* Set video media parameters. */ + lsmash_media_parameters_t media_param; + lsmash_initialize_media_parameters( &media_param ); + media_param.timescale = i_media_timescale; + media_param.media_handler_name = "X264 Video Media Handler"; + if( p_mp4->b_brand_qt ) + media_param.data_handler_name = "X264 URL Data Handler"; + if( p_mp4->b_use_recovery ) { - void *ptr = realloc( p_mp4->p_sample->data, needed_size ); - if( !ptr ) - return -1; - p_mp4->p_sample->data = ptr; - p_mp4->i_data_size = needed_size; + media_param.roll_grouping = p_param->b_intra_refresh; + media_param.rap_grouping = p_param->b_open_gop; } + MP4_FAIL_IF_ERR( lsmash_set_media_parameters( p_mp4->p_root, p_mp4->i_track, &media_param ), + "failed to set media parameters for video.\n" ); + p_mp4->i_video_timescale = lsmash_get_media_timescale( p_mp4->p_root, p_mp4->i_track ); + MP4_FAIL_IF_ERR( !p_mp4->i_video_timescale, "media timescale for video is broken.\n" ); + + /* Add a sample entry. */ + p_mp4->i_sample_entry = lsmash_add_sample_entry( p_mp4->p_root, p_mp4->i_track, ISOM_CODEC_TYPE_AVC1_VIDEO, &summary ); + MP4_FAIL_IF_ERR( !p_mp4->i_sample_entry, + "failed to add sample entry for video.\n" ); + + if( p_mp4->major_brand != ISOM_BRAND_TYPE_QT ) + MP4_FAIL_IF_ERR( lsmash_add_btrt( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry ), + "failed to add btrt.\n" ); + return 0; } static int write_headers( hnd_t handle, x264_nal_t *p_nal ) { mp4_hnd_t *p_mp4 = handle; - GF_AVCConfigSlot *p_slot; - int sps_size = p_nal[0].i_payload - 4; - int pps_size = p_nal[1].i_payload - 4; - int sei_size = p_nal[2].i_payload; + uint32_t sps_size = p_nal[0].i_payload - 4; + uint32_t pps_size = p_nal[1].i_payload - 4; + uint32_t sei_size = p_nal[2].i_payload; uint8_t *sps = p_nal[0].p_payload + 4; uint8_t *pps = p_nal[1].p_payload + 4; uint8_t *sei = p_nal[2].p_payload; - // SPS - - p_mp4->p_config->configurationVersion = 1; - p_mp4->p_config->AVCProfileIndication = sps[1]; - p_mp4->p_config->profile_compatibility = sps[2]; - p_mp4->p_config->AVCLevelIndication = sps[3]; - p_slot = malloc( sizeof(GF_AVCConfigSlot) ); - if( !p_slot ) - return -1; - p_slot->size = sps_size; - p_slot->data = malloc( p_slot->size ); - if( !p_slot->data ) - return -1; - memcpy( p_slot->data, sps, sps_size ); - gf_list_add( p_mp4->p_config->sequenceParameterSets, p_slot ); - - // PPS - - p_slot = malloc( sizeof(GF_AVCConfigSlot) ); - if( !p_slot ) - return -1; - p_slot->size = pps_size; - p_slot->data = malloc( p_slot->size ); - if( !p_slot->data ) - return -1; - memcpy( p_slot->data, pps, pps_size ); - gf_list_add( p_mp4->p_config->pictureParameterSets, p_slot ); - gf_isom_avc_config_update( p_mp4->p_file, p_mp4->i_track, 1, p_mp4->p_config ); - - // SEI - - if( check_buffer( p_mp4, p_mp4->p_sample->dataLength + sei_size ) ) - return -1; - memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, sei, sei_size ); - p_mp4->p_sample->dataLength += sei_size; + MP4_FAIL_IF_ERR( lsmash_set_avc_config( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, 1, sps[1], sps[2], sps[3], 3, 1, X264_BIT_DEPTH-8, X264_BIT_DEPTH-8 ), + "failed to set avc config.\n" ); + /* SPS */ + MP4_FAIL_IF_ERR( lsmash_add_sps_entry( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, sps, sps_size ), + "failed to add sps.\n" ); + /* PPS */ + MP4_FAIL_IF_ERR( lsmash_add_pps_entry( p_mp4->p_root, p_mp4->i_track, p_mp4->i_sample_entry, pps, pps_size ), + "failed to add pps.\n" ); + /* SEI */ + p_mp4->p_sei_buffer = malloc( sei_size ); + MP4_FAIL_IF_ERR( !p_mp4->p_sei_buffer, + "failed to allocate sei transition buffer.\n" ); + memcpy( p_mp4->p_sei_buffer, sei, sei_size ); + p_mp4->i_sei_size = sei_size; return sei_size + sps_size + pps_size; } @@ -315,21 +376,35 @@ static int write_headers( hnd_t handle, x264_nal_t *p_nal ) static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture ) { mp4_hnd_t *p_mp4 = handle; - int64_t dts; - int64_t cts; - - if( check_buffer( p_mp4, p_mp4->p_sample->dataLength + i_size ) ) - return -1; - memcpy( p_mp4->p_sample->data + p_mp4->p_sample->dataLength, p_nalu, i_size ); - p_mp4->p_sample->dataLength += i_size; + uint64_t dts, cts; if( !p_mp4->i_numframe ) - p_mp4->i_delay_time = p_picture->i_dts * -1; + { + p_mp4->i_start_offset = p_picture->i_dts * -1; + p_mp4->i_first_cts = p_mp4->b_dts_compress ? 0 : p_mp4->i_start_offset * p_mp4->i_time_inc; + if( p_mp4->b_fragments ) + MP4_LOG_IF_ERR( lsmash_create_explicit_timeline_map( p_mp4->p_root, p_mp4->i_track, UINT32_MAX, p_mp4->i_first_cts, ISOM_EDIT_MODE_NORMAL ), + "failed to set timeline map for video.\n" ); + } + + lsmash_sample_t *p_sample = lsmash_create_sample( i_size + p_mp4->i_sei_size ); + MP4_FAIL_IF_ERR( !p_sample, + "failed to create a video sample data.\n" ); + + if( p_mp4->p_sei_buffer ) + { + memcpy( p_sample->data, p_mp4->p_sei_buffer, p_mp4->i_sei_size ); + free( p_mp4->p_sei_buffer ); + p_mp4->p_sei_buffer = NULL; + } + + memcpy( p_sample->data + p_mp4->i_sei_size, p_nalu, i_size ); + p_mp4->i_sei_size = 0; if( p_mp4->b_dts_compress ) { if( p_mp4->i_numframe == 1 ) - p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc; + p_mp4->i_init_delta = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc; dts = p_mp4->i_numframe > p_mp4->i_delay_frames ? p_picture->i_dts * p_mp4->i_time_inc : p_mp4->i_numframe * (p_mp4->i_init_delta / p_mp4->i_dts_compress_multiplier); @@ -337,16 +412,28 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_ } else { - dts = (p_picture->i_dts + p_mp4->i_delay_time) * p_mp4->i_time_inc; - cts = (p_picture->i_pts + p_mp4->i_delay_time) * p_mp4->i_time_inc; + dts = (p_picture->i_dts + p_mp4->i_start_offset) * p_mp4->i_time_inc; + cts = (p_picture->i_pts + p_mp4->i_start_offset) * p_mp4->i_time_inc; + } + + p_sample->dts = dts; + p_sample->cts = cts; + p_sample->index = p_mp4->i_sample_entry; + p_sample->prop.random_access_type = p_picture->b_keyframe ? ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP : ISOM_SAMPLE_RANDOM_ACCESS_TYPE_NONE; + + if( p_mp4->b_fragments && p_mp4->i_numframe && p_sample->prop.random_access_type ) + { + MP4_FAIL_IF_ERR( lsmash_flush_pooled_samples( p_mp4->p_root, p_mp4->i_track, p_sample->dts - p_mp4->i_prev_dts ), + "failed to flush the rest of samples.\n" ); + MP4_FAIL_IF_ERR( lsmash_create_fragment_movie( p_mp4->p_root ), + "failed to create a movie fragment.\n" ); } - p_mp4->p_sample->IsRAP = p_picture->b_keyframe; - p_mp4->p_sample->DTS = dts; - p_mp4->p_sample->CTS_Offset = (uint32_t)(cts - dts); - gf_isom_add_sample( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_mp4->p_sample ); + /* Append data per sample. */ + MP4_FAIL_IF_ERR( lsmash_append_sample( p_mp4->p_root, p_mp4->i_track, p_sample ), + "failed to append a video frame.\n" ); - p_mp4->p_sample->dataLength = 0; + p_mp4->i_prev_dts = dts; p_mp4->i_numframe++; return i_size; diff --git a/output/mp4/box.h b/output/mp4/box.h new file mode 100644 index 0000000..1a6a86c --- /dev/null +++ b/output/mp4/box.h @@ -0,0 +1,1664 @@ +/***************************************************************************** + * 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 "utils.h" + +typedef struct isom_box_tag isom_box_t; + +/* If size is 1, then largesize is actual size. + * If size is 0, then this box is the last one in the file. + * usertype is for uuid. */ +#define ISOM_BASEBOX_COMMON \ + lsmash_root_t *root; /* pointer of root */ \ + isom_box_t *parent; /* pointer of the parent box of this box */ \ + uint8_t manager; /* flags for L-SMASH */ \ + uint64_t pos; /* starting position of this box in the file */ \ + uint64_t size; /* the number of bytes in this box */ \ + uint32_t type; /* four characters codes that identify box type */ \ + uint8_t *usertype + +#define ISOM_FULLBOX_COMMON \ + ISOM_BASEBOX_COMMON; \ + uint8_t version; /* Basically, version is either 0 or 1 */ \ + uint32_t flags /* In the actual structure of box, flags is 24 bits. */ + +#define ISOM_DEFAULT_BOX_HEADER_SIZE 8 +#define ISOM_DEFAULT_FULLBOX_HEADER_SIZE 12 +#define ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE 16 + +#define LSMASH_UNKNOWN_BOX 0x01 +#define LSMASH_ABSENT_IN_ROOT 0x02 + +struct isom_box_tag +{ + ISOM_FULLBOX_COMMON; +}; + +/* File Type Box + * This box identifies the specifications to which this file complies. + * This box shall occur before any variable-length box. + * In the absence of this box, the file is QuickTime file format or MP4 version 1 file format. + * In MP4 version 1 file format, Object Descriptor Box is mandatory. + * In QuickTime file format, Object Descriptor Box isn't defined. + * Therefore, if this box and an Object Descriptor Box are absent in the file, the file shall be QuikcTime file format. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t major_brand; /* brand identifier */ + uint32_t minor_version; /* the minor version of the major brand */ + uint32_t *compatible_brands; /* a list, to the end of the box, of brands */ + + uint32_t brand_count; /* the number of factors in compatible_brands array */ +} isom_ftyp_t; + +/* Track Header Box + * This box specifies the characteristics of a single track. */ +typedef struct +{ + /* version is either 0 or 1 + * flags + * 0x000001: Indicates that the track is enabled. + * A disabled track is treated as if it were not present. + * 0x000002: Indicates that the track is used in the presentation. + * 0x000004: Indicates that the track is used when previewing the presentation. + * 0x000008: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format) + * ISOM: If in a presentation all tracks have neither track_in_movie nor track_in_preview set, + * then all tracks shall be treated as if both flags were set on all tracks. */ + ISOM_FULLBOX_COMMON; + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t track_ID; /* an integer that uniquely identifies the track + * Track IDs are never re-used and cannot be zero. */ + uint32_t reserved1; + uint64_t duration; /* the duration of this track expressed in the movie timescale units */ + /* The following fields are treated as reserved in MP4 version 1. */ + uint32_t reserved2[2]; + int16_t layer; /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */ + int16_t alternate_group; /* an integer that specifies a group or collection of tracks + * If this field is not 0, it should be the same for tracks that contain alternate data for one another + * and different for tracks belonging to different such groups. + * Only one track within an alternate group should be played or streamed at any one time. */ + int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ + uint16_t reserved3; + int32_t matrix[9]; /* transformation matrix for the video */ + /* track's visual presentation size + * All images in the sequence are scaled to this size, before any overall transformation of the track represented by the matrix. + * Note: these fields are treated as reserved in MP4 version 1. */ + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ + /* */ +} isom_tkhd_t; + +/* Track Clean Aperture Dimensions Box + * A presentation mode where clap and pasp are reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_clef_t; + +/* Track Production Aperture Dimensions Box + * A presentation mode where pasp is reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_prof_t; + +/* Track Encoded Pixels Dimensions Box + * A presentation mode where clap and pasp are not reflected. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t width; /* fixed point 16.16 number */ + uint32_t height; /* fixed point 16.16 number */ +} isom_enof_t; + +/* Track Aperture Mode Dimensions Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_clef_t *clef; /* Track Clean Aperture Dimensions Box */ + isom_prof_t *prof; /* Track Production Aperture Dimensions Box */ + isom_enof_t *enof; /* Track Encoded Pixels Dimensions Box */ +} isom_tapt_t; + +/* Edit List Box + * This box contains an explicit timeline map. + * Each entry defines part of the track timeline: by mapping part of the media timeline, or by indicating 'empty' time, + * or by defining a 'dwell', where a single time-point in the media is held for a period. + * The last edit in a track shall never be an empty edit. + * Any difference between the duration in the Movie Header Box, and the track's duration is expressed as an implicit empty edit at the end. + * It is recommended that any edits, explicit or implied, not select any portion of the composition timeline that doesn't map to a sample. + * Therefore, if the first sample in the track has non-zero CTS, then this track should have at least one edit and the start time in it should + * correspond to the value of the CTS the first sample has or more not to exceed the largest CTS in this track. */ +typedef struct +{ + /* version == 0: 64bits -> 32bits */ + uint64_t segment_duration; /* the duration of this edit expressed in the movie timescale units */ + int64_t media_time; /* the starting composition time within the media of this edit segment + * If this field is set to -1, it is an empty edit. */ + int32_t media_rate; /* the relative rate at which to play the media corresponding to this edit segment + * If this value is 0, then the edit is specifying a 'dwell': + * the media at media_time is presented for the segment_duration. + * This field is expressed as 16.16 fixed-point number. */ +} isom_elst_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + lsmash_entry_list_t *list; +} isom_elst_t; + +/* Edit Box + * This optional box maps the presentation time-line to the media time-line as it is stored in the file. + * In the absence of this box, there is an implicit one-to-one mapping of these time-lines, + * and the presentation of a track starts at the beginning of the presentation. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_elst_t *elst; /* Edit List Box */ +} isom_edts_t; + +/* Track Reference Box + * The Track Reference Box contains Track Reference Type Boxes. + * Track Reference Type Boxes define relationships between tracks. + * They allow one track to specify how it is related to other tracks. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t *track_ID; /* track_IDs of reference tracks / Zero value must not be used */ + + uint32_t ref_count; /* number of reference tracks */ +} isom_tref_type_t; + +typedef struct +{ + ISOM_BASEBOX_COMMON; + lsmash_entry_list_t *ref_list; /* Track Reference Type Boxes */ +} isom_tref_t; + +/* Media Header Box + * This box declares overall information that is media-independent, and relevant to characteristics of the media in a track.*/ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of the media in this track (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the media in this track was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t timescale; /* media timescale: timescale for this media */ + uint64_t duration; /* the duration of this media expressed in the timescale indicated in this box */ + /* */ +#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 and (ISOM: should/QTFF: must) come before Media Information Box. + * ISOM: this box might be also in Meta Box. + * QTFF: this box might be also in Media Information Box. If this box is present there, it must come before Data Information Box. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t componentType; /* ISOM: pre_difined = 0 + * QTFF: 'mhlr' for Media Handler Reference Box and 'dhlr' for Data Handler Reference Box */ + uint32_t componentSubtype; /* 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 + ** There is a different media information header for each track type + ** (corresponding to the media handler-type); the matching header shall be present. **/ +/* Video Media Header Box + * This box contains general presentation information, independent of the coding, for video media. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* flags is 1 */ + uint16_t graphicsmode; /* template: graphicsmode = 0 */ + uint16_t opcolor[3]; /* template: opcolor = { 0, 0, 0 } */ +} isom_vmhd_t; + +/* Sound Media Header Box + * This box contains general presentation information, independent of the coding, for audio media. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + int16_t balance; /* a fixed-point 8.8 number that places mono audio tracks in a stereo space. template: balance = 0 */ + uint16_t reserved; +} isom_smhd_t; + +/* Hint Media Header Box + * This box contains general information, independent of the protocol, for hint tracks. (A PDU is a Protocol Data Unit.) */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t maxPDUsize; /* the size in bytes of the largest PDU in this (hint) stream */ + uint16_t avgPDUsize; /* the average size of a PDU over the entire presentation */ + uint32_t maxbitrate; /* the maximum rate in bits/second over any window of one second */ + uint32_t avgbitrate; /* the average rate in bits/second over the entire presentation */ + uint32_t reserved; +} isom_hmhd_t; + +/* Null Media Header Box + * This box may be used for streams other than visual and audio (e.g., timed metadata streams). */ +typedef struct +{ + /* Streams other than visual and audio may use a Null Media Header Box */ + ISOM_FULLBOX_COMMON; /* flags is currently all zero */ +} isom_nmhd_t; + +/* Generic Media Information Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint16_t graphicsmode; + uint16_t opcolor[3]; + int16_t balance; /* This field is nomally set to 0. */ + uint16_t reserved; /* Reserved for use by Apple. Set this field to 0. */ +} isom_gmin_t; + +/* Text Media Information Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + int32_t matrix[9]; /* Unkown fields. Default values are probably: + * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ +} isom_text_t; + +/* Generic Media Information Header Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_gmin_t *gmin; /* Generic Media Information Box */ + isom_text_t *text; /* Text Media Information Box */ +} isom_gmhd_t; +/** **/ + +/* Data Reference Box + * name and location fields are expressed in null-terminated string using UTF-8 characters. */ +typedef struct +{ + /* This box is DataEntryUrlBox or DataEntryUrnBox */ + ISOM_FULLBOX_COMMON; /* flags == 0x000001 means that the media data is in the same file + * as the Movie Box containing this data reference. */ + char *name; /* only for DataEntryUrnBox */ + char *location; /* a location to find the resource with the given name */ + + uint32_t name_length; + uint32_t location_length; +} isom_dref_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_dref_t; + +/* Data Information Box */ +typedef struct +{ + /* This box is in Media Information Box or Meta Box */ + ISOM_BASEBOX_COMMON; + isom_dref_t *dref; /* Data Reference Box */ +} isom_dinf_t; + +/** Sample Description **/ +/* ES Descriptor Box */ +struct mp4sys_ES_Descriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + struct mp4sys_ES_Descriptor_t *ES; +} isom_esds_t; + +/* AVCDecoderConfigurationRecord */ +typedef struct +{ +#define ISOM_REQUIRES_AVCC_EXTENSION( x ) ((x) == 100 || (x) == 110 || (x) == 122 || (x) == 144) + ISOM_BASEBOX_COMMON; + uint8_t configurationVersion; /* 1 */ + uint8_t AVCProfileIndication; /* profile_idc in SPS */ + uint8_t profile_compatibility; + uint8_t AVCLevelIndication; /* level_idc in SPS */ + uint8_t lengthSizeMinusOne; /* in bytes of the NALUnitLength field. upper 6-bits are reserved as 111111b */ + uint8_t numOfSequenceParameterSets; /* upper 3-bits are reserved as 111b */ + lsmash_entry_list_t *sequenceParameterSets; /* SPSs */ + uint8_t numOfPictureParameterSets; + lsmash_entry_list_t *pictureParameterSets; /* PPSs */ + /* if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) ) */ + uint8_t chroma_format; /* chroma_format_idc in SPS / upper 6-bits are reserved as 111111b */ + uint8_t bit_depth_luma_minus8; /* shall be in the range of 0 to 4 / upper 5-bits are reserved as 11111b */ + uint8_t bit_depth_chroma_minus8; /* shall be in the range of 0 to 4 / upper 5-bits are reserved as 11111b */ + uint8_t numOfSequenceParameterSetExt; + lsmash_entry_list_t *sequenceParameterSetExt; /* SPSExts */ + /* */ +} isom_avcC_t; + +/* Parameter Set Entry */ +typedef struct +{ + uint16_t parameterSetLength; + uint8_t *parameterSetNALUnit; +} isom_avcC_ps_entry_t; + +/* MPEG-4 Bit Rate Box + * This box signals the bit rate information of the AVC video stream. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t bufferSizeDB; /* the size of the decoding buffer for the elementary stream in bytes */ + uint32_t maxBitrate; /* the maximum rate in bits/second over any window of one second */ + uint32_t avgBitrate; /* the average rate in bits/second over the entire presentation */ +} isom_btrt_t; + +/* Clean Aperture Box + * There are notionally four values in this box and these parameters are represented as a fraction N/D. + * Here, we refer to the pair of parameters fooN and fooD as foo. + * Considering the pixel dimensions as defined by the VisualSampleEntry width and height. + * If picture centre of the image is at pcX and pcY, then horizOff and vertOff are defined as follows: + * pcX = horizOff + (width - 1)/2; + * pcY = vertOff + (height - 1)/2; + * The leftmost/rightmost pixel and the topmost/bottommost line of the clean aperture fall at: + * pcX +/- (cleanApertureWidth - 1)/2; + * pcY +/- (cleanApertureHeight - 1)/2; */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t cleanApertureWidthN; + uint32_t cleanApertureWidthD; + uint32_t cleanApertureHeightN; + uint32_t cleanApertureHeightD; + int32_t horizOffN; + uint32_t horizOffD; + int32_t vertOffN; + uint32_t vertOffD; +} isom_clap_t; + +/* Pixel Aspect Ratio Box + * This box specifies the aspect ratio of a pixel, in arbitrary units. + * If a pixel appears H wide and V tall, then hSpacing/vSpacing is equal to H/V. + * When adjusting pixel aspect ratio, normally, the horizontal dimension of the video is scaled, if needed. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t hSpacing; /* horizontal spacing */ + uint32_t vSpacing; /* vertical spacing */ +} isom_pasp_t; + +/* Color Parameter Box + * This box is used to map the numerical values of pixels in the file to a common representation of color + * in which images can be correctly compared, combined, and displayed. + * This box is defined in QuickTime file format. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t color_parameter_type; /* 'nclc' or 'prof' */ + /* for 'nclc' */ + uint16_t primaries_index; /* CIE 1931 xy chromaticity coordinates */ + uint16_t transfer_function_index; /* nonlinear transfer function from RGB to ErEgEb */ + uint16_t matrix_index; /* matrix from ErEgEb to EyEcbEcr */ +} isom_colr_t; + +/* Sample Scale Box + * If this box is present and can be interpreted by the decoder, + * all samples shall be displayed according to the scaling behaviour that is specified in this box. + * Otherwise, all samples are scaled to the size that is indicated by the width and height field in the Track Header Box. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint8_t constraint_flag; /* Upper 7-bits are reserved. + * If this flag is set, all samples described by this sample entry shall be scaled + * according to the method specified by the field 'scale_method'. */ + uint8_t scale_method; /* The semantics of the values for scale_method are as specified for the 'fit' attribute of regions in SMIL 1.0. */ + int16_t display_center_x; + int16_t display_center_y; +} isom_stsl_t; + +/* Sample Entry */ +#define ISOM_SAMPLE_ENTRY \ + ISOM_BASEBOX_COMMON; \ + uint8_t reserved[6]; \ + uint16_t data_reference_index; + +typedef struct +{ + ISOM_SAMPLE_ENTRY; +} isom_sample_entry_t; + +/* Mpeg Sample Entry */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + isom_esds_t *esds; /* ES Descriptor Box */ +} isom_mp4s_entry_t; + +/* ISOM: Visual Sample Entry / QTFF: Image Description */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + int16_t version; /* ISOM: pre_defined / QTFF: sample description version */ + int16_t revision_level; /* ISOM: reserved / QTFF: version of the CODEC */ + int32_t vendor; /* ISOM: pre_defined / QTFF: whose CODEC */ + uint32_t temporalQuality; /* ISOM: pre_defined / QTFF: the temporal quality factor */ + uint32_t spatialQuality; /* ISOM: pre_defined / QTFF: the spatial quality factor */ + /* The width and height are the maximum pixel counts that the codec will deliver. + * Since these are counts they do not take into account pixel aspect ratio. */ + uint16_t width; + uint16_t height; + /* */ + uint32_t horizresolution; /* 16.16 fixed-point / template: horizresolution = 0x00480000 / 72 dpi */ + uint32_t vertresolution; /* 16.16 fixed-point / template: vertresolution = 0x00480000 / 72 dpi */ + uint32_t dataSize; /* ISOM: reserved / QTFF: if known, the size of data for this descriptor */ + uint16_t frame_count; /* frame per sample / template: frame_count = 1 */ + char compressorname[33]; /* a fixed 32-byte field, with the first byte set to the number of bytes to be displayed */ + uint16_t depth; /* ISOM: template: depth = 0x0018 + * AVC : 0x0018: colour with no alpha + * 0x0028: grayscale with no alpha + * 0x0020: gray or colour with alpha + * QTFF: depth of this data (1-32) or (33-40 grayscale) */ + int16_t color_table_ID; /* ISOM: template: pre_defined = -1 + * QTFF: color table ID + * If this field is set to 0, the default color table should be used for the specified depth + * If the color table ID is set to 0, a color table is contained within the sample description itself. + * The color table immediately follows the color table ID field. */ + /* common extensions */ + 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 */ + /* MP4 specific extension */ + isom_esds_t *esds; /* ES Descriptor Box */ + /* AVC specific extensions */ + isom_avcC_t *avcC; /* AVCDecoderConfigurationRecord */ + isom_btrt_t *btrt; /* MPEG-4 Bit Rate Box @ optional */ +} isom_visual_entry_t; + +/* Format Box + * This box shows the data format of the stored sound media. + * ISO base media file format also defines the same four-character-code for the type field, + * however, that is used to indicate original sample description of the media when a protected sample entry is used. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t data_format; /* copy of sample description type */ +} isom_frma_t; + +/* Audio Endian Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + int16_t littleEndian; +} isom_enda_t; + +/* MPEG-4 Audio Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + uint32_t unknown; /* always 0? */ +} isom_mp4a_t; + +/* Terminator Box + * This box is present to indicate the end of the sound description. It contains no data. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; /* size = 8, type = 0x00000000 */ +} isom_terminator_t; + +/* Sound Information Decompression Parameters Box + * This box provides the ability to store data specific to a given audio decompressor in the sound description. + * The contents of this box are dependent on the audio decompressor. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_frma_t *frma; /* Format Box */ + isom_enda_t *enda; /* Audio Endian Box */ + isom_mp4a_t *mp4a; /* MPEG-4 Audio Box */ + isom_esds_t *esds; /* ES Descriptor Box */ + isom_terminator_t *terminator; /* Terminator Box */ + + uint32_t exdata_length; + void *exdata; +} isom_wave_t; + +/* Channel Compositor Box */ +typedef struct +{ + uint32_t channelLabel; /* the channelLabel that describes the channel */ + uint32_t channelFlags; /* flags that control the interpretation of coordinates */ + uint32_t coordinates[3]; /* an ordered triple that specifies a precise speaker location / 32-bit floating point */ +} isom_channel_description_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t channelLayoutTag; /* the channelLayoutTag indicates the layout */ + uint32_t channelBitmap; /* If channelLayoutTag is set to 0x00010000, this field is the channel usage bitmap. */ + uint32_t numberChannelDescriptions; /* the number of items in the Channel Descriptions array */ + /* Channel Descriptions array */ + isom_channel_description_t *channelDescriptions; +} isom_chan_t; + +/* ISOM: Audio Sample Entry / QTFF: Sound Description */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + int16_t version; /* ISOM: reserved + * QTFF: sample description version + * version = 0 supports only 'raw ' or 'twos' audio format. + * version = 1 is used to support out-of-band configuration settings for decompression. + * version = 2 is used to support high samplerate or 3 or more multichannel audio. */ + int16_t revision_level; /* ISOM: reserved / QTFF: version of the CODEC */ + int32_t vendor; /* ISOM: reserved / QTFF: whose CODEC */ + uint16_t channelcount; /* ISOM: template: channelcount = 2 + * QTFF: the number of audio channels + * Allowable values are 1 (mono) or 2 (stereo). + * For more than 2, set this field to 3 and use numAudioChannels instead of this field. */ + uint16_t samplesize; /* ISOM: template: samplesize = 16 + * QTFF: the number of bits in each uncompressed sample for a single channel + * Allowable values are 8 or 16. + * For non-mod8, set this field to 16 and use constBitsPerChannel instead of this field. + * For more than 16, set this field to 16 and use bytesPerPacket instead of this field. */ + int16_t compression_ID; /* ISOM: pre_defined + * QTFF: version = 0 -> must be set to 0. + * version = 2 -> must be set to -2. */ + uint16_t packet_size; /* ISOM: reserved / QTFF: must be set to 0. */ + uint32_t samplerate; /* the sampling rate expressed as a 16.16 fixed-point number + * ISOM: template: samplerate = {default samplerate of media}<<16 + * QTFF: the integer portion should match the media's timescale. + * If this field is invalid because of higher samplerate, + * then set this field to 0x00010000 and use audioSampleRate instead of this field. */ + /* version 1 fields + * These fields are for description of the compression ratio of fixed ratio audio compression algorithms. + * If these fields are not used, they are set to 0. */ + uint32_t samplesPerPacket; /* For compressed audio, be set to the number of uncompressed frames generated by a compressed frame. + * For uncompressed audio, shall be set to 1. */ + uint32_t bytesPerPacket; /* the number of bytes in a sample for a single channel */ + uint32_t bytesPerFrame; /* the number of bytes in a frame */ + uint32_t bytesPerSample; /* 8-bit audio: 1, other audio: 2 */ + /* version 2 fields + * LPCMFrame: one sample from each channel. + * AudioPacket: For uncompressed audio, an AudioPacket is simply one LPCMFrame. + * For compressed audio, an AudioPacket is the natural compressed access unit of that format. */ + uint32_t sizeOfStructOnly; /* offset to extensions */ + uint64_t audioSampleRate; /* 64-bit floating point */ + uint32_t numAudioChannels; /* any channel assignment info will be in Channel Compositor Box. */ + int32_t always7F000000; /* always 0x7F000000 */ + uint32_t constBitsPerChannel; /* only set if constant (and only for uncompressed audio) */ + uint32_t formatSpecificFlags; + uint32_t constBytesPerAudioPacket; /* only set if constant */ + uint32_t constLPCMFramesPerAudioPacket; /* only set if constant */ + /* extensions */ + isom_esds_t *esds; /* ISOM: ES Descriptor Box / QTFF: null */ + isom_wave_t *wave; /* ISOM: null / QTFF: Sound Information Decompression Parameters Box */ + isom_chan_t *chan; /* ISOM: null / QTFF: Channel Compositor Box @ optional */ + + uint32_t exdata_length; + void *exdata; + lsmash_audio_summary_t summary; +} isom_audio_entry_t; + +/* Hint Sample Entry */ +#define ISOM_HINT_SAMPLE_ENTRY \ + ISOM_SAMPLE_ENTRY; \ + uint8_t *data; + +typedef struct +{ + ISOM_HINT_SAMPLE_ENTRY; + uint32_t data_length; +} isom_hint_entry_t; + +/* Metadata Sample Entry */ +#define ISOM_METADATA_SAMPLE_ENTRY \ + ISOM_SAMPLE_ENTRY; + +typedef struct +{ + ISOM_METADATA_SAMPLE_ENTRY; +} isom_metadata_entry_t; + +/* QuickTime Text Sample Description */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + int32_t displayFlags; + int32_t textJustification; + uint16_t bgColor[3]; /* background RGB color */ + /* defaultTextBox */ + int16_t top; + int16_t left; + int16_t bottom; + int16_t right; + /* defaultStyle */ + int32_t scrpStartChar; /* starting character position */ + int16_t scrpHeight; + int16_t scrpAscent; + int16_t scrpFont; + uint16_t scrpFace; /* only first 8-bits are used */ + int16_t scrpSize; + uint16_t scrpColor[3]; /* foreground RGB color */ + /* defaultFontName is Pascal string */ + uint8_t font_name_length; + char *font_name; +} isom_text_entry_t; + +/* FontRecord */ +typedef struct +{ + uint16_t font_ID; + /* Pascal string */ + uint8_t font_name_length; + char *font_name; +} isom_font_record_t; + +/* Font Table Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + /* FontRecord + * entry_count is uint16_t. */ + lsmash_entry_list_t *list; +} isom_ftab_t; + +/* Timed Text Sample Entry */ +typedef struct +{ + ISOM_SAMPLE_ENTRY; + uint32_t displayFlags; + int8_t horizontal_justification; + int8_t vertical_justification; + uint8_t background_color_rgba[4]; + /* BoxRecord default_text_box */ + int16_t top; + int16_t left; + int16_t bottom; + int16_t right; + /* StyleRecord default_style */ + uint16_t startChar; /* always 0 */ + uint16_t endChar; /* always 0 */ + uint16_t font_ID; + uint8_t face_style_flags; + uint8_t font_size; + uint8_t text_color_rgba[4]; + /* Font Table Box font_table */ + isom_ftab_t *ftab; +} isom_tx3g_entry_t; + +/* Sample Description Box */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_stsd_t; +/** **/ + +/* Decoding Time to Sample Box + * This box contains a compact version of a table that allows indexing from decoding time to sample number. + * Each entry in the table gives the number of consecutive samples with the same time delta, and the delta of those samples. + * By adding the deltas a complete time-to-sample map may be built. + * All samples must have non-zero durations except for the last one. + * The sum of all deltas gives the media duration in the track (not mapped to the movie timescale, and not considering any edit list). + * DTS is an abbreviation of 'decoding time stamp'. */ +typedef struct +{ + uint32_t sample_count; /* number of consecutive samples that have the given sample_delta */ + uint32_t sample_delta; /* DTS[0] = 0; DTS[n+1] = DTS[n] + sample_delta[n]; */ +} isom_stts_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_stts_t; + +/* Composition Time to Sample Box + * This box provides the offset between decoding time and composition time. + * CTS is an abbreviation of 'composition time stamp'. + * This box is optional and must only be present if DTS and CTS differ for any samples. + * ISOM: if version is set to 1, sample_offset is signed 32-bit integer. + * QTFF: sample_offset is always signed 32-bit integer. */ +typedef struct +{ + uint32_t sample_count; /* number of consecutive samples that have the given sample_offset */ + uint32_t sample_offset; /* CTS[n] = DTS[n] + sample_offset[n]; */ +} isom_ctts_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_ctts_t; + +/* Composition to Decode Box (Composition Shift Least Greatest Box) + * This box may be used to relate the composition and decoding timelines, + * and deal with some of the ambiguities that signed composition offsets introduce. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + int32_t compositionToDTSShift; /* If this value is added to the composition times (as calculated by the CTS offsets from the DTS), + * then for all samples, their CTS is guaranteed to be greater than or equal to their DTS, + * and the buffer model implied by the indicated profile/level will be honoured; + * if leastDecodeToDisplayDelta is positive or zero, this field can be 0; + * otherwise it should be at least (- leastDecodeToDisplayDelta). */ + int32_t leastDecodeToDisplayDelta; /* the smallest sample_offset in this track */ + int32_t greatestDecodeToDisplayDelta; /* the largest sample_offset in this track */ + int32_t compositionStartTime; /* the smallest CTS for any sample */ + int32_t compositionEndTime; /* the CTS plus the composition duration, of the sample with the largest CTS in this track */ +} isom_cslg_t; + +/* Sample Size Box + * This box contains the sample count and a table giving the size in bytes of each sample. + * The total number of samples in the media is always indicated in the sample_count. + * Note: a sample size of zero is not prohibited in general, but it must be valid and defined for the coding system, + * as defined by the sample entry, that the sample belongs to. */ +typedef struct +{ + uint32_t entry_size; /* the size of a sample */ +} isom_stsz_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t sample_size; /* If this field is set to 0, then the samples have different sizes. */ + uint32_t sample_count; /* the number of samples in the track */ + lsmash_entry_list_t *list; /* available if sample_size == 0 */ +} isom_stsz_t; + +/* Sync Sample Box + * If this box is not present, every sample is a random access point. + * In AVC streams, this box cannot point non-IDR samples. + * The table is arranged in strictly increasing order of sample number. */ +typedef struct +{ + uint32_t sample_number; /* the numbers of the samples that are random access points in the stream. */ +} isom_stss_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_stss_t; + +/* Partial Sync Sample Box + * Tip from QT engineering - Open-GOP intra frames need to be marked as "partial sync samples". + * Partial sync frames perform a partial reset of inter-frame dependencies; + * decoding two partial sync frames and the non-droppable difference frames between them is + * sufficient to prepare a decompressor for correctly decoding the difference frames that follow. */ +typedef struct +{ + uint32_t sample_number; /* the numbers of the samples that are partial sync samples in the stream. */ +} isom_stps_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_stps_t; + +/* Independent and Disposable Samples Box */ +typedef struct +{ + unsigned is_leading : 2; /* ISOM: leading / QTFF: samples later in decode order may have earlier display times */ + unsigned sample_depends_on : 2; /* independency */ + unsigned sample_is_depended_on : 2; /* disposable */ + unsigned sample_has_redundancy : 2; /* redundancy */ +} isom_sdtp_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + /* According to the specification, the size of the table, sample_count, doesn't exist in this box. + * Instead of this, it is taken from the sample_count in the stsz or the stz2 box. */ + lsmash_entry_list_t *list; +} isom_sdtp_t; + +/* Sample To Chunk Box + * This box can be used to find the chunk that contains a sample, its position, and the associated sample description. + * The table is compactly coded. Each entry gives the index of the first chunk of a run of chunks with the same characteristics. + * By subtracting one entry here from the previous one, you can compute how many chunks are in this run. + * You can convert this to a sample count by multiplying by the appropriate samples_per_chunk. */ +typedef struct +{ + uint32_t first_chunk; /* the index of the first chunk in this run of chunks that share the same samples_per_chunk and sample_description_index */ + uint32_t samples_per_chunk; /* the number of samples in each of these chunks */ + uint32_t sample_description_index; /* the index of the sample entry that describes the samples in this chunk */ +} isom_stsc_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; + lsmash_entry_list_t *list; +} isom_stsc_t; + +/* Chunk Offset Box + * chunk_offset is the offset of the start of a chunk into its containing media file. + * Offsets are file offsets, not the offset into any box within the file. */ +typedef struct +{ + uint32_t chunk_offset; +} isom_stco_entry_t; + +typedef struct +{ + /* for large presentations */ + uint64_t chunk_offset; +} isom_co64_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; /* type = 'stco': 32-bit chunk offsets / type = 'co64': 64-bit chunk offsets */ + lsmash_entry_list_t *list; + + uint8_t large_presentation; /* Set 1 to this if 64-bit chunk-offset are needed. */ +} isom_stco_t; /* share with co64 box */ + +/* Sample Group Description Box + * This box gives information about the characteristics of sample groups. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* Use of version 0 entries is deprecated. */ + uint32_t grouping_type; /* an integer that identifies the sbgp that is associated with this sample group description */ + uint32_t default_length; /* the length of every group entry (if the length is constant), or zero (if it is variable) + * This field is available only if version == 1. */ + lsmash_entry_list_t *list; +} isom_sgpd_entry_t; + +/* Random Access Entry + * Samples marked by this group must be random access points, and may also be sync points. */ +typedef struct +{ + /* grouping_type is 'rap ' */ + uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ + unsigned num_leading_samples_known : 1; /* the value of one indicates that the number of leading samples is known for each sample in this group, + * and the number is specified by num_leading_samples. */ + unsigned num_leading_samples : 7; /* the number of leading samples for each sample in this group + * Note: when num_leading_samples_known is equal to 0, this field should be ignored. */ +} isom_rap_entry_t; + +/* Roll Recovery Entry + * This grouping type is defined as that group of samples having the same roll distance. */ +typedef struct +{ + /* grouping_type is 'roll' */ + uint32_t description_length; /* This field is available only if version == 1 and default_length == 0. */ + int16_t roll_distance; /* the number of samples that must be decoded in order for a sample to be decoded correctly + * The value zero must not be used. */ +} isom_roll_entry_t; + +/* Sample to Group Box + * This box is used to find the group that a sample belongs to and the associated description of that sample group. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t grouping_type; /* Links it to its sample group description table with the same value for grouping type. */ + uint32_t grouping_type_parameter; /* an indication of the sub-type of the grouping + * This field is available only if version == 1. */ + lsmash_entry_list_t *list; +} isom_sbgp_entry_t; + +typedef struct +{ + uint32_t sample_count; /* the number of consecutive samples with the same sample group descriptor */ + uint32_t group_description_index; /* the index of the sample group entry which describes the samples in this group + * The index ranges from 1 to the number of sample group entries in the Sample Group Description Box, + * or takes the value 0 to indicate that this sample is a member of no group of this type. */ +} isom_group_assignment_entry_t; + +/* Sample Table Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_stsd_t *stsd; /* Sample Description Box */ + isom_stts_t *stts; /* Decoding Time to Sample Box */ + isom_ctts_t *ctts; /* Composition Time to Sample Box */ + isom_cslg_t *cslg; /* ISOM: Composition to Decode Box / QTFF: Composition Shift Least Greatest Box */ + isom_stss_t *stss; /* Sync Sample Box */ + isom_stps_t *stps; /* ISOM: null / QTFF: Partial Sync Sample Box */ + isom_sdtp_t *sdtp; /* Independent and Disposable Samples Box */ + isom_stsc_t *stsc; /* Sample To Chunk Box */ + isom_stsz_t *stsz; /* Sample Size Box */ + isom_stco_t *stco; /* Chunk Offset Box */ + lsmash_entry_list_t *sgpd_list; /* ISOM: Sample Group Description Boxes / QTFF: null */ + lsmash_entry_list_t *sbgp_list; /* ISOM: Sample To Group Boxes / QTFF: null */ +} isom_stbl_t; + +/* Media Information Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + /* Media Information Header Boxes */ + isom_vmhd_t *vmhd; /* Video Media Header Box */ + isom_smhd_t *smhd; /* Sound Media Header Box */ + isom_hmhd_t *hmhd; /* ISOM: Hint Media Header Box / QTFF: null */ + isom_nmhd_t *nmhd; /* ISOM: Null Media Header Box / QTFF: null */ + isom_gmhd_t *gmhd; /* ISOM: null / QTFF: Generic Media Information Header Box */ + /* */ + isom_hdlr_t *hdlr; /* ISOM: null / QTFF: Data Handler Reference Box + * Note: this box must come before Data Information Box. */ + isom_dinf_t *dinf; /* Data Information Box */ + isom_stbl_t *stbl; /* Sample Table Box */ +} isom_minf_t; + +/* Media Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mdhd_t *mdhd; /* Media Header Box */ + isom_hdlr_t *hdlr; /* ISOM: Handler Reference Box / QTFF: Media Handler Reference Box + * Note: this box must come before Media Information Box. */ + isom_minf_t *minf; /* Media Information Box */ +} isom_mdia_t; + +/* Movie Header Box + * This box defines overall information which is media-independent, and relevant to the entire presentation considered as a whole. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version is either 0 or 1 */ + /* version == 0: uint64_t -> uint32_t */ + uint64_t creation_time; /* the creation time of the presentation (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint64_t modification_time; /* the most recent time the presentation was modified (in seconds since midnight, Jan. 1, 1904, in UTC time) */ + uint32_t timescale; /* movie timescale: timescale for the entire presentation */ + uint64_t duration; /* the duration, expressed in movie timescale, of the longest track */ + /* The following fields are treated as reserved in MP4 version 1. */ + int32_t rate; /* fixed point 16.16 number. 0x00010000 is normal forward playback. */ + int16_t volume; /* fixed point 8.8 number. 0x0100 is full volume. */ + int16_t reserved; + int32_t preferredLong[2]; /* ISOM: reserved / QTFF: unknown */ + int32_t matrix[9]; /* transformation matrix for the video */ + /* The following fields are defined in QuickTime file format. + * In ISO Base Media file format, these fields are treated as pre_defined. */ + int32_t previewTime; /* the time value in the movie at which the preview begins */ + int32_t previewDuration; /* the duration of the movie preview in movie timescale units */ + int32_t posterTime; /* the time value of the time of the movie poster */ + int32_t selectionTime; /* the time value for the start time of the current selection */ + int32_t selectionDuration; /* the duration of the current selection in movie timescale units */ + int32_t currentTime; /* the time value for current time position within the movie */ + /* */ + uint32_t next_track_ID; /* larger than the largest track-ID in use */ +} isom_mvhd_t; + +/* Object Descriptor Box + * Note that this box is mandatory under 14496-1:2001 (mp41) while not mandatory under 14496-14:2003 (mp42). */ +struct mp4sys_ObjectDescriptor_t; /* FIXME: I think these structs using mp4sys should be placed in isom.c */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + struct mp4sys_ObjectDescriptor_t *OD; +} isom_iods_t; + +/* Media Data Box + * This box contains the media data. + * A presentation may contain zero or more Media Data Boxes.*/ +typedef struct +{ + ISOM_BASEBOX_COMMON; /* If size is 0, then this box is the last box. */ + + uint64_t placeholder_pos; /* placeholder position for largesize */ +} isom_mdat_t; + +/* Free Space Box + * The contents of a free-space box are irrelevant and may be ignored without affecting the presentation. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; /* type is 'free' or 'skip' */ + uint32_t length; + uint8_t *data; +} isom_free_t; + +typedef isom_free_t isom_skip_t; + +/* Chapter List Box + * This box is NOT defined in the ISO/MPEG-4 specs. */ +typedef struct +{ + uint64_t start_time; /* version = 0: expressed in movie timescale units + * version = 1: expressed in 100 nanoseconds */ + /* Chapter name is Pascal string */ + uint8_t chapter_name_length; + char *chapter_name; +} isom_chpl_entry_t; + +typedef struct +{ + ISOM_FULLBOX_COMMON; /* version = 0 is defined in F4V file format. */ + uint8_t unknown; /* only available under version = 1 */ + lsmash_entry_list_t *list; /* if version is set to 0, entry_count is uint8_t. */ +} isom_chpl_t; + +typedef struct +{ + char *chapter_name; + uint64_t start_time; +} isom_chapter_entry_t; + +/* User Data Box + * This box is a container box for informative user-data. + * This user data is formatted as a set of boxes with more specific box types, which declare more precisely their content. + * QTFF: for historical reasons, this box is optionally terminated by a 32-bit integer set to 0. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_chpl_t *chpl; /* Chapter List Box */ +} isom_udta_t; + +/** Caches for handling tracks **/ +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 */ + uint64_t pool_size; /* the sum of the size of samples in the pool */ + 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_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ + isom_rap_entry_t *random_access; /* the address corresponding to the random access entry in Sample Group Description Box */ + uint8_t is_prev_rap; /* whether the previous sample is a random access point or not */ +} isom_rap_group_t; + +typedef struct +{ + isom_group_assignment_entry_t *assignment; /* the address corresponding to the entry in Sample to Group Box */ + uint32_t first_sample; /* the number of the first sample of the group */ + uint32_t recovery_point; /* the identifier necessary for the recovery from its starting point to be completed */ + uint8_t delimited; /* the flag if the sample_count is determined */ + uint8_t described; /* the flag if the group description is determined */ +} isom_roll_group_t; + +typedef struct +{ + lsmash_entry_list_t *pool; /* grouping pooled to delimit and describe */ +} isom_grouping_t; + +typedef struct +{ + uint8_t has_samples; + uint32_t traf_number; + uint32_t last_duration; /* the last sample duration in this track fragment */ + uint64_t largest_cts; /* the largest CTS in this track fragments */ +} isom_fragment_t; + +typedef struct +{ + uint8_t all_sync; /* if all samples are sync sample */ + isom_chunk_t chunk; + isom_timestamp_t timestamp; + isom_grouping_t roll; + isom_rap_group_t *rap; + isom_fragment_t *fragment; +} isom_cache_t; + +/** Movie Fragments Boxes **/ +/* Track Fragments Flags ('tf_flags') */ +typedef enum +{ + ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT = 0x000001, /* base_data_offset field exists. */ + ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002, /* sample_description_index field exists. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008, /* default_sample_duration field exists. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010, /* default_sample_size field exists. */ + ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020, /* default_sample_flags field exists. */ + ISOM_TF_FLAGS_DURATION_IS_EMPTY = 0x010000, /* There are no samples for this time interval. */ +} isom_tf_flags_code; + +/* Track Run Flags ('tr_flags') */ +typedef enum +{ + ISOM_TR_FLAGS_DATA_OFFSET_PRESENT = 0x000001, /* data_offset field exists. */ + ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT = 0x000004, /* first_sample_flags field exists. */ + ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT = 0x000100, /* sample_duration field exists. */ + ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT = 0x000200, /* sample_size field exists. */ + ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT = 0x000400, /* sample_flags field exists. */ + ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT = 0x000800, /* sample_composition_time_offset field exists. */ +} isom_tr_flags_code; + +/* Sample Flags */ +typedef struct +{ + unsigned reserved : 4; + /* The definition of the following fields is quite the same as Independent and Disposable Samples Box. */ + unsigned is_leading : 2; + unsigned sample_depends_on : 2; + unsigned sample_is_depended_on : 2; + unsigned sample_has_redundancy : 2; + /* */ + unsigned sample_padding_value : 3; /* the number of bits at the end of this sample */ + unsigned sample_is_non_sync_sample : 1; /* 0 value means this sample is sync sample. */ + uint16_t sample_degradation_priority; +} isom_sample_flags_t; + +/* Movie Extends Header Box + * This box is omitted when used in live streaming. + * If this box is not present, the overall duration must be computed by examining each fragment. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + /* version == 0: uint64_t -> uint32_t */ + uint64_t fragment_duration; /* the duration of the longest track, in the timescale indicated in the Movie Header Box, including movie fragments. */ +} isom_mehd_t; + +/* Track Extends Box + * This box sets up default values used by the movie fragments. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t track_ID; /* identifier of the track; this shall be the track ID of a track in the Movie Box */ + uint32_t default_sample_description_index; + uint32_t default_sample_duration; + uint32_t default_sample_size; + isom_sample_flags_t default_sample_flags; +} isom_trex_entry_t; + +/* Movie Extends Box + * This box warns readers that there might be Movie Fragment Boxes in this file. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mehd_t *mehd; /* Movie Extends Header Box / omitted when used in live streaming */ + lsmash_entry_list_t *trex_list; /* Track Extends Box */ + + uint64_t placeholder_pos; /* placeholder position for Movie Extends Header Box */ +} isom_mvex_t; + +/* Movie Fragment Header Box + * This box contains a sequence number, as a safety check. + * The sequence number 'usually' starts at 1 and must increase for each movie fragment in the file, in the order in which they occur. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t sequence_number; /* the ordinal number of this fragment, in increasing order */ +} isom_mfhd_t; + +/* Track Fragment Header Box + * Each movie fragment can contain zero or more fragments for each track; + * and a track fragment can contain zero or more contiguous runs of samples. + * This box sets up information and defaults used for those runs of samples. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* flags field is used for 'tf_flags'. */ + uint32_t track_ID; + /* all the following are optional fields */ + uint64_t base_data_offset; /* an explicit anchor for the data offsets in each track run + * Offsets are file offsets as like as chunk_offset in Chunk Offset Box. + * If not provided, the base_data_offset for the first track in the movie fragment is the position + * of the first byte of the enclosing Movie Fragment Box, and for second and subsequent track fragments, + * the default is the end of the data defined by the preceding fragment. + * To avoid the case this field might overflow, e.g. semi-permanent live streaming and broadcasting, + * you shall not use this optional field. */ + uint32_t sample_description_index; /* override default_sample_description_index in Track Extends Box */ + uint32_t default_sample_duration; /* override default_sample_duration in Track Extends Box */ + uint32_t default_sample_size; /* override default_sample_size in Track Extends Box */ + isom_sample_flags_t default_sample_flags; /* override default_sample_flags in Track Extends Box */ +} isom_tfhd_t; + +/* Track Fragment Run Box + * Within the Track Fragment Box, there are zero or more Track Fragment Run Boxes. + * If the duration-is-empty flag is set in the tf_flags, there are no track runs. + * A track run documents a contiguous set of samples for a track. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; /* flags field is used for 'tr_flags'. */ + uint32_t sample_count; /* the number of samples being added in this run; also the number of rows in the following table */ + /* The following are optional fields. */ + int32_t data_offset; /* This value is added to the implicit or explicit data_offset established in the Track Fragment Header Box. + * If this field is not present, then the data for this run starts immediately after the data of the previous run, + * or at the base_data_offset defined by the Track Fragment Header Box if this is the first run in a track fragment. */ + isom_sample_flags_t first_sample_flags; /* a set of flags for the first sample only of this run */ + lsmash_entry_list_t *optional; /* all fields in this array are optional. */ +} isom_trun_entry_t; + +typedef struct +{ + /* If the following fields is present, each field overrides default value described in Track Fragment Header Box or Track Extends Box. */ + uint32_t sample_duration; /* override default_sample_duration */ + uint32_t sample_size; /* override default_sample_size */ + isom_sample_flags_t sample_flags; /* override default_sample_flags */ + /* */ + uint32_t sample_composition_time_offset; /* composition time offset */ +} isom_trun_optional_row_t; + +/* Track Fragment Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_tfhd_t *tfhd; /* Track Fragment Header Box */ + lsmash_entry_list_t *trun_list; /* Track Fragment Run Box List + * If the duration-is-empty flag is set in the tf_flags, there are no track runs. */ + + isom_cache_t *cache; +} isom_traf_entry_t; + +/* Movie Fragment Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mfhd_t *mfhd; /* Movie Fragment Header Box */ + lsmash_entry_list_t *traf_list; /* Track Fragment Box List */ +} isom_moof_entry_t; + +/* Track Fragment Random Access Box + * Each entry in this box contains the location and the presentation time of the random accessible sample. + * Note that not every random accessible sample in the track needs to be listed in the table. + * The absence of this box does not mean that all the samples are sync samples. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t track_ID; + unsigned int reserved : 26; + unsigned int length_size_of_traf_num : 2; /* the length in byte of the traf_number field minus one */ + unsigned int length_size_of_trun_num : 2; /* the length in byte of the trun_number field minus one */ + unsigned int length_size_of_sample_num : 2; /* the length in byte of the sample_number field minus one */ + uint32_t number_of_entry; /* the number of the entries for this track + * Value zero indicates that every sample is a random access point and no table entry follows. */ + lsmash_entry_list_t *list; /* entry_count corresponds to number_of_entry. */ +} isom_tfra_entry_t; + +typedef struct +{ + /* version == 0: 64bits -> 32bits */ + uint64_t time; /* the presentation time of the random access sample in units defined in the Media Header Box of the associated track + * According to 14496-12:2008/FPDAM 3, presentation times are composition times. */ + uint64_t moof_offset; /* the offset of the Movie Fragment Box used in this entry + * Offset is the byte-offset between the beginning of the file and the beginning of the Movie Fragment Box. */ + /* */ + uint32_t traf_number; /* the Track Fragment Box ('traf') number that contains the random accessible sample + * The number ranges from 1 in each Movie Fragment Box ('moof'). */ + uint32_t trun_number; /* the Track Fragment Run Box ('trun') number that contains the random accessible sample + * The number ranges from 1 in each Track Fragment Box ('traf'). */ + uint32_t sample_number; /* the sample number that contains the random accessible sample + * The number ranges from 1 in each Track Fragment Run Box ('trun'). */ +} isom_tfra_location_time_entry_t; + +/* Movie Fragment Random Access Offset Box + * This box provides a copy of the length field from the enclosing Movie Fragment Random Access Box. */ +typedef struct +{ + ISOM_FULLBOX_COMMON; + uint32_t length; /* an integer gives the number of bytes of the enclosing Movie Fragment Random Access Box + * This field is placed at the last of the enclosing box to assist readers scanning + * from the end of the file in finding the Movie Fragment Random Access Box. */ +} isom_mfro_t; + +/* Movie Fragment Random Access Box + * This box provides a table which may assist readers in finding random access points in a file using movie fragments, + * and is usually placed at or near the end of the file. + * The last box within the Movie Fragment Random Access Box, which is called Movie Fragment Random Access Offset Box, + * provides a copy of the length field from the Movie Fragment Random Access Box. */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + lsmash_entry_list_t *tfra_list; /* Track Fragment Random Access Box */ + isom_mfro_t *mfro; /* Movie Fragment Random Access Offset Box */ +} isom_mfra_t; + +/* Movie fragment manager + * The presence of this means we use the structure of movie fragments. */ +typedef struct +{ + isom_moof_entry_t *movie; /* the address corresponding to the current Movie Fragment Box */ + uint64_t fragment_count; /* the number of movie fragments we created */ + uint64_t pool_size; + lsmash_entry_list_t *pool; /* samples pooled to interleave for the current movie fragment */ +} isom_fragment_manager_t; + +/** **/ + +/* Movie Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_mvhd_t *mvhd; /* Movie Header Box */ + isom_iods_t *iods; /* ISOM: Object Descriptor Box / QTFF: null */ + lsmash_entry_list_t *trak_list; /* Track Box List */ + isom_udta_t *udta; /* User Data Box */ + isom_mvex_t *mvex; /* Movie Extends Box */ +} isom_moov_t; + +/* ROOT */ +struct lsmash_root_tag +{ + ISOM_FULLBOX_COMMON; /* the size field expresses total file size + * the flags field expresses file mode */ + isom_ftyp_t *ftyp; /* File Type Box */ + isom_moov_t *moov; /* Movie Box */ + lsmash_entry_list_t *moof_list; /* Movie Fragment Box List */ + isom_mdat_t *mdat; /* Media Data Box */ + isom_free_t *free; /* Free Space Box */ + isom_mfra_t *mfra; /* Movie Fragment Random Access Box */ + + lsmash_bs_t *bs; /* bytestream manager */ + isom_fragment_manager_t *fragment; /* movie fragment manager */ + double max_chunk_duration; /* max duration per chunk in seconds */ + double max_async_tolerance; /* max tolerance, in seconds, for amount of interleaving asynchronization between tracks */ + uint64_t max_chunk_size; /* max size per chunk in bytes. */ + uint64_t max_read_size; /* max size of reading from a chunk at a time. */ + uint8_t file_type_written; /* whether File Type Box was written */ + uint8_t qt_compatible; /* compatibility with QuickTime file format */ + uint8_t isom_compatible; /* compatibility with ISO Base Media file format */ + uint8_t avc_extensions; /* compatibility with AVC extensions */ + uint8_t mp4_version1; /* compatibility with MP4 ver.1 file format */ + uint8_t mp4_version2; /* compatibility with MP4 ver.2 file format */ + uint8_t itunes_audio; /* compatibility with iTunes Audio */ + uint8_t max_3gpp_version; /* maximum 3GPP version */ + uint8_t max_isom_version; /* maximum ISO Base Media file format version */ + lsmash_entry_list_t *print; + lsmash_entry_list_t *timeline; +}; + +/* Track Box */ +typedef struct +{ + ISOM_BASEBOX_COMMON; + isom_tkhd_t *tkhd; /* Track Header Box */ + isom_tapt_t *tapt; /* ISOM: null / QTFF: Track Aperture Mode Dimensions Box */ + isom_edts_t *edts; /* Edit Box */ + isom_tref_t *tref; /* Track Reference Box */ + isom_mdia_t *mdia; /* Media Box */ + isom_udta_t *udta; /* User Data Box */ + + isom_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 }, + { 41, ISOM_LANGUAGE_CODE_YIDDISH }, + { 42, ISOM_LANGUAGE_CODE_SERBIAN }, + { 43, ISOM_LANGUAGE_CODE_MACEDONIAN }, + { 44, ISOM_LANGUAGE_CODE_BULGARIAN }, + { 45, ISOM_LANGUAGE_CODE_UKRAINIAN }, + { 46, ISOM_LANGUAGE_CODE_BELARUSIAN }, + { 47, ISOM_LANGUAGE_CODE_UZBEK }, + { 48, ISOM_LANGUAGE_CODE_KAZAKH }, + { 49, ISOM_LANGUAGE_CODE_AZERBAIJANI }, + { 51, ISOM_LANGUAGE_CODE_ARMENIAN }, + { 52, ISOM_LANGUAGE_CODE_GEORGIAN }, + { 53, ISOM_LANGUAGE_CODE_MOLDAVIAN }, + { 54, ISOM_LANGUAGE_CODE_KIRGHIZ }, + { 55, ISOM_LANGUAGE_CODE_TAJIK }, + { 56, ISOM_LANGUAGE_CODE_TURKMEN }, + { 57, ISOM_LANGUAGE_CODE_MONGOLIAN }, + { 59, ISOM_LANGUAGE_CODE_PASHTO }, + { 60, ISOM_LANGUAGE_CODE_KURDISH }, + { 61, ISOM_LANGUAGE_CODE_KASHMIRI }, + { 62, ISOM_LANGUAGE_CODE_SINDHI }, + { 63, ISOM_LANGUAGE_CODE_TIBETAN }, + { 64, ISOM_LANGUAGE_CODE_NEPALI }, + { 65, ISOM_LANGUAGE_CODE_SANSKRIT }, + { 66, ISOM_LANGUAGE_CODE_MARATHI }, + { 67, ISOM_LANGUAGE_CODE_BENGALI }, + { 68, ISOM_LANGUAGE_CODE_ASSAMESE }, + { 69, ISOM_LANGUAGE_CODE_GUJARATI }, + { 70, ISOM_LANGUAGE_CODE_PUNJABI }, + { 71, ISOM_LANGUAGE_CODE_ORIYA }, + { 72, ISOM_LANGUAGE_CODE_MALAYALAM }, + { 73, ISOM_LANGUAGE_CODE_KANNADA }, + { 74, ISOM_LANGUAGE_CODE_TAMIL }, + { 75, ISOM_LANGUAGE_CODE_TELUGU }, + { 76, ISOM_LANGUAGE_CODE_SINHALESE }, + { 77, ISOM_LANGUAGE_CODE_BURMESE }, + { 78, ISOM_LANGUAGE_CODE_KHMER }, + { 79, ISOM_LANGUAGE_CODE_LAO }, + { 80, ISOM_LANGUAGE_CODE_VIETNAMESE }, + { 81, ISOM_LANGUAGE_CODE_INDONESIAN }, + { 82, ISOM_LANGUAGE_CODE_TAGALOG }, + { 83, ISOM_LANGUAGE_CODE_MALAY_ROMAN }, + { 84, ISOM_LANGUAGE_CODE_MAYAY_ARABIC }, + { 85, ISOM_LANGUAGE_CODE_AMHARIC }, + { 87, ISOM_LANGUAGE_CODE_OROMO }, + { 88, ISOM_LANGUAGE_CODE_SOMALI }, + { 89, ISOM_LANGUAGE_CODE_SWAHILI }, + { 90, ISOM_LANGUAGE_CODE_KINYARWANDA }, + { 91, ISOM_LANGUAGE_CODE_RUNDI }, + { 92, ISOM_LANGUAGE_CODE_CHEWA }, + { 93, ISOM_LANGUAGE_CODE_MALAGASY }, + { 94, ISOM_LANGUAGE_CODE_ESPERANTO }, + { 128, ISOM_LANGUAGE_CODE_WELSH }, + { 129, ISOM_LANGUAGE_CODE_BASQUE }, + { 130, ISOM_LANGUAGE_CODE_CATALAN }, + { 131, ISOM_LANGUAGE_CODE_LATIN }, + { 132, ISOM_LANGUAGE_CODE_QUECHUA }, + { 133, ISOM_LANGUAGE_CODE_GUARANI }, + { 134, ISOM_LANGUAGE_CODE_AYMARA }, + { 135, ISOM_LANGUAGE_CODE_TATAR }, + { 136, ISOM_LANGUAGE_CODE_UIGHUR }, + { 137, ISOM_LANGUAGE_CODE_DZONGKHA }, + { 138, ISOM_LANGUAGE_CODE_JAVANESE }, + { UINT16_MAX, 0 } +}; + +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, +}; + +int isom_is_fullbox( void *box ); +int isom_is_lpcm_audio( uint32_t type ); + +void isom_init_box_common( void *box, void *parent, uint32_t type ); + +int isom_check_compatibility( lsmash_root_t *root ); + +char *isom_4cc2str( uint32_t fourcc ); +char *isom_unpack_iso_language( uint16_t language ); + +isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID ); +isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ); +isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ); + +isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size ); +void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps ); + +int isom_add_edts( isom_trak_entry_t *trak ); +int isom_add_elst( isom_edts_t *edts ); +int isom_add_clap( isom_visual_entry_t *visual ); +int isom_add_pasp( isom_visual_entry_t *visual ); +int isom_add_colr( isom_visual_entry_t *visual ); +int isom_add_stsl( isom_visual_entry_t *visual ); +int isom_add_avcC( isom_visual_entry_t *visual ); +int isom_add_btrt( isom_visual_entry_t *visual ); +int isom_add_wave( isom_audio_entry_t *audio ); +int isom_add_frma( isom_wave_t *wave ); +int isom_add_enda( isom_wave_t *wave ); +int isom_add_mp4a( isom_wave_t *wave ); +int isom_add_terminator( isom_wave_t *wave ); +int isom_add_chan( isom_audio_entry_t *audio ); +int isom_add_ftab( isom_tx3g_entry_t *tx3g ); + +void isom_remove_tapt( isom_tapt_t *tapt ); +void isom_remove_clap( isom_clap_t *clap ); +void isom_remove_pasp( isom_pasp_t *pasp ); +void isom_remove_colr( isom_colr_t *colr ); +void isom_remove_stsl( isom_stsl_t *stsl ); +void isom_remove_avcC( isom_avcC_t *avcC ); +void isom_remove_btrt( isom_btrt_t *btrt ); +void isom_remove_frma( isom_frma_t *frma ); +void isom_remove_enda( isom_enda_t *enda ); +void isom_remove_mp4a( isom_mp4a_t *mp4a ); +void isom_remove_terminator( isom_terminator_t *terminator ); +void isom_remove_wave( isom_wave_t *wave ); +void isom_remove_chan( isom_chan_t *chan ); +void isom_remove_ftab( isom_ftab_t *ftab ); +void isom_remove_sample_description( isom_sample_entry_t *sample ); + +#define isom_create_box( box_name, parent_name, box_4cc ) \ + isom_##box_name##_t *(box_name) = malloc( sizeof(isom_##box_name##_t) ); \ + if( !box_name ) \ + return -1; \ + memset( box_name, 0, sizeof(isom_##box_name##_t) ); \ + isom_init_box_common( box_name, parent_name, box_4cc ) + +#define isom_create_list_box( box_name, parent_name, box_4cc ) \ + isom_create_box( box_name, parent_name, box_4cc ); \ + box_name->list = lsmash_create_entry_list(); \ + if( !box_name->list ) \ + { \ + free( box_name ); \ + return -1; \ + } + +#endif diff --git a/output/mp4/importer.c b/output/mp4/importer.c new file mode 100644 index 0000000..15ac923 --- /dev/null +++ b/output/mp4/importer.c @@ -0,0 +1,1047 @@ +/***************************************************************************** + * 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. */ + +#include "internal.h" /* must be placed first */ + +#include +#include + +#define LSMASH_IMPORTER_INTERNAL +#include "importer.h" + +#include "mp4a.h" +#include "box.h" + +/*************************************************************************** + importer framework +***************************************************************************/ +struct mp4sys_importer_tag; + +typedef void ( *mp4sys_importer_cleanup )( struct mp4sys_importer_tag* 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_create_audio_summary(); + if( !summary ) + return NULL; + 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, DecoderSpecificInfo and 14496-3 Subpart 9: MPEG-1/2 Audio in MPEG-4. + */ + summary->object_type_indication = header->profile_ObjectType + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile; + summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; + summary->asc = NULL; + summary->asc_length = 0; + // summary->sbr_mode = MP4A_AAC_SBR_NONE; /* MPEG-2 AAC should not be HE-AAC, but we forgive them. */ + return summary; + } +#endif + if( lsmash_setup_AudioSpecificConfig( summary ) ) + { + lsmash_cleanup_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; + lsmash_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 ) + { + lsmash_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 ); + lsmash_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_create_audio_summary(); + if( !summary ) + return NULL; + summary->sample_type = ISOM_CODEC_TYPE_MP4A_AUDIO; + summary->object_type_indication = header->ID ? MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3 : MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3; + summary->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( lsmash_setup_AudioSpecificConfig( summary ) ) + { + lsmash_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, 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; + lsmash_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 ) + { + lsmash_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 ); + lsmash_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 = lsmash_create_audio_summary(); + 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 ) + { + lsmash_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; + lsmash_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, lsmash_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 ) || lsmash_fseek( importer->stream, 0, SEEK_SET ) ) + break; + } + } + else + { + /* needs name matching. */ + for( int i = 0; (funcs = mp4sys_importer_tbl[i]) != NULL; i++ ) + { + if( strcmp( funcs->name, format ) ) + continue; + if( funcs->probe( importer ) ) + funcs = NULL; + break; + } + } + if( !funcs ) + { + mp4sys_importer_close( importer ); + return NULL; + } + importer->funcs = *funcs; + return importer; +} + +/* 0 if success, positive if changed, negative if failed */ +int mp4sys_importer_get_access_unit( mp4sys_importer_t* importer, uint32_t track_number, 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_create_audio_summary(); + 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( lsmash_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..e2ff0c1 --- /dev/null +++ b/output/mp4/importer.h @@ -0,0 +1,47 @@ +/***************************************************************************** + * 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 +***************************************************************************/ + +#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/internal.h b/output/mp4/internal.h new file mode 100644 index 0000000..b697f5d --- /dev/null +++ b/output/mp4/internal.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * internal.h: + ***************************************************************************** + * Copyright (C) 2010 L-SMASH project + * + * Authors: Takashi Hirata + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *****************************************************************************/ + +/* This file is available under an ISC license. */ + +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "common/osdep.h" /* must be placed before stdio.h */ +#include +#include + +#ifndef lsmash_fseek +#define lsmash_fseek fseek +#define lsmash_ftell ftell +#endif + +#include "lsmash.h" + +#endif diff --git a/output/mp4/isom.c b/output/mp4/isom.c new file mode 100644 index 0000000..8c603e9 --- /dev/null +++ b/output/mp4/isom.c @@ -0,0 +1,9218 @@ +/***************************************************************************** + * 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 "internal.h" /* must be placed first */ + +#include +#include +#include +#include /* for chapter handling */ + +#include "box.h" +#include "mp4a.h" +#include "mp4sys.h" +#ifdef LSMASH_DEMUXER_ENABLED +#include "read.h" +#include "print.h" +#include "timeline.h" +#endif + + +/*---- ----*/ +/* Return 1 if the box is fullbox, Otherwise return 0. */ +int isom_is_fullbox( void *box ) +{ + uint32_t type = ((isom_box_t *)box)->type; + static const uint32_t fullbox_table[] = { + ISOM_BOX_TYPE_MVHD, + ISOM_BOX_TYPE_IODS, + ISOM_BOX_TYPE_ESDS, + ISOM_BOX_TYPE_TKHD, + QT_BOX_TYPE_CLEF, + QT_BOX_TYPE_PROF, + QT_BOX_TYPE_ENOF, + ISOM_BOX_TYPE_ELST, + ISOM_BOX_TYPE_MDHD, + ISOM_BOX_TYPE_HDLR, + ISOM_BOX_TYPE_VMHD, + ISOM_BOX_TYPE_SMHD, + ISOM_BOX_TYPE_HMHD, + ISOM_BOX_TYPE_NMHD, + QT_BOX_TYPE_GMIN, + ISOM_BOX_TYPE_DREF, + ISOM_BOX_TYPE_URL , + ISOM_BOX_TYPE_STSD, + ISOM_BOX_TYPE_STSL, + QT_BOX_TYPE_CHAN, + ISOM_BOX_TYPE_STTS, + ISOM_BOX_TYPE_CTTS, + ISOM_BOX_TYPE_CSLG, + ISOM_BOX_TYPE_STSS, + QT_BOX_TYPE_STPS, + ISOM_BOX_TYPE_SDTP, + ISOM_BOX_TYPE_STSC, + ISOM_BOX_TYPE_STSZ, + ISOM_BOX_TYPE_STCO, + ISOM_BOX_TYPE_CO64, + ISOM_BOX_TYPE_SGPD, + ISOM_BOX_TYPE_SBGP, + ISOM_BOX_TYPE_CHPL, + ISOM_BOX_TYPE_MEHD, + ISOM_BOX_TYPE_TREX, + ISOM_BOX_TYPE_MFHD, + ISOM_BOX_TYPE_TFHD, + ISOM_BOX_TYPE_TRUN, + ISOM_BOX_TYPE_TFRA, + ISOM_BOX_TYPE_MFRO + }; + for( int i = 0; i < sizeof(fullbox_table)/sizeof(uint32_t); i++ ) + if( type == fullbox_table[i] ) + return 1; + return 0; +} + +/* Return 1 if the sample type is LPCM audio, Otherwise return 0. */ +int isom_is_lpcm_audio( uint32_t type ) +{ + return type == QT_CODEC_TYPE_23NI_AUDIO + || type == QT_CODEC_TYPE_NONE_AUDIO + || type == QT_CODEC_TYPE_LPCM_AUDIO + || type == QT_CODEC_TYPE_RAW_AUDIO + || type == QT_CODEC_TYPE_SOWT_AUDIO + || type == QT_CODEC_TYPE_TWOS_AUDIO + || type == QT_CODEC_TYPE_FL32_AUDIO + || type == QT_CODEC_TYPE_FL64_AUDIO + || type == QT_CODEC_TYPE_IN24_AUDIO + || type == QT_CODEC_TYPE_IN32_AUDIO + || type == QT_CODEC_TYPE_NOT_SPECIFIED; +} + +char *isom_4cc2str( uint32_t fourcc ) +{ + static char str[5]; + str[0] = (fourcc >> 24) & 0xff; + str[1] = (fourcc >> 16) & 0xff; + str[2] = (fourcc >> 8) & 0xff; + str[3] = fourcc & 0xff; + str[4] = 0; + return str; +} + +static inline void isom_init_basebox_common( isom_box_t *box, isom_box_t *parent, uint32_t type ) +{ + box->root = parent->root; + box->parent = parent; + box->size = 0; + box->type = type; + box->usertype = NULL; +} + +static inline void isom_init_fullbox_common( isom_box_t *box, isom_box_t *parent, uint32_t type ) +{ + box->root = parent->root; + box->parent = parent; + box->size = 0; + box->type = type; + box->usertype = NULL; + box->version = 0; + box->flags = 0; +} + +void isom_init_box_common( void *box, void *parent, uint32_t type ) +{ + assert( parent && ((isom_box_t *)parent)->root ); + if( ((isom_box_t *)parent)->type == ISOM_BOX_TYPE_STSD ) + { + isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); + return; + } + if( isom_is_fullbox( box ) ) + isom_init_fullbox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); + else + isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type ); +} + +static void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box ) +{ + if( box->size > UINT32_MAX ) + { + lsmash_bs_put_be32( bs, 1 ); + lsmash_bs_put_be32( bs, box->type ); + lsmash_bs_put_be64( bs, box->size ); /* largesize */ + } + else + { + lsmash_bs_put_be32( bs, (uint32_t)box->size ); + lsmash_bs_put_be32( bs, box->type ); + } + if( box->type == ISOM_BOX_TYPE_UUID ) + lsmash_bs_put_bytes( bs, box->usertype, 16 ); +} + +static void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_bs_put_basebox_common( bs, box ); + lsmash_bs_put_byte( bs, box->version ); + lsmash_bs_put_be24( bs, box->flags ); +} + +static void isom_bs_put_box_common( lsmash_bs_t *bs, void *box ) +{ + if( !box ) + { + bs->error = 1; + return; + } + isom_box_t *parent = ((isom_box_t *)box)->parent; + if( parent && parent->type == ISOM_BOX_TYPE_STSD ) + { + isom_bs_put_basebox_common( bs, (isom_box_t *)box ); + return; + } + if( isom_is_fullbox( box ) ) + isom_bs_put_fullbox_common( bs, (isom_box_t *)box ); + else + isom_bs_put_basebox_common( bs, (isom_box_t *)box ); +} + +isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID ) +{ + if( !track_ID || !root || !root->moov || !root->moov->trak_list ) + return NULL; + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak || !trak->tkhd ) + return NULL; + if( trak->tkhd->track_ID == track_ID ) + return trak; + } + return NULL; +} + +static isom_trex_entry_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID ) +{ + if( !track_ID || !mvex || !mvex->trex_list ) + return NULL; + for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next ) + { + isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data; + if( !trex ) + return NULL; + if( trex->track_ID == track_ID ) + return trex; + } + return NULL; +} + +static isom_traf_entry_t *isom_get_traf( isom_moof_entry_t *moof, uint32_t track_ID ) +{ + if( !track_ID || !moof || !moof->traf_list ) + return NULL; + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + { + isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data; + if( !traf || !traf->tfhd ) + return NULL; + if( traf->tfhd->track_ID == track_ID ) + return traf; + } + return NULL; +} + +static isom_tfra_entry_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID ) +{ + if( !track_ID || !mfra || !mfra->tfra_list ) + return NULL; + for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next ) + { + isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data; + if( !tfra ) + return NULL; + if( tfra->track_ID == track_ID ) + return tfra; + } + return NULL; +} + +static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate ) +{ + isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) ); + if( !data ) + return -1; + data->segment_duration = segment_duration; + data->media_time = media_time; + data->media_rate = media_rate; + if( lsmash_add_entry( elst->list, data ) ) + { + free( data ); + return -1; + } + if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN ) + elst->version = 1; + return 0; +} + +static isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, lsmash_track_reference_type_code type, uint32_t ref_count, uint32_t *track_ID ) +{ + if( !tref || !tref->ref_list ) + return NULL; + isom_tref_type_t *ref = malloc( sizeof(isom_tref_type_t) ); + if( !ref ) + return NULL; + isom_init_box_common( ref, tref, type ); + ref->ref_count = ref_count; + ref->track_ID = track_ID; + if( lsmash_add_entry( tref->ref_list, ref ) ) + { + free( ref ); + return NULL; + } + return ref; +} + +static int isom_add_dref_entry( isom_dref_t *dref, uint32_t flags, char *name, char *location ) +{ + if( !dref || !dref->list ) + return -1; + isom_dref_entry_t *data = 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; +} + +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; +} + +void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps ) +{ + if( !ps ) + return; + if( ps->parameterSetNALUnit ) + free( ps->parameterSetNALUnit ); + free( ps ); +} + +int lsmash_add_sps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *sps, uint32_t sps_size ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list ) + return -1; + isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data || !data->avcC ) + return -1; + isom_avcC_t *avcC = (isom_avcC_t *)data->avcC; + isom_avcC_ps_entry_t *ps = isom_create_ps_entry( sps, sps_size ); + if( !ps ) + return -1; + if( lsmash_add_entry( avcC->sequenceParameterSets, ps ) ) + { + isom_remove_avcC_ps( ps ); + return -1; + } + avcC->numOfSequenceParameterSets = avcC->sequenceParameterSets->entry_count; + return 0; +} + +int lsmash_add_pps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *pps, uint32_t pps_size ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list ) + return -1; + isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data || !data->avcC ) + return -1; + isom_avcC_t *avcC = (isom_avcC_t *)data->avcC; + isom_avcC_ps_entry_t *ps = isom_create_ps_entry( pps, pps_size ); + if( !ps ) + return -1; + if( lsmash_add_entry( avcC->pictureParameterSets, ps ) ) + { + isom_remove_avcC_ps( ps ); + return -1; + } + avcC->numOfPictureParameterSets = avcC->pictureParameterSets->entry_count; + return 0; +} + +int lsmash_add_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list ) + return -1; + isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data || !data->avcC ) + return -1; + isom_avcC_t *avcC = (isom_avcC_t *)data->avcC; + isom_avcC_ps_entry_t *ps = isom_create_ps_entry( spsext, spsext_size ); + if( !ps ) + return -1; + if( lsmash_add_entry( avcC->sequenceParameterSetExt, ps ) ) + { + isom_remove_avcC_ps( ps ); + return -1; + } + avcC->numOfSequenceParameterSetExt = avcC->sequenceParameterSetExt->entry_count; + return 0; +} + +int isom_add_avcC( isom_visual_entry_t *visual ) +{ + if( !visual ) + return -1; + isom_create_box( avcC, visual, ISOM_BOX_TYPE_AVCC ); + avcC->sequenceParameterSets = lsmash_create_entry_list(); + if( !avcC->sequenceParameterSets ) + { + free( avcC ); + return -1; + } + avcC->pictureParameterSets = lsmash_create_entry_list(); + if( !avcC->pictureParameterSets ) + { + isom_remove_avcC( avcC ); + return -1; + } + avcC->sequenceParameterSetExt = lsmash_create_entry_list(); + if( !avcC->sequenceParameterSetExt ) + { + isom_remove_avcC( avcC ); + return -1; + } + visual->avcC = avcC; + return 0; +} + +int isom_add_clap( isom_visual_entry_t *visual ) +{ + if( !visual || visual->clap ) + return -1; + isom_create_box( clap, visual, ISOM_BOX_TYPE_CLAP ); + clap->cleanApertureWidthN = 1; + clap->cleanApertureWidthD = 1; + clap->cleanApertureHeightN = 1; + clap->cleanApertureHeightD = 1; + clap->horizOffN = 0; + clap->horizOffD = 1; + clap->vertOffN = 0; + clap->vertOffD = 1; + visual->clap = clap; + return 0; +} + +int isom_add_pasp( isom_visual_entry_t *visual ) +{ + if( !visual || visual->pasp ) + return -1; + isom_create_box( pasp, visual, ISOM_BOX_TYPE_PASP ); + pasp->hSpacing = 1; + pasp->vSpacing = 1; + visual->pasp = pasp; + return 0; +} + +int isom_add_colr( isom_visual_entry_t *visual ) +{ + if( !visual || visual->colr ) + return -1; + isom_create_box( colr, visual, QT_BOX_TYPE_COLR ); + isom_color_parameter_t *param = (isom_color_parameter_t *)(&isom_color_parameter_tbl[0]); + colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC; + colr->primaries_index = param->primaries; + colr->transfer_function_index = param->transfer; + colr->matrix_index = param->matrix; + visual->colr = colr; + return 0; +} + +int isom_add_stsl( isom_visual_entry_t *visual ) +{ + if( !visual || visual->stsl ) + return -1; + isom_create_box( stsl, visual, ISOM_BOX_TYPE_STSL ); + stsl->scale_method = ISOM_SCALING_METHOD_HIDDEN; + visual->stsl = stsl; + return 0; +} + +static void isom_remove_esds( isom_esds_t *esds ); +static void isom_remove_visual_extensions( isom_visual_entry_t *visual ); + +static int isom_add_visual_extensions( isom_visual_entry_t *visual, lsmash_video_summary_t *summary ) +{ + /* Check if set up Track Aperture Modes. */ + isom_trak_entry_t *trak = (isom_trak_entry_t *)visual->parent->parent->parent->parent->parent; + int qt_compatible = trak->root->qt_compatible; + isom_tapt_t *tapt = trak->tapt; + int set_aperture_modes = qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */ + && !summary->scaling_method /* Sample scaling method might conflict with this feature. */ + && tapt && tapt->clef && tapt->prof && tapt->enof /* Check if required boxes exist. */ + && !((isom_stsd_t *)visual->parent)->list->entry_count; /* Multiple sample description might conflict with this, so in that case, disable this feature. + * Note: this sample description isn't added yet here. */ + if( !set_aperture_modes ) + isom_remove_tapt( trak->tapt ); + /* Set up Clean Aperture. */ + if( set_aperture_modes || summary->crop_top || summary->crop_left || summary->crop_bottom || summary->crop_right ) + { + if( isom_add_clap( visual ) ) + { + isom_remove_visual_extensions( visual ); + return -1; + } + isom_clap_t *clap = visual->clap; + clap->cleanApertureWidthN = summary->width - (summary->crop_left + summary->crop_right); + clap->cleanApertureHeightN = summary->height - (summary->crop_top + summary->crop_bottom); + clap->horizOffN = (int64_t)summary->crop_left - summary->crop_right; + clap->vertOffN = (int64_t)summary->crop_top - summary->crop_bottom; + if( !(clap->horizOffN & 0x1) ) + { + clap->horizOffN /= 2; + clap->horizOffD = 1; + } + else + clap->horizOffD = 2; + if( !(clap->vertOffN & 0x1) ) + { + clap->vertOffN /= 2; + clap->vertOffD = 1; + } + else + clap->vertOffD = 2; + } + /* Set up Pixel Aspect Ratio. */ + if( set_aperture_modes || (summary->par_h && summary->par_v) ) + { + if( isom_add_pasp( visual ) ) + { + isom_remove_visual_extensions( visual ); + return -1; + } + isom_pasp_t *pasp = visual->pasp; + pasp->hSpacing = summary->par_h; + pasp->vSpacing = summary->par_v; + } + /* Set up Color Parameter. */ + if( qt_compatible && (summary->primaries || summary->transfer || summary->matrix) ) + { + if( isom_add_colr( visual ) ) + { + isom_remove_visual_extensions( visual ); + return -1; + } + isom_colr_t *colr = visual->colr; + uint16_t primaries = summary->primaries; + uint16_t transfer = summary->transfer; + uint16_t matrix = summary->matrix; + /* Set 'nclc' to parameter type, we don't support 'prof'. */ + colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC; + /* primaries */ + if( primaries >= QT_COLOR_PARAMETER_END ) + return -1; + else if( primaries > UINT16_MAX ) + colr->primaries_index = isom_color_parameter_tbl[primaries - UINT16_MAX_PLUS_ONE].primaries; + else + colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) ? primaries : 2; + /* transfer */ + if( transfer >= QT_COLOR_PARAMETER_END ) + return -1; + else if( transfer > UINT16_MAX ) + colr->transfer_function_index = isom_color_parameter_tbl[transfer - UINT16_MAX_PLUS_ONE].transfer; + else + colr->transfer_function_index = (transfer == 1 || transfer == 7) ? transfer : 2; + /* matrix */ + if( matrix >= QT_COLOR_PARAMETER_END ) + return -1; + else if( matrix > UINT16_MAX ) + colr->matrix_index = isom_color_parameter_tbl[matrix - UINT16_MAX_PLUS_ONE].matrix; + else + colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7 ) ? matrix : 2; + } + /* Set up Sample Scaling. */ + if( !qt_compatible && summary->scaling_method ) + { + if( isom_add_stsl( visual ) ) + { + isom_remove_visual_extensions( visual ); + return -1; + } + isom_stsl_t *stsl = visual->stsl; + stsl->constraint_flag = 1; + stsl->scale_method = summary->scaling_method; + } + /* Set up AVC Decoder Configuration. */ + static const uint32_t avc_type[] = + { + ISOM_CODEC_TYPE_AVC1_VIDEO, + ISOM_CODEC_TYPE_AVC2_VIDEO, + ISOM_CODEC_TYPE_AVCP_VIDEO + }; + for( int i = 0; i < sizeof(avc_type)/sizeof(avc_type[0]); i++ ) + if( visual->type == avc_type[i] ) + { + if( isom_add_avcC( visual ) ) + return -1; + break; + } + /* Set up Track Apeture Modes. */ + if( set_aperture_modes ) + { + uint32_t width = visual->width << 16; + uint32_t height = visual->height << 16; + double clap_width = ((double)visual->clap->cleanApertureWidthN / visual->clap->cleanApertureWidthD) * (1<<16); + double clap_height = ((double)visual->clap->cleanApertureHeightN / visual->clap->cleanApertureHeightD) * (1<<16); + double par = (double)visual->pasp->hSpacing / visual->pasp->vSpacing; + if( par >= 1.0 ) + { + tapt->clef->width = clap_width * par; + tapt->clef->height = clap_height; + tapt->prof->width = width * par; + tapt->prof->height = height; + } + else + { + tapt->clef->width = clap_width; + tapt->clef->height = clap_height / par; + tapt->prof->width = width; + tapt->prof->height = height / par; + } + tapt->enof->width = width; + tapt->enof->height = height; + } + return 0; +} + +static int isom_add_visual_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_video_summary_t *summary ) +{ + if( !stsd || !stsd->list || !summary ) + return -1; + lsmash_entry_list_t *list = stsd->list; + isom_visual_entry_t *visual = 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; + switch( sample_type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + strcpy( visual->compressorname, "\012AVC Coding" ); + break; + case ISOM_CODEC_TYPE_AVCP_VIDEO : + strcpy( visual->compressorname, "\016AVC Parameters" ); + break; + default : + break; + } + visual->depth = 0x0018; + visual->color_table_ID = -1; + if( isom_add_visual_extensions( visual, summary ) + || lsmash_add_entry( list, visual ) ) + { + isom_remove_visual_extensions( visual ); + free( visual ); + return -1; + } + return 0; +} + +#if 0 +static int isom_add_mp4s_entry( isom_stsd_t *stsd ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_mp4s_entry_t *mp4s = 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; +} +#endif + +int isom_add_wave( isom_audio_entry_t *audio ) +{ + if( !audio || audio->wave ) + return -1; + isom_create_box( wave, audio, QT_BOX_TYPE_WAVE ); + audio->wave = wave; + return 0; +} + +int isom_add_frma( isom_wave_t *wave ) +{ + if( !wave || wave->frma ) + return -1; + isom_create_box( frma, wave, QT_BOX_TYPE_FRMA ); + wave->frma = frma; + return 0; +} + +int isom_add_enda( isom_wave_t *wave ) +{ + if( !wave || wave->enda ) + return -1; + isom_create_box( enda, wave, QT_BOX_TYPE_ENDA ); + wave->enda = enda; + return 0; +} + +int isom_add_mp4a( isom_wave_t *wave ) +{ + if( !wave || wave->mp4a ) + return -1; + isom_create_box( mp4a, wave, QT_BOX_TYPE_MP4A ); + wave->mp4a = mp4a; + return 0; +} + +int isom_add_terminator( isom_wave_t *wave ) +{ + if( !wave || wave->terminator ) + return -1; + isom_create_box( terminator, wave, QT_BOX_TYPE_TERMINATOR ); + wave->terminator = terminator; + return 0; +} + +int isom_add_chan( isom_audio_entry_t *audio ) +{ + if( !audio || audio->chan ) + return -1; + isom_create_box( chan, audio, QT_BOX_TYPE_CHAN ); + chan->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN; + audio->chan = chan; + return 0; +} + +static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio ) +{ + lsmash_audio_summary_t *summary = &audio->summary; + if( isom_add_wave( audio ) + || isom_add_frma( audio->wave ) + || isom_add_mp4a( audio->wave ) + || isom_add_terminator( audio->wave ) ) + return -1; + audio->data_reference_index = 1; + audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1; + audio->channelcount = audio->version == 2 ? 3 : LSMASH_MIN( summary->channels, 2 ); + audio->samplesize = 16; + audio->compression_ID = QT_COMPRESSION_ID_VARIABLE_COMPRESSION; + audio->packet_size = 0; + if( audio->version == 1 ) + { + audio->samplerate = summary->frequency << 16; + audio->samplesPerPacket = summary->samples_in_frame; + audio->bytesPerPacket = 1; /* Apparently, this field is set to 1. */ + audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; + audio->bytesPerSample = 1 + (summary->bit_depth != 8); + } + else /* audio->version == 2 */ + { + audio->samplerate = 0x00010000; + audio->sizeOfStructOnly = 72; + audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i; + audio->numAudioChannels = summary->channels; + audio->always7F000000 = 0x7F000000; + audio->constBitsPerChannel = 0; /* compressed audio */ + audio->formatSpecificFlags = 0; + audio->constBytesPerAudioPacket = 0; /* variable */ + audio->constLPCMFramesPerAudioPacket = summary->samples_in_frame; + } + audio->wave->frma->data_format = audio->type; + /* create ES Descriptor */ + isom_esds_t *esds = malloc( sizeof(isom_esds_t) ); + if( !esds ) + return -1; + 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 ) + return -1; + audio->wave->esds = esds; + return 0; +} + +static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio ) +{ + lsmash_audio_summary_t *summary = &audio->summary; + if( summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream ) + return -1; + switch( summary->object_type_indication ) + { + case MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3: + case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile: + case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile: + case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile: + case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3: /* Legacy Interface */ + case MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3: /* Legacy Interface */ + break; + default: + return -1; + } + isom_create_box( esds, audio, ISOM_BOX_TYPE_ESDS ); + mp4sys_ES_Descriptor_params_t esd_param; + esd_param.ES_ID = 0; /* This is esds internal, so 0 is allowed. */ + esd_param.objectTypeIndication = summary->object_type_indication; + esd_param.streamType = summary->stream_type; + esd_param.bufferSizeDB = 0; /* NOTE: ISO/IEC 14496-3 does not mention this, so we use 0. */ + esd_param.maxBitrate = 0; /* This will be updated later if needed. or... I think this can be arbitrary value. */ + esd_param.avgBitrate = 0; /* FIXME: 0 if VBR. */ + esd_param.dsi_payload = summary->exdata; + esd_param.dsi_payload_length = summary->exdata_length; + esds->ES = mp4sys_setup_ES_Descriptor( &esd_param ); + if( !esds->ES ) + return -1; + audio->data_reference_index = 1; + /* WARNING: This field cannot retain frequency above 65535Hz. + This is not "FIXME", I just honestly implemented what the spec says. + BTW, who ever expects sampling frequency takes fixed-point decimal??? */ + audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0; + /* In pure mp4 file, these "template" fields shall be default values according to the spec. + But not pure - hybrid with other spec - mp4 file can take other values. + Which is to say, these template values shall be ignored in terms of mp4, except some object_type_indications. + see 14496-14, "Template fields used". */ + audio->channelcount = 2; + audio->samplesize = 16; + audio->esds = esds; + return 0; +} + +static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio ) +{ + uint32_t sample_type = audio->type; + lsmash_audio_summary_t *summary = &audio->summary; + /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */ + if( (sample_type == QT_CODEC_TYPE_RAW_AUDIO && (summary->bit_depth != 8 || summary->sample_format)) + || (sample_type == QT_CODEC_TYPE_FL32_AUDIO && (summary->bit_depth != 32 || !summary->sample_format)) + || (sample_type == QT_CODEC_TYPE_FL64_AUDIO && (summary->bit_depth != 64 || !summary->sample_format)) + || (sample_type == QT_CODEC_TYPE_IN24_AUDIO && (summary->bit_depth != 24 || summary->sample_format)) + || (sample_type == QT_CODEC_TYPE_IN32_AUDIO && (summary->bit_depth != 32 || summary->sample_format)) + || (sample_type == QT_CODEC_TYPE_23NI_AUDIO && (summary->bit_depth != 32 || summary->sample_format || !summary->endianness)) + || (sample_type == QT_CODEC_TYPE_SOWT_AUDIO && (summary->bit_depth != 16 || summary->sample_format || !summary->endianness)) + || (sample_type == QT_CODEC_TYPE_TWOS_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness)) + || (sample_type == QT_CODEC_TYPE_NONE_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness)) + || (sample_type == QT_CODEC_TYPE_NOT_SPECIFIED && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness)) + || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->bit_depth % 8) ) + { + audio->type = QT_CODEC_TYPE_LPCM_AUDIO; + audio->version = 2; + } + else if( sample_type == QT_CODEC_TYPE_LPCM_AUDIO ) + audio->version = 2; + else if( summary->bit_depth > 16 + || (sample_type != QT_CODEC_TYPE_RAW_AUDIO && sample_type != QT_CODEC_TYPE_TWOS_AUDIO + && sample_type != QT_CODEC_TYPE_NONE_AUDIO && sample_type != QT_CODEC_TYPE_NOT_SPECIFIED) ) + audio->version = 1; + audio->data_reference_index = 1; + /* Set up constBytesPerAudioPacket field. + * We use constBytesPerAudioPacket as the actual size of audio frame even when version is not 2. */ + audio->constBytesPerAudioPacket = (summary->bit_depth * summary->channels) / 8; + /* Set up other fields in this description by its version. */ + if( audio->version == 2 ) + { + audio->channelcount = 3; + audio->samplesize = 16; + audio->compression_ID = -2; + audio->samplerate = 0x00010000; + audio->sizeOfStructOnly = 72; + audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i; + audio->numAudioChannels = summary->channels; + audio->always7F000000 = 0x7F000000; + audio->constBitsPerChannel = summary->bit_depth; + audio->constLPCMFramesPerAudioPacket = 1; + if( summary->sample_format ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_FLOAT; + if( sample_type == QT_CODEC_TYPE_TWOS_AUDIO || !summary->endianness ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN; + if( !summary->sample_format && summary->signedness ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER; + if( summary->packed ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_PACKED; + if( !summary->packed && summary->alignment ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH; + if( !summary->interleaved ) + audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED; + } + else if( audio->version == 1 ) + { + audio->channelcount = summary->channels; + audio->samplesize = 16; + /* Audio formats other than 'raw ' and 'twos' are treated as compressed audio. */ + if( sample_type == QT_CODEC_TYPE_RAW_AUDIO || sample_type == QT_CODEC_TYPE_TWOS_AUDIO ) + audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED; + else + audio->compression_ID = QT_COMPRESSION_ID_FIXED_COMPRESSION; + audio->samplerate = summary->frequency << 16; + audio->samplesPerPacket = 1; + audio->bytesPerPacket = summary->bit_depth / 8; + audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; /* sample_size field in stsz box is NOT used. */ + audio->bytesPerSample = 1 + (summary->bit_depth != 8); + if( sample_type == QT_CODEC_TYPE_FL32_AUDIO || sample_type == QT_CODEC_TYPE_FL64_AUDIO + || sample_type == QT_CODEC_TYPE_IN24_AUDIO || sample_type == QT_CODEC_TYPE_IN32_AUDIO ) + { + if( isom_add_wave( audio ) + || isom_add_frma( audio->wave ) + || isom_add_enda( audio->wave ) + || isom_add_terminator( audio->wave ) ) + return -1; + audio->wave->frma->data_format = sample_type; + audio->wave->enda->littleEndian = summary->endianness; + } + } + else /* audio->version == 0 */ + { + audio->channelcount = summary->channels; + audio->samplesize = summary->bit_depth; + audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED; + audio->samplerate = summary->frequency << 16; + } + return 0; +} + +static int isom_set_extra_description( isom_audio_entry_t *audio ) +{ + lsmash_audio_summary_t *summary = &audio->summary; + audio->data_reference_index = 1; + audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0; + audio->channelcount = 2; + audio->samplesize = 16; + if( summary->exdata ) + { + audio->exdata_length = summary->exdata_length; + audio->exdata = malloc( audio->exdata_length ); + if( !audio->exdata ) + return -1; + memcpy( audio->exdata, summary->exdata, audio->exdata_length ); + } + else + audio->exdata = NULL; + return 0; +} + +static int isom_add_audio_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_audio_summary_t *summary ) +{ + if( !stsd || !stsd->list || !summary ) + return -1; + isom_audio_entry_t *audio = 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 ); + memcpy( &audio->summary, summary, sizeof(lsmash_audio_summary_t) ); + int ret = 0; + lsmash_root_t *root = stsd->root; + if( sample_type == ISOM_CODEC_TYPE_MP4A_AUDIO ) + { + if( root->ftyp && root->ftyp->major_brand == ISOM_BRAND_TYPE_QT ) + ret = isom_set_qtff_mp4a_description( audio ); + else + ret = isom_set_isom_mp4a_description( audio ); + } + else if( isom_is_lpcm_audio( sample_type ) ) + ret = isom_set_qtff_lpcm_description( audio ); + else + ret = isom_set_extra_description( audio ); + if( ret ) + goto fail; + if( root->qt_compatible ) + { + lsmash_channel_layout_tag_code layout_tag = summary->layout_tag; + lsmash_channel_bitmap_code bitmap = summary->bitmap; + if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS /* We don't support the feature of Channel Descriptions. */ + || (layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!bitmap || bitmap > QT_CHANNEL_BIT_FULL)) ) + { + layout_tag = summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN | summary->channels; + bitmap = summary->bitmap = 0; + } + /* Don't create Channel Compositor Box if the channel layout is unknown. */ + if( (layout_tag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 ) + { + if( isom_add_chan( audio ) ) + goto fail; + audio->chan->channelLayoutTag = layout_tag; + audio->chan->channelBitmap = bitmap; + } + } + if( lsmash_add_entry( stsd->list, audio ) ) + goto fail; + return 0; +fail: + isom_remove_esds( audio->esds ); + isom_remove_wave( audio->wave ); + isom_remove_chan( audio->chan ); + if( audio->exdata ) + free( audio->exdata ); + free( audio ); + return -1; +} + +static int isom_add_text_entry( isom_stsd_t *stsd ) +{ + if( !stsd || !stsd->list ) + return -1; + isom_text_entry_t *text = 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; +} + +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 : + case ISOM_CODEC_TYPE_MP4V_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : +#endif + ret = isom_add_visual_entry( stsd, sample_type, (lsmash_video_summary_t *)summary ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + ret = isom_add_mp4s_entry( stsd ); + break; +#endif + case ISOM_CODEC_TYPE_MP4A_AUDIO : + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + case ISOM_CODEC_TYPE_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 + ret = isom_add_audio_entry( stsd, sample_type, (lsmash_audio_summary_t *)summary ); + break; + case ISOM_CODEC_TYPE_TX3G_TEXT : + ret = isom_add_tx3g_entry( stsd ); + break; + case QT_CODEC_TYPE_TEXT_TEXT : + ret = isom_add_text_entry( stsd ); + break; + default : + return 0; + } + return ret ? 0 : list->entry_count; +} + +static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta ) +{ + if( !stbl || !stbl->stts || !stbl->stts->list ) + return -1; + isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) ); + if( !data ) + return -1; + data->sample_count = 1; + data->sample_delta = sample_delta; + if( lsmash_add_entry( stbl->stts->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset ) +{ + if( !stbl || !stbl->ctts || !stbl->ctts->list ) + return -1; + isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) ); + if( !data ) + return -1; + data->sample_count = 1; + data->sample_offset = sample_offset; + if( lsmash_add_entry( stbl->ctts->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index ) +{ + if( !stbl || !stbl->stsc || !stbl->stsc->list ) + return -1; + isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) ); + if( !data ) + return -1; + data->first_chunk = first_chunk; + data->samples_per_chunk = samples_per_chunk; + data->sample_description_index = sample_description_index; + if( lsmash_add_entry( stbl->stsc->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size ) +{ + if( !stbl || !stbl->stsz ) + return -1; + isom_stsz_t *stsz = stbl->stsz; + /* retrieve initial sample_size */ + if( !stsz->sample_count ) + stsz->sample_size = entry_size; + /* if it seems constant access_unit size at present, update sample_count only */ + if( !stsz->list && stsz->sample_size == entry_size ) + { + ++ stsz->sample_count; + return 0; + } + /* found sample_size varies, create sample_size list */ + if( !stsz->list ) + { + stsz->list = lsmash_create_entry_list(); + if( !stsz->list ) + return -1; + for( uint32_t i = 0; i < stsz->sample_count; i++ ) + { + isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return -1; + data->entry_size = stsz->sample_size; + if( lsmash_add_entry( stsz->list, data ) ) + { + free( data ); + return -1; + } + } + stsz->sample_size = 0; + } + isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return -1; + data->entry_size = entry_size; + if( lsmash_add_entry( stsz->list, data ) ) + { + free( data ); + return -1; + } + ++ stsz->sample_count; + return 0; +} + +static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number ) +{ + if( !stbl || !stbl->stss || !stbl->stss->list ) + return -1; + isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) ); + if( !data ) + return -1; + data->sample_number = sample_number; + if( lsmash_add_entry( stbl->stss->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number ) +{ + if( !stbl || !stbl->stps || !stbl->stps->list ) + return -1; + isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) ); + if( !data ) + return -1; + data->sample_number = sample_number; + if( lsmash_add_entry( stbl->stps->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_sdtp_entry( isom_stbl_t *stbl, lsmash_sample_property_t *prop, uint8_t avc_extensions ) +{ + if( !prop ) + return -1; + if( !stbl || !stbl->sdtp || !stbl->sdtp->list ) + return -1; + isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) ); + if( !data ) + return -1; + /* isom_sdtp_entry_t is smaller than lsmash_sample_property_t. */ + data->is_leading = (avc_extensions ? prop->leading : prop->allow_earlier) & 0x03; + data->sample_depends_on = prop->independent & 0x03; + data->sample_is_depended_on = prop->disposable & 0x03; + data->sample_has_redundancy = prop->redundant & 0x03; + if( lsmash_add_entry( stbl->sdtp->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_add_co64( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stco ) + return -1; + isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_CO64 ); + stco->large_presentation = 1; + stbl->stco = stco; + return 0; +} + +static int isom_add_stco( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stco ) + return -1; + isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_STCO ); + stco->large_presentation = 0; + stbl->stco = stco; + return 0; +} + +static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset ) +{ + if( !stbl || !stbl->stco || !stbl->stco->list ) + return -1; + isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) ); + if( !data ) + return -1; + data->chunk_offset = chunk_offset; + if( lsmash_add_entry( stbl->stco->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +static int isom_convert_stco_to_co64( isom_stbl_t* stbl ) +{ + /* backup stco */ + isom_stco_t *stco = stbl->stco; + stbl->stco = NULL; + if( isom_add_co64( stbl ) ) + return -1; + /* move chunk_offset to co64 from stco */ + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + { + isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data; + if( isom_add_co64_entry( stbl, data->chunk_offset ) ) + return -1; + } + lsmash_remove_list( stco->list, NULL ); + free( stco ); + return 0; +} + +static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset ) +{ + if( !stbl || !stbl->stco || !stbl->stco->list ) + return -1; + if( stbl->stco->large_presentation ) + return isom_add_co64_entry( stbl, chunk_offset ); + if( chunk_offset > UINT32_MAX ) + { + if( isom_convert_stco_to_co64( stbl ) ) + return -1; + return isom_add_co64_entry( stbl, chunk_offset ); + } + isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) ); + if( !data ) + return -1; + data->chunk_offset = (uint32_t)chunk_offset; + if( lsmash_add_entry( stbl->stco->list, data ) ) + { + free( data ); + return -1; + } + return 0; +} + +isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl->sgpd_list ) + return NULL; + for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next ) + { + isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data; + if( !sgpd || !sgpd->list ) + return NULL; + if( sgpd->grouping_type == grouping_type ) + return sgpd; + } + return NULL; +} + +isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl->sbgp_list ) + return NULL; + for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next ) + { + isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)entry->data; + if( !sbgp || !sbgp->list ) + return NULL; + if( sbgp->grouping_type == grouping_type ) + return sbgp; + } + return NULL; +} + +static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_entry_t *sgpd ) +{ + if( !sgpd ) + return NULL; + isom_rap_entry_t *data = malloc( sizeof(isom_rap_entry_t) ); + if( !data ) + return NULL; + memset( data, 0, sizeof(isom_rap_entry_t) ); + if( lsmash_add_entry( sgpd->list, data ) ) + { + free( data ); + return NULL; + } + return data; +} + +static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_entry_t *sgpd, int16_t roll_distance ) +{ + if( !sgpd ) + return NULL; + isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) ); + if( !data ) + return NULL; + data->description_length = 0; + data->roll_distance = roll_distance; + if( lsmash_add_entry( sgpd->list, data ) ) + { + free( data ); + return NULL; + } + return data; +} + +static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_entry_t *sbgp, uint32_t sample_count, uint32_t group_description_index ) +{ + if( !sbgp ) + return NULL; + isom_group_assignment_entry_t *data = malloc( sizeof(isom_group_assignment_entry_t) ); + if( !data ) + return NULL; + data->sample_count = sample_count; + data->group_description_index = group_description_index; + if( lsmash_add_entry( sbgp->list, data ) ) + { + free( data ); + return NULL; + } + return data; +} + +static int isom_add_chpl_entry( isom_chpl_t *chpl, isom_chapter_entry_t *chap_data ) +{ + if( !chap_data->chapter_name || !chpl || !chpl->list ) + return -1; + isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) ); + if( !data ) + return -1; + data->start_time = chap_data->start_time; + data->chapter_name_length = strlen( chap_data->chapter_name ); + data->chapter_name = ( char* )malloc( data->chapter_name_length + 1 ); + if( !data->chapter_name ) + { + free( data ); + return -1; + } + memcpy( data->chapter_name, chap_data->chapter_name, data->chapter_name_length ); + data->chapter_name[data->chapter_name_length] = '\0'; + if( lsmash_add_entry( chpl->list, data ) ) + { + free( data->chapter_name ); + free( data ); + return -1; + } + return 0; +} + +static isom_trex_entry_t *isom_add_trex( isom_mvex_t *mvex ) +{ + if( !mvex ) + return NULL; + if( !mvex->trex_list ) + { + mvex->trex_list = lsmash_create_entry_list(); + if( !mvex->trex_list ) + return NULL; + } + isom_trex_entry_t *trex = malloc( sizeof(isom_trex_entry_t) ); + if( !trex ) + return NULL; + memset( trex, 0, sizeof(isom_trex_entry_t) ); + isom_init_box_common( trex, mvex, ISOM_BOX_TYPE_TREX ); + if( lsmash_add_entry( mvex->trex_list, trex ) ) + { + free( trex ); + return NULL; + } + return trex; +} + +static isom_trun_entry_t *isom_add_trun( isom_traf_entry_t *traf ) +{ + if( !traf ) + return NULL; + if( !traf->trun_list ) + { + traf->trun_list = lsmash_create_entry_list(); + if( !traf->trun_list ) + return NULL; + } + isom_trun_entry_t *trun = malloc( sizeof(isom_trun_entry_t) ); + if( !trun ) + return NULL; + memset( trun, 0, sizeof(isom_trun_entry_t) ); + isom_init_box_common( trun, traf, ISOM_BOX_TYPE_TRUN ); + if( lsmash_add_entry( traf->trun_list, trun ) ) + { + free( trun ); + return NULL; + } + return trun; +} + +static isom_traf_entry_t *isom_add_traf( lsmash_root_t *root, isom_moof_entry_t *moof ) +{ + if( !root || !root->moof_list || !moof ) + return NULL; + if( !moof->traf_list ) + { + moof->traf_list = lsmash_create_entry_list(); + if( !moof->traf_list ) + return NULL; + } + isom_traf_entry_t *traf = malloc( sizeof(isom_traf_entry_t) ); + if( !traf ) + return NULL; + memset( traf, 0, sizeof(isom_traf_entry_t) ); + isom_init_box_common( traf, moof, ISOM_BOX_TYPE_TRAF ); + isom_cache_t *cache = malloc( sizeof(isom_cache_t) ); + if( !cache ) + { + free( traf ); + return NULL; + } + memset( cache, 0, sizeof(isom_cache_t) ); + if( lsmash_add_entry( moof->traf_list, traf ) ) + { + free( cache ); + free( traf ); + return NULL; + } + traf->cache = cache; + return traf; +} + +static isom_moof_entry_t *isom_add_moof( lsmash_root_t *root ) +{ + if( !root ) + return NULL; + if( !root->moof_list ) + { + root->moof_list = lsmash_create_entry_list(); + if( !root->moof_list ) + return NULL; + } + isom_moof_entry_t *moof = malloc( sizeof(isom_moof_entry_t) ); + if( !moof ) + return NULL; + memset( moof, 0, sizeof(isom_moof_entry_t) ); + isom_init_box_common( moof, root, ISOM_BOX_TYPE_MOOF ); + if( lsmash_add_entry( root->moof_list, moof ) ) + { + free( moof ); + return NULL; + } + return moof; +} + +static isom_tfra_entry_t *isom_add_tfra( isom_mfra_t *mfra ) +{ + if( !mfra ) + return NULL; + if( !mfra->tfra_list ) + { + mfra->tfra_list = lsmash_create_entry_list(); + if( !mfra->tfra_list ) + return NULL; + } + isom_tfra_entry_t *tfra = malloc( sizeof(isom_tfra_entry_t) ); + if( !tfra ) + return NULL; + memset( tfra, 0, sizeof(isom_tfra_entry_t) ); + isom_init_box_common( tfra, mfra, ISOM_BOX_TYPE_TFRA ); + if( lsmash_add_entry( mfra->tfra_list, tfra ) ) + { + free( tfra ); + return NULL; + } + return tfra; +} + +static int isom_add_ftyp( lsmash_root_t *root ) +{ + if( root->ftyp ) + return -1; + isom_create_box( ftyp, root, ISOM_BOX_TYPE_FTYP ); + ftyp->size = ISOM_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, mp4a_get_audioProfileLevelIndication( &((isom_audio_entry_t*)sample_entry)->summary ) ); + break; +#if 0 + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + /* FIXME: Do we have to arbitrate like audio? */ + if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED ) + *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED; + break; +#endif + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + case ISOM_CODEC_TYPE_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_AUDIO : +#ifdef LSMASH_DEMUXER_ENABLED + case ISOM_CODEC_TYPE_EC_3_AUDIO : +#endif +#if 0 + case ISOM_CODEC_TYPE_DRA1_AUDIO : + case ISOM_CODEC_TYPE_DTSC_AUDIO : + case ISOM_CODEC_TYPE_DTSH_AUDIO : + case ISOM_CODEC_TYPE_DTSL_AUDIO : + case ISOM_CODEC_TYPE_ENCA_AUDIO : + case ISOM_CODEC_TYPE_G719_AUDIO : + case ISOM_CODEC_TYPE_G726_AUDIO : + case ISOM_CODEC_TYPE_M4AE_AUDIO : + case ISOM_CODEC_TYPE_MLPA_AUDIO : + case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAWP_AUDIO : + case ISOM_CODEC_TYPE_SEVC_AUDIO : + case ISOM_CODEC_TYPE_SQCP_AUDIO : + case ISOM_CODEC_TYPE_SSMV_AUDIO : + case ISOM_CODEC_TYPE_TWOS_AUDIO : +#endif + /* NOTE: These audio codecs other than mp4a does not have appropriate pli. */ + *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + break; +#if 0 + case ISOM_CODEC_TYPE_FDP_HINT : + case ISOM_CODEC_TYPE_M2TS_HINT : + case ISOM_CODEC_TYPE_PM2T_HINT : + case ISOM_CODEC_TYPE_PRTP_HINT : + case ISOM_CODEC_TYPE_RM2T_HINT : + case ISOM_CODEC_TYPE_RRTP_HINT : + case ISOM_CODEC_TYPE_RSRP_HINT : + case ISOM_CODEC_TYPE_RTP_HINT : + case ISOM_CODEC_TYPE_SM2T_HINT : + case ISOM_CODEC_TYPE_SRTP_HINT : + /* FIXME: Do we have to set OD_profileLevelIndication? */ + break; + case ISOM_CODEC_TYPE_IXSE_META : + case ISOM_CODEC_TYPE_METT_META : + case ISOM_CODEC_TYPE_METX_META : + case ISOM_CODEC_TYPE_MLIX_META : + case ISOM_CODEC_TYPE_OKSD_META : + case ISOM_CODEC_TYPE_SVCM_META : + case ISOM_CODEC_TYPE_TEXT_META : + case ISOM_CODEC_TYPE_URIM_META : + case ISOM_CODEC_TYPE_XML_META : + /* FIXME: Do we have to set OD_profileLevelIndication? */ + break; +#endif + } + } + return 0; +} + +static int isom_add_iods( isom_moov_t *moov ) +{ + if( !moov || !moov->trak_list || moov->iods ) + return -1; + isom_create_box( iods, moov, ISOM_BOX_TYPE_IODS ); + iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */ + if( !iods->OD ) + { + free( iods ); + return -1; + } + mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED; + mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED; + for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data; + if( !trak || !trak->tkhd ) + return -1; + if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) ) + return -1; + if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) ) + return -1; + } + if( mp4sys_to_InitialObjectDescriptor( iods->OD, + 0, /* FIXME: I'm not quite sure what the spec says. */ + MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED, + audio_pli, visual_pli, + MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) ) + { + free( iods ); + return -1; + } + moov->iods = iods; + return 0; +} + +static int isom_add_tkhd( isom_trak_entry_t *trak, uint32_t handler_type ) +{ + if( !trak || !trak->root || !trak->root->moov || !trak->root->moov->mvhd || !trak->root->moov->trak_list ) + return -1; + if( !trak->tkhd ) + { + isom_create_box( tkhd, trak, ISOM_BOX_TYPE_TKHD ); + 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; +} + +int isom_add_elst( isom_edts_t *edts ) +{ + if( edts->elst ) + return 0; + isom_create_list_box( elst, edts, ISOM_BOX_TYPE_ELST ); + edts->elst = elst; + return 0; +} + +int isom_add_edts( isom_trak_entry_t *trak ) +{ + if( trak->edts ) + return 0; + isom_create_box( edts, trak, ISOM_BOX_TYPE_EDTS ); + trak->edts = edts; + return 0; +} + +static int isom_add_tref( isom_trak_entry_t *trak ) +{ + if( trak->tref ) + return 0; + isom_create_box( tref, trak, ISOM_BOX_TYPE_TREF ); + tref->ref_list = lsmash_create_entry_list(); + if( !tref->ref_list ) + { + free( tref ); + return -1; + } + trak->tref = tref; + return 0; +} + +static int isom_add_mdhd( isom_mdia_t *mdia, uint16_t default_language ) +{ + if( !mdia || mdia->mdhd ) + return -1; + isom_create_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD ); + mdhd->language = default_language; + mdia->mdhd = mdhd; + return 0; +} + +static int isom_add_mdia( isom_trak_entry_t *trak ) +{ + if( !trak || trak->mdia ) + return -1; + isom_create_box( mdia, trak, ISOM_BOX_TYPE_MDIA ); + trak->mdia = mdia; + return 0; +} + +static int isom_add_hdlr( isom_mdia_t *mdia, isom_minf_t *minf, uint32_t media_type ) +{ + 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 ); + lsmash_root_t *root = hdlr->root; + 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 isom_add_btrt( isom_visual_entry_t *visual ) +{ + if( !visual || visual->btrt ) + return -1; + isom_create_box( btrt, visual, ISOM_BOX_TYPE_BTRT ); + visual->btrt = btrt; + return 0; +} + +int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list ) + return -1; + isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + return isom_add_btrt( data ); +} + +static int isom_add_stts( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stts ) + return -1; + isom_create_list_box( stts, stbl, ISOM_BOX_TYPE_STTS ); + stbl->stts = stts; + return 0; +} + +static int isom_add_ctts( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->ctts ) + return -1; + isom_create_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS ); + stbl->ctts = ctts; + return 0; +} + +static int isom_add_cslg( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->cslg ) + return -1; + isom_create_box( cslg, stbl, ISOM_BOX_TYPE_CSLG ); + stbl->cslg = cslg; + return 0; +} + +static int isom_add_stsc( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stsc ) + return -1; + isom_create_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC ); + stbl->stsc = stsc; + return 0; +} + +static int isom_add_stsz( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stsz ) + return -1; + isom_create_box( stsz, stbl, ISOM_BOX_TYPE_STSZ ); /* We don't create a list here. */ + stbl->stsz = stsz; + return 0; +} + +static int isom_add_stss( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stss ) + return -1; + isom_create_list_box( stss, stbl, ISOM_BOX_TYPE_STSS ); + stbl->stss = stss; + return 0; +} + +static int isom_add_stps( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->stps ) + return -1; + isom_create_list_box( stps, stbl, QT_BOX_TYPE_STPS ); + stbl->stps = stps; + return 0; +} + +static int isom_add_sdtp( isom_stbl_t *stbl ) +{ + if( !stbl || stbl->sdtp ) + return -1; + isom_create_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP ); + stbl->sdtp = sdtp; + return 0; +} + +static isom_sgpd_entry_t *isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl ) + return NULL; + if( !stbl->sgpd_list ) + { + stbl->sgpd_list = lsmash_create_entry_list(); + if( !stbl->sgpd_list ) + return NULL; + } + isom_sgpd_entry_t *sgpd = malloc( sizeof(isom_sgpd_entry_t) ); + if( !sgpd ) + return NULL; + memset( sgpd, 0, sizeof(isom_sgpd_entry_t) ); + isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD ); + sgpd->list = lsmash_create_entry_list(); + if( !sgpd->list || lsmash_add_entry( stbl->sgpd_list, sgpd ) ) + { + free( sgpd ); + return NULL; + } + sgpd->grouping_type = grouping_type; + sgpd->version = 1; /* We use version 1 because it is recommended in the spec. */ + switch( grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + sgpd->default_length = 1; + break; + case ISOM_GROUP_TYPE_ROLL : + sgpd->default_length = 2; + break; + default : + /* We don't consider other grouping types currently. */ + break; + } + return sgpd; +} + +static isom_sbgp_entry_t *isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type ) +{ + if( !stbl ) + return NULL; + if( !stbl->sbgp_list ) + { + stbl->sbgp_list = lsmash_create_entry_list(); + if( !stbl->sbgp_list ) + return NULL; + } + isom_sbgp_entry_t *sbgp = malloc( sizeof(isom_sbgp_entry_t) ); + if( !sbgp ) + return NULL; + memset( sbgp, 0, sizeof(isom_sbgp_entry_t) ); + isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP ); + sbgp->list = lsmash_create_entry_list(); + if( !sbgp->list || lsmash_add_entry( stbl->sbgp_list, sbgp ) ) + { + free( sbgp ); + return NULL; + } + sbgp->grouping_type = grouping_type; + return sbgp; +} + +static int isom_add_stbl( isom_minf_t *minf ) +{ + if( !minf || minf->stbl ) + return -1; + isom_create_box( stbl, minf, ISOM_BOX_TYPE_STBL ); + minf->stbl = stbl; + return 0; +} + +static int isom_add_chpl( isom_moov_t *moov ) +{ + if( !moov || !moov->udta || moov->udta->chpl ) + return -1; + isom_create_list_box( chpl, moov, 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 ) + 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 ) + { + free( trak ); + return NULL; + } + memset( cache, 0, sizeof(isom_cache_t) ); + isom_fragment_t *fragment = NULL; + if( root->fragment ) + { + fragment = malloc( sizeof(isom_fragment_t) ); + if( !fragment ) + { + free( cache ); + free( trak ); + return NULL; + } + memset( fragment, 0, sizeof(isom_fragment_t) ); + cache->fragment = fragment; + } + if( lsmash_add_entry( moov->trak_list, trak ) ) + { + if( fragment ) + free( fragment ); + free( cache ); + free( trak ); + return NULL; + } + trak->cache = cache; + return trak; +} + +static int isom_add_mvex( isom_moov_t *moov ) +{ + if( !moov || moov->mvex ) + return -1; + isom_create_box( mvex, moov, ISOM_BOX_TYPE_MVEX ); + moov->mvex = mvex; + return 0; +} + +static int isom_add_mehd( isom_mvex_t *mvex ) +{ + if( !mvex || mvex->mehd ) + return -1; + isom_create_box( mehd, mvex, ISOM_BOX_TYPE_MEHD ); + mvex->mehd = mehd; + return 0; +} + +static int isom_add_tfhd( isom_traf_entry_t *traf ) +{ + if( !traf || traf->tfhd ) + return -1; + isom_create_box( tfhd, traf, ISOM_BOX_TYPE_TFHD ); + traf->tfhd = tfhd; + return 0; +} + +static int isom_add_mfhd( isom_moof_entry_t *moof ) +{ + if( !moof || moof->mfhd ) + return -1; + isom_create_box( mfhd, moof, ISOM_BOX_TYPE_MFHD ); + moof->mfhd = mfhd; + return 0; +} + +static int isom_add_mfra( lsmash_root_t *root ) +{ + if( !root || root->mfra ) + return -1; + isom_create_box( mfra, root, ISOM_BOX_TYPE_MFRA ); + root->mfra = mfra; + return 0; +} + +static int isom_add_mfro( isom_mfra_t *mfra ) +{ + if( !mfra || mfra->mfro ) + return -1; + isom_create_box( mfro, mfra, ISOM_BOX_TYPE_MFRO ); + mfra->mfro = mfro; + return 0; +} + +#define isom_remove_box( box_name, parent_type ) \ + do \ + { \ + parent_type *parent = (parent_type *)box_name->parent; \ + free( box_name ); \ + if( parent ) \ + parent->box_name = NULL; \ + } while( 0 ) + +static void isom_remove_ftyp( isom_ftyp_t *ftyp ) +{ + if( !ftyp ) + return; + if( ftyp->compatible_brands ) + free( ftyp->compatible_brands ); + isom_remove_box( ftyp, lsmash_root_t ); +} + +static void isom_remove_tkhd( isom_tkhd_t *tkhd ) +{ + if( !tkhd ) + return; + isom_remove_box( tkhd, isom_trak_entry_t ); +} + +static void isom_remove_clef( isom_clef_t *clef ) +{ + if( !clef ) + return; + isom_remove_box( clef, isom_tapt_t ); +} + +static void isom_remove_prof( isom_prof_t *prof ) +{ + if( !prof ) + return; + isom_remove_box( prof, isom_tapt_t ); +} + +static void isom_remove_enof( isom_enof_t *enof ) +{ + if( !enof ) + return; + isom_remove_box( enof, isom_tapt_t ); +} + +void isom_remove_tapt( isom_tapt_t *tapt ) +{ + if( !tapt ) + return; + isom_remove_clef( tapt->clef ); + isom_remove_prof( tapt->prof ); + isom_remove_enof( tapt->enof ); + isom_remove_box( tapt, isom_trak_entry_t ); +} + +static void isom_remove_elst( isom_elst_t *elst ) +{ + if( !elst ) + return; + lsmash_remove_list( elst->list, NULL ); + isom_remove_box( elst, isom_edts_t ); +} + +static void isom_remove_edts( isom_edts_t *edts ) +{ + if( !edts ) + return; + isom_remove_elst( edts->elst ); + isom_remove_box( edts, isom_trak_entry_t ); +} + +static void isom_remove_track_reference_type( isom_tref_type_t *ref ) +{ + if( !ref ) + return; + if( ref->track_ID ) + free( ref->track_ID ); + free( ref ); +} + +static void isom_remove_tref( isom_tref_t *tref ) +{ + if( !tref ) + return; + lsmash_remove_list( tref->ref_list, isom_remove_track_reference_type ); + isom_remove_box( tref, isom_trak_entry_t ); +} + +static void isom_remove_mdhd( isom_mdhd_t *mdhd ) +{ + if( !mdhd ) + return; + isom_remove_box( mdhd, isom_mdia_t ); +} + +static void isom_remove_vmhd( isom_vmhd_t *vmhd ) +{ + if( !vmhd ) + return; + isom_remove_box( vmhd, isom_minf_t ); +} + +static void isom_remove_smhd( isom_smhd_t *smhd ) +{ + if( !smhd ) + return; + isom_remove_box( smhd, isom_minf_t ); +} + +static void isom_remove_hmhd( isom_hmhd_t *hmhd ) +{ + if( !hmhd ) + return; + isom_remove_box( hmhd, isom_minf_t ); +} + +static void isom_remove_nmhd( isom_nmhd_t *nmhd ) +{ + if( !nmhd ) + return; + isom_remove_box( nmhd, isom_minf_t ); +} + +static void isom_remove_gmin( isom_gmin_t *gmin ) +{ + if( !gmin ) + return; + isom_remove_box( gmin, isom_gmhd_t ); +} + +static void isom_remove_text( isom_text_t *text ) +{ + if( !text ) + return; + isom_remove_box( text, isom_gmhd_t ); +} + +static void isom_remove_gmhd( isom_gmhd_t *gmhd ) +{ + if( !gmhd ) + return; + isom_remove_gmin( gmhd->gmin ); + isom_remove_text( gmhd->text ); + isom_remove_box( gmhd, isom_minf_t ); +} + +static void isom_remove_hdlr( isom_hdlr_t *hdlr ) +{ + if( !hdlr ) + return; + if( hdlr->componentName ) + free( hdlr->componentName ); + if( hdlr->parent ) + { + if( hdlr->parent->type == ISOM_BOX_TYPE_MDIA ) + isom_remove_box( hdlr, isom_mdia_t ); + else if( hdlr->parent->type == ISOM_BOX_TYPE_MINF ) + isom_remove_box( hdlr, isom_minf_t ); + else + assert( 0 ); + return; + } + free( hdlr ); +} + +void isom_remove_clap( isom_clap_t *clap ) +{ + if( !clap ) + return; + isom_remove_box( clap, isom_visual_entry_t ); +} + +void isom_remove_pasp( isom_pasp_t *pasp ) +{ + if( !pasp ) + return; + isom_remove_box( pasp, isom_visual_entry_t ); +} + +void isom_remove_colr( isom_colr_t *colr ) +{ + if( !colr ) + return; + isom_remove_box( colr, isom_visual_entry_t ); +} + +void isom_remove_stsl( isom_stsl_t *stsl ) +{ + if( !stsl ) + return; + isom_remove_box( stsl, isom_visual_entry_t ); +} + +static void isom_remove_esds( isom_esds_t *esds ) +{ + if( !esds ) + return; + mp4sys_remove_ES_Descriptor( esds->ES ); + if( esds->parent ) + { + switch( esds->parent->type ) + { + case ISOM_CODEC_TYPE_MP4V_VIDEO : + isom_remove_box( esds, isom_visual_entry_t ); + break; + case ISOM_CODEC_TYPE_MP4A_AUDIO : + case ISOM_CODEC_TYPE_M4AE_AUDIO : + isom_remove_box( esds, isom_audio_entry_t ); + break; + case QT_BOX_TYPE_WAVE : + isom_remove_box( esds, isom_wave_t ); + break; + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + isom_remove_box( esds, isom_mp4s_entry_t ); + break; + default : + assert( 0 ); + } + return; + } + free( esds ); +} + +void isom_remove_avcC( isom_avcC_t *avcC ) +{ + if( !avcC ) + return; + lsmash_remove_list( avcC->sequenceParameterSets, isom_remove_avcC_ps ); + lsmash_remove_list( avcC->pictureParameterSets, isom_remove_avcC_ps ); + lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps ); + isom_remove_box( avcC, isom_visual_entry_t ); +} + +void isom_remove_btrt( isom_btrt_t *btrt ) +{ + if( !btrt ) + return; + isom_remove_box( btrt, isom_visual_entry_t ); +} + +static void isom_remove_visual_extensions( isom_visual_entry_t *visual ) +{ + if( !visual ) + return; + isom_remove_clap( visual->clap ); + isom_remove_pasp( visual->pasp ); + isom_remove_colr( visual->colr ); + isom_remove_stsl( visual->stsl ); + isom_remove_esds( visual->esds ); + isom_remove_avcC( visual->avcC ); + isom_remove_btrt( visual->btrt ); +} + +static void isom_remove_font_record( isom_font_record_t *font_record ) +{ + if( !font_record ) + return; + if( font_record->font_name ) + free( font_record->font_name ); + free( font_record ); +} + +void isom_remove_ftab( isom_ftab_t *ftab ) +{ + if( !ftab ) + return; + lsmash_remove_list( ftab->list, isom_remove_font_record ); + isom_remove_box( ftab, isom_tx3g_entry_t ); +} + +void isom_remove_frma( isom_frma_t *frma ) +{ + if( !frma ) + return; + isom_remove_box( frma, isom_wave_t ); +} + +void isom_remove_enda( isom_enda_t *enda ) +{ + if( !enda ) + return; + isom_remove_box( enda, isom_wave_t ); +} + +void isom_remove_mp4a( isom_mp4a_t *mp4a ) +{ + if( !mp4a ) + return; + isom_remove_box( mp4a, isom_wave_t ); +} + +void isom_remove_terminator( isom_terminator_t *terminator ) +{ + if( !terminator ) + return; + isom_remove_box( terminator, isom_wave_t ); +} + +void isom_remove_wave( isom_wave_t *wave ) +{ + if( !wave ) + return; + isom_remove_frma( wave->frma ); + isom_remove_enda( wave->enda ); + isom_remove_mp4a( wave->mp4a ); + isom_remove_esds( wave->esds ); + isom_remove_terminator( wave->terminator ); + if( wave->exdata ) + free( wave->exdata ); + isom_remove_box( wave, isom_audio_entry_t ); +} + +void isom_remove_chan( isom_chan_t *chan ) +{ + if( !chan ) + return; + if( chan->channelDescriptions ) + free( chan->channelDescriptions ); + isom_remove_box( chan, isom_audio_entry_t ); +} + +void isom_remove_sample_description( isom_sample_entry_t *sample ) +{ + if( !sample ) + return; + switch( sample->type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + case ISOM_CODEC_TYPE_SVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + case ISOM_CODEC_TYPE_MP4V_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : + { + isom_visual_entry_t *visual = (isom_visual_entry_t *)sample; + isom_remove_visual_extensions( (isom_visual_entry_t *)visual ); + free( visual ); + break; + } + case ISOM_CODEC_TYPE_MP4A_AUDIO : + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + case ISOM_CODEC_TYPE_SAMR_AUDIO : + case ISOM_CODEC_TYPE_SAWB_AUDIO : + case QT_CODEC_TYPE_23NI_AUDIO : + case QT_CODEC_TYPE_NONE_AUDIO : + case QT_CODEC_TYPE_LPCM_AUDIO : + case QT_CODEC_TYPE_RAW_AUDIO : + case QT_CODEC_TYPE_SOWT_AUDIO : + case QT_CODEC_TYPE_TWOS_AUDIO : + case QT_CODEC_TYPE_FL32_AUDIO : + case QT_CODEC_TYPE_FL64_AUDIO : + case QT_CODEC_TYPE_IN24_AUDIO : + case QT_CODEC_TYPE_IN32_AUDIO : + case QT_CODEC_TYPE_NOT_SPECIFIED : + case ISOM_CODEC_TYPE_DRA1_AUDIO : + case ISOM_CODEC_TYPE_DTSC_AUDIO : + case ISOM_CODEC_TYPE_DTSH_AUDIO : + case ISOM_CODEC_TYPE_DTSL_AUDIO : + case ISOM_CODEC_TYPE_EC_3_AUDIO : + case ISOM_CODEC_TYPE_ENCA_AUDIO : + case ISOM_CODEC_TYPE_G719_AUDIO : + case ISOM_CODEC_TYPE_G726_AUDIO : + case ISOM_CODEC_TYPE_M4AE_AUDIO : + case ISOM_CODEC_TYPE_MLPA_AUDIO : + //case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAWP_AUDIO : + case ISOM_CODEC_TYPE_SEVC_AUDIO : + case ISOM_CODEC_TYPE_SQCP_AUDIO : + case ISOM_CODEC_TYPE_SSMV_AUDIO : + //case ISOM_CODEC_TYPE_TWOS_AUDIO : + { + isom_audio_entry_t *audio = (isom_audio_entry_t *)sample; + isom_remove_esds( audio->esds ); + isom_remove_wave( audio->wave ); + isom_remove_chan( audio->chan ); + if( audio->exdata ) + free( audio->exdata ); + free( audio ); + break; + } + case ISOM_CODEC_TYPE_FDP_HINT : + case ISOM_CODEC_TYPE_M2TS_HINT : + case ISOM_CODEC_TYPE_PM2T_HINT : + case ISOM_CODEC_TYPE_PRTP_HINT : + case ISOM_CODEC_TYPE_RM2T_HINT : + case ISOM_CODEC_TYPE_RRTP_HINT : + case ISOM_CODEC_TYPE_RSRP_HINT : + case ISOM_CODEC_TYPE_RTP_HINT : + case ISOM_CODEC_TYPE_SM2T_HINT : + case ISOM_CODEC_TYPE_SRTP_HINT : + { + isom_hint_entry_t *hint = (isom_hint_entry_t *)sample; + if( hint->data ) + free( hint->data ); + free( hint ); + break; + } + case ISOM_CODEC_TYPE_IXSE_META : + case ISOM_CODEC_TYPE_METT_META : + case ISOM_CODEC_TYPE_METX_META : + case ISOM_CODEC_TYPE_MLIX_META : + case ISOM_CODEC_TYPE_OKSD_META : + case ISOM_CODEC_TYPE_SVCM_META : + //case ISOM_CODEC_TYPE_TEXT_META : + case ISOM_CODEC_TYPE_URIM_META : + case ISOM_CODEC_TYPE_XML_META : + { + isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)sample; + free( metadata ); + break; + } + case ISOM_CODEC_TYPE_TX3G_TEXT : + { + isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)sample; + if( tx3g->ftab ) + isom_remove_ftab( tx3g->ftab ); + free( tx3g ); + break; + } + case QT_CODEC_TYPE_TEXT_TEXT : + { + isom_text_entry_t *text = (isom_text_entry_t *)sample; + if( text->font_name ) + free( text->font_name ); + free( text ); + break; + } + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + { + isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)sample; + isom_remove_esds( mp4s->esds ); + free( mp4s ); + break; + } + default : + break; + } +} + +static void isom_remove_stsd( isom_stsd_t *stsd ) +{ + if( !stsd ) + return; + lsmash_remove_list( stsd->list, isom_remove_sample_description ); + isom_remove_box( stsd, isom_stbl_t ); +} + +static void isom_remove_stts( isom_stts_t *stts ) +{ + if( !stts ) + return; + lsmash_remove_list( stts->list, NULL ); + isom_remove_box( stts, isom_stbl_t ); +} + +static void isom_remove_ctts( isom_ctts_t *ctts ) +{ + if( !ctts ) + return; + lsmash_remove_list( ctts->list, NULL ); + isom_remove_box( ctts, isom_stbl_t ); +} + +static void isom_remove_cslg( isom_cslg_t *cslg ) +{ + if( !cslg ) + return; + isom_remove_box( cslg, isom_stbl_t ); +} + +static void isom_remove_stsc( isom_stsc_t *stsc ) +{ + if( !stsc ) + return; + lsmash_remove_list( stsc->list, NULL ); + isom_remove_box( stsc, isom_stbl_t ); +} + +static void isom_remove_stsz( isom_stsz_t *stsz ) +{ + if( !stsz ) + return; + lsmash_remove_list( stsz->list, NULL ); + isom_remove_box( stsz, isom_stbl_t ); +} + +static void isom_remove_stss( isom_stss_t *stss ) +{ + if( !stss ) + return; + lsmash_remove_list( stss->list, NULL ); + isom_remove_box( stss, isom_stbl_t ); +} + +static void isom_remove_stps( isom_stps_t *stps ) +{ + if( !stps ) + return; + lsmash_remove_list( stps->list, NULL ); + isom_remove_box( stps, isom_stbl_t ); +} + +static void isom_remove_sdtp( isom_sdtp_t *sdtp ) +{ + if( !sdtp ) + return; + lsmash_remove_list( sdtp->list, NULL ); + isom_remove_box( sdtp, isom_stbl_t ); +} + +static void isom_remove_stco( isom_stco_t *stco ) +{ + if( !stco ) + return; + lsmash_remove_list( stco->list, NULL ); + isom_remove_box( stco, isom_stbl_t ); +} + +static void isom_remove_sgpd( isom_sgpd_entry_t *sgpd ) +{ + if( !sgpd ) + return; + lsmash_remove_list( sgpd->list, NULL ); + free( sgpd ); +} + +static void isom_remove_sbgp( isom_sbgp_entry_t *sbgp ) +{ + if( !sbgp ) + return; + lsmash_remove_list( sbgp->list, NULL ); + free( sbgp ); +} + +static void isom_remove_stbl( isom_stbl_t *stbl ) +{ + if( !stbl ) + return; + isom_remove_stsd( stbl->stsd ); + isom_remove_stts( stbl->stts ); + isom_remove_ctts( stbl->ctts ); + isom_remove_cslg( stbl->cslg ); + isom_remove_stsc( stbl->stsc ); + isom_remove_stsz( stbl->stsz ); + isom_remove_stss( stbl->stss ); + isom_remove_stps( stbl->stps ); + isom_remove_sdtp( stbl->sdtp ); + isom_remove_stco( stbl->stco ); + lsmash_remove_list( stbl->sgpd_list, isom_remove_sgpd ); + lsmash_remove_list( stbl->sbgp_list, isom_remove_sbgp ); + isom_remove_box( stbl, isom_minf_t ); +} + +static void isom_remove_dref( isom_dref_t *dref ) +{ + if( !dref ) + return; + if( !dref->list ) + { + free( dref ); + return; + } + for( lsmash_entry_t *entry = dref->list->head; entry; ) + { + isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data; + if( data ) + { + if( data->name ) + free( data->name ); + if( data->location ) + free( data->location ); + free( data ); + } + lsmash_entry_t *next = entry->next; + free( entry ); + entry = next; + } + free( dref->list ); + isom_remove_box( dref, isom_dinf_t ); +} + +static void isom_remove_dinf( isom_dinf_t *dinf ) +{ + if( !dinf ) + return; + isom_remove_dref( dinf->dref ); + isom_remove_box( dinf, isom_minf_t ); +} + +static void isom_remove_minf( isom_minf_t *minf ) +{ + if( !minf ) + return; + isom_remove_vmhd( minf->vmhd ); + isom_remove_smhd( minf->smhd ); + isom_remove_hmhd( minf->hmhd ); + isom_remove_nmhd( minf->nmhd ); + isom_remove_gmhd( minf->gmhd ); + isom_remove_hdlr( minf->hdlr ); + isom_remove_dinf( minf->dinf ); + isom_remove_stbl( minf->stbl ); + isom_remove_box( minf, isom_mdia_t ); +} + +static void isom_remove_mdia( isom_mdia_t *mdia ) +{ + if( !mdia ) + return; + isom_remove_mdhd( mdia->mdhd ); + isom_remove_minf( mdia->minf ); + isom_remove_hdlr( mdia->hdlr ); + isom_remove_box( mdia, isom_trak_entry_t ); +} + +static void isom_remove_chpl( isom_chpl_t *chpl ) +{ + if( !chpl ) + return; + if( !chpl->list ) + { + free( chpl ); + return; + } + for( lsmash_entry_t *entry = chpl->list->head; entry; ) + { + isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data; + if( data ) + { + if( data->chapter_name ) + free( data->chapter_name ); + free( data ); + } + lsmash_entry_t *next = entry->next; + free( entry ); + entry = next; + } + free( chpl->list ); + isom_remove_box( chpl, isom_udta_t ); +} + +static void isom_remove_udta( isom_udta_t *udta ) +{ + if( !udta ) + return; + isom_remove_chpl( udta->chpl ); + if( udta->parent ) + { + if( udta->parent->type == ISOM_BOX_TYPE_MOOV ) + isom_remove_box( udta, isom_moov_t ); + else if( udta->parent->type == ISOM_BOX_TYPE_TRAK ) + isom_remove_box( udta, isom_trak_entry_t ); + else + assert( 0 ); + return; + } + free( udta ); +} + +static void isom_remove_trak( isom_trak_entry_t *trak ) +{ + if( !trak ) + return; + isom_remove_tkhd( trak->tkhd ); + isom_remove_tapt( trak->tapt ); + isom_remove_edts( trak->edts ); + isom_remove_tref( trak->tref ); + isom_remove_mdia( trak->mdia ); + isom_remove_udta( trak->udta ); + if( trak->cache ) + { + lsmash_remove_list( trak->cache->chunk.pool, lsmash_delete_sample ); + lsmash_remove_list( trak->cache->roll.pool, NULL ); + if( trak->cache->rap ) + free( trak->cache->rap ); + free( trak->cache ); + } + free( trak ); /* Note: the list that contains this trak still has the address of the entry. */ +} + +static void isom_remove_iods( isom_iods_t *iods ) +{ + if( !iods ) + return; + mp4sys_remove_ObjectDescriptor( iods->OD ); + isom_remove_box( iods, isom_moov_t ); +} + +static void isom_remove_mehd( isom_mehd_t *mehd ) +{ + if( !mehd ) + return; + isom_remove_box( mehd, isom_mvex_t ); +} + +static void isom_remove_mvex( isom_mvex_t *mvex ) +{ + if( !mvex ) + return; + isom_remove_mehd( mvex->mehd ); + lsmash_remove_list( mvex->trex_list, NULL ); + isom_remove_box( mvex, isom_moov_t ); +} + +static void isom_remove_moov( lsmash_root_t *root ) +{ + if( !root || !root->moov ) + return; + isom_moov_t *moov = root->moov; + if( moov->mvhd ) + free( moov->mvhd ); + isom_remove_iods( moov->iods ); + isom_remove_udta( moov->udta ); + lsmash_remove_list( moov->trak_list, isom_remove_trak ); + isom_remove_mvex( moov->mvex ); + free( moov ); + root->moov = NULL; +} + +static void isom_remove_mfhd( isom_mfhd_t *mfhd ) +{ + if( !mfhd ) + return; + isom_remove_box( mfhd, isom_moof_entry_t ); +} + +static void isom_remove_tfhd( isom_tfhd_t *tfhd ) +{ + if( !tfhd ) + return; + isom_remove_box( tfhd, isom_traf_entry_t ); +} + +static void isom_remove_trun( isom_trun_entry_t *trun ) +{ + if( !trun ) + return; + lsmash_remove_list( trun->optional, NULL ); + free( trun ); /* Note: the list that contains this trun still has the address of the entry. */ +} + +static void isom_remove_traf( isom_traf_entry_t *traf ) +{ + if( !traf ) + return; + isom_remove_tfhd( traf->tfhd ); + lsmash_remove_list( traf->trun_list, isom_remove_trun ); + free( traf ); /* Note: the list that contains this traf still has the address of the entry. */ +} + +static void isom_remove_moof( isom_moof_entry_t *moof ) +{ + if( !moof ) + return; + isom_remove_mfhd( moof->mfhd ); + lsmash_remove_list( moof->traf_list, isom_remove_traf ); + free( moof ); +} + +static void isom_remove_mdat( isom_mdat_t *mdat ) +{ + if( !mdat ) + return; + isom_remove_box( mdat, lsmash_root_t ); +} + +static void isom_remove_free( isom_free_t *skip ) +{ + if( !skip ) + return; + if( skip->data ) + free( skip->data ); + lsmash_root_t *root = (lsmash_root_t *)skip->parent; + free( skip ); + root->free = NULL; +} + +static void isom_remove_tfra( isom_tfra_entry_t *tfra ) +{ + if( !tfra ) + return; + lsmash_remove_list( tfra->list, NULL ); + free( tfra ); +} + +static void isom_remove_mfro( isom_mfro_t *mfro ) +{ + if( !mfro ) + return; + isom_remove_box( mfro, isom_mfra_t ); +} + +static void isom_remove_mfra( isom_mfra_t *mfra ) +{ + if( !mfra ) + return; + lsmash_remove_list( mfra->tfra_list, isom_remove_tfra ); + isom_remove_mfro( mfra->mfro ); + isom_remove_box( mfra, lsmash_root_t ); +} + +/* Box writers */ +static int isom_write_tkhd( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_tkhd_t *tkhd = trak->tkhd; + if( !tkhd ) + return -1; + isom_bs_put_box_common( bs, tkhd ); + if( tkhd->version ) + { + lsmash_bs_put_be64( bs, tkhd->creation_time ); + lsmash_bs_put_be64( bs, tkhd->modification_time ); + lsmash_bs_put_be32( bs, tkhd->track_ID ); + lsmash_bs_put_be32( bs, tkhd->reserved1 ); + lsmash_bs_put_be64( bs, tkhd->duration ); + } + else + { + lsmash_bs_put_be32( bs, (uint32_t)tkhd->creation_time ); + lsmash_bs_put_be32( bs, (uint32_t)tkhd->modification_time ); + lsmash_bs_put_be32( bs, tkhd->track_ID ); + lsmash_bs_put_be32( bs, tkhd->reserved1 ); + lsmash_bs_put_be32( bs, (uint32_t)tkhd->duration ); + } + lsmash_bs_put_be32( bs, tkhd->reserved2[0] ); + lsmash_bs_put_be32( bs, tkhd->reserved2[1] ); + lsmash_bs_put_be16( bs, tkhd->layer ); + lsmash_bs_put_be16( bs, tkhd->alternate_group ); + lsmash_bs_put_be16( bs, tkhd->volume ); + lsmash_bs_put_be16( bs, tkhd->reserved3 ); + for( uint32_t i = 0; i < 9; i++ ) + lsmash_bs_put_be32( bs, tkhd->matrix[i] ); + lsmash_bs_put_be32( bs, tkhd->width ); + lsmash_bs_put_be32( bs, tkhd->height ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_clef( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_clef_t *clef = trak->tapt->clef; + if( !clef ) + return 0; + isom_bs_put_box_common( bs, clef ); + lsmash_bs_put_be32( bs, clef->width ); + lsmash_bs_put_be32( bs, clef->height ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_prof( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_prof_t *prof = trak->tapt->prof; + if( !prof ) + return 0; + isom_bs_put_box_common( bs, prof ); + lsmash_bs_put_be32( bs, prof->width ); + lsmash_bs_put_be32( bs, prof->height ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_enof( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_enof_t *enof = trak->tapt->enof; + if( !enof ) + return 0; + isom_bs_put_box_common( bs, enof ); + lsmash_bs_put_be32( bs, enof->width ); + lsmash_bs_put_be32( bs, enof->height ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_tapt( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_tapt_t *tapt = trak->tapt; + if( !tapt ) + return 0; + isom_bs_put_box_common( bs, tapt ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_clef( bs, trak ) + || isom_write_prof( bs, trak ) + || isom_write_enof( bs, trak ) ) + return -1; + return 0; +} + +static int isom_write_elst( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_elst_t *elst = trak->edts->elst; + if( !elst ) + return -1; + if( !elst->list->entry_count ) + return 0; + if( elst->root->fragment && elst->root->bs->stream != stdout ) + elst->pos = elst->root->bs->written; /* Remember to rewrite entries. */ + isom_bs_put_box_common( bs, elst ); + lsmash_bs_put_be32( bs, elst->list->entry_count ); + for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next ) + { + isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data; + if( !data ) + return -1; + if( elst->version ) + { + lsmash_bs_put_be64( bs, data->segment_duration ); + lsmash_bs_put_be64( bs, data->media_time ); + } + else + { + lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration ); + lsmash_bs_put_be32( bs, (uint32_t)data->media_time ); + } + lsmash_bs_put_be32( bs, data->media_rate ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_edts( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_edts_t *edts = trak->edts; + if( !edts ) + return 0; + isom_bs_put_box_common( bs, edts ); + if( lsmash_bs_write_data( bs ) ) + return -1; + return isom_write_elst( bs, trak ); +} + +static int isom_write_tref( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_tref_t *tref = trak->tref; + if( !tref ) + return 0; + isom_bs_put_box_common( bs, tref ); + if( tref->ref_list ) + for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next ) + { + isom_tref_type_t *ref = (isom_tref_type_t *)entry->data; + if( !ref ) + return -1; + isom_bs_put_box_common( bs, ref ); + for( uint32_t i = 0; i < ref->ref_count; i++ ) + lsmash_bs_put_be32( bs, ref->track_ID[i] ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mdhd( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_mdhd_t *mdhd = trak->mdia->mdhd; + if( !mdhd ) + return -1; + isom_bs_put_box_common( bs, mdhd ); + if( mdhd->version ) + { + lsmash_bs_put_be64( bs, mdhd->creation_time ); + lsmash_bs_put_be64( bs, mdhd->modification_time ); + lsmash_bs_put_be32( bs, mdhd->timescale ); + lsmash_bs_put_be64( bs, mdhd->duration ); + } + else + { + lsmash_bs_put_be32( bs, (uint32_t)mdhd->creation_time ); + lsmash_bs_put_be32( bs, (uint32_t)mdhd->modification_time ); + lsmash_bs_put_be32( bs, mdhd->timescale ); + lsmash_bs_put_be32( bs, (uint32_t)mdhd->duration ); + } + lsmash_bs_put_be16( bs, mdhd->language ); + lsmash_bs_put_be16( bs, mdhd->quality ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_hdlr( lsmash_bs_t *bs, isom_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 int isom_write_pasp( lsmash_bs_t *bs, isom_pasp_t *pasp ) +{ + if( !pasp ) + return 0; + isom_bs_put_box_common( bs, pasp ); + lsmash_bs_put_be32( bs, pasp->hSpacing ); + lsmash_bs_put_be32( bs, pasp->vSpacing ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_clap( lsmash_bs_t *bs, isom_clap_t *clap ) +{ + if( !clap ) + return 0; + isom_bs_put_box_common( bs, clap ); + lsmash_bs_put_be32( bs, clap->cleanApertureWidthN ); + lsmash_bs_put_be32( bs, clap->cleanApertureWidthD ); + lsmash_bs_put_be32( bs, clap->cleanApertureHeightN ); + lsmash_bs_put_be32( bs, clap->cleanApertureHeightD ); + lsmash_bs_put_be32( bs, clap->horizOffN ); + lsmash_bs_put_be32( bs, clap->horizOffD ); + lsmash_bs_put_be32( bs, clap->vertOffN ); + lsmash_bs_put_be32( bs, clap->vertOffD ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_colr( lsmash_bs_t *bs, isom_colr_t *colr ) +{ + if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF ) + return 0; + isom_bs_put_box_common( bs, colr ); + lsmash_bs_put_be32( bs, colr->color_parameter_type ); + lsmash_bs_put_be16( bs, colr->primaries_index ); + lsmash_bs_put_be16( bs, colr->transfer_function_index ); + lsmash_bs_put_be16( bs, colr->matrix_index ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stsl( lsmash_bs_t *bs, isom_stsl_t *stsl ) +{ + if( !stsl ) + return 0; + isom_bs_put_box_common( bs, stsl ); + lsmash_bs_put_byte( bs, stsl->constraint_flag ); + lsmash_bs_put_byte( bs, stsl->scale_method ); + lsmash_bs_put_be16( bs, stsl->display_center_x ); + lsmash_bs_put_be16( bs, stsl->display_center_y ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_esds( lsmash_bs_t *bs, isom_esds_t *esds ) +{ + if( !esds ) + return 0; + isom_bs_put_box_common( bs, esds ); + return mp4sys_write_ES_Descriptor( bs, esds->ES ); +} + +static int isom_put_ps_entries( lsmash_bs_t *bs, lsmash_entry_list_t *list ) +{ + for( lsmash_entry_t *entry = list->head; entry; entry = entry->next ) + { + isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be16( bs, data->parameterSetLength ); + lsmash_bs_put_bytes( bs, data->parameterSetNALUnit, data->parameterSetLength ); + } + return 0; +} + +static int isom_write_avcC( lsmash_bs_t *bs, isom_avcC_t *avcC ) +{ + if( !avcC ) + return 0; + if( !avcC->sequenceParameterSets || !avcC->pictureParameterSets ) + return -1; + isom_bs_put_box_common( bs, avcC ); + lsmash_bs_put_byte( bs, avcC->configurationVersion ); + lsmash_bs_put_byte( bs, avcC->AVCProfileIndication ); + lsmash_bs_put_byte( bs, avcC->profile_compatibility ); + lsmash_bs_put_byte( bs, avcC->AVCLevelIndication ); + lsmash_bs_put_byte( bs, avcC->lengthSizeMinusOne | 0xfc ); /* upper 6-bits are reserved as 111111b */ + lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSets | 0xe0 ); /* upper 3-bits are reserved as 111b */ + if( isom_put_ps_entries( bs, avcC->sequenceParameterSets ) ) + return -1; + lsmash_bs_put_byte( bs, avcC->numOfPictureParameterSets ); + if( isom_put_ps_entries( bs, avcC->pictureParameterSets ) ) + return -1; + if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) ) + { + lsmash_bs_put_byte( bs, avcC->chroma_format | 0xfc ); /* upper 6-bits are reserved as 111111b */ + lsmash_bs_put_byte( bs, avcC->bit_depth_luma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */ + lsmash_bs_put_byte( bs, avcC->bit_depth_chroma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */ + lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSetExt ); + if( isom_put_ps_entries( bs, avcC->sequenceParameterSetExt ) ) + return -1; + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_btrt( lsmash_bs_t *bs, isom_btrt_t *btrt ) +{ + if( !btrt ) + return 0; + isom_bs_put_box_common( bs, btrt ); + lsmash_bs_put_be32( bs, btrt->bufferSizeDB ); + lsmash_bs_put_be32( bs, btrt->maxBitrate ); + lsmash_bs_put_be32( bs, btrt->avgBitrate ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_visual_extensions( lsmash_bs_t *bs, isom_visual_entry_t *visual ) +{ + if( !visual ) + return 0; + if( isom_write_clap( bs, visual->clap ) + || isom_write_pasp( bs, visual->pasp ) + || isom_write_colr( bs, visual->colr ) + || isom_write_stsl( bs, visual->stsl ) + || isom_write_esds( bs, visual->esds ) + || isom_write_avcC( bs, visual->avcC ) + || isom_write_btrt( bs, visual->btrt ) ) + return -1; + return 0; +} + +static int isom_write_frma( lsmash_bs_t *bs, isom_frma_t *frma ) +{ + if( !frma ) + return -1; + isom_bs_put_box_common( bs, frma ); + lsmash_bs_put_be32( bs, frma->data_format ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_enda( lsmash_bs_t *bs, isom_enda_t *enda ) +{ + if( !enda ) + return 0; + isom_bs_put_box_common( bs, enda ); + lsmash_bs_put_be16( bs, enda->littleEndian ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mp4a( lsmash_bs_t *bs, isom_mp4a_t *mp4a ) +{ + if( !mp4a ) + return 0; + isom_bs_put_box_common( bs, mp4a ); + lsmash_bs_put_be32( bs, mp4a->unknown ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_terminator( lsmash_bs_t *bs, isom_terminator_t *terminator ) +{ + if( !terminator ) + return -1; + isom_bs_put_box_common( bs, terminator ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_wave( lsmash_bs_t *bs, isom_wave_t *wave ) +{ + if( !wave ) + return 0; + isom_bs_put_box_common( bs, wave ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_frma( bs, wave->frma ) + || isom_write_enda( bs, wave->enda ) + || isom_write_mp4a( bs, wave->mp4a ) ) + return -1; + lsmash_bs_put_bytes( bs, wave->exdata, wave->exdata_length ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_esds( bs, wave->esds ) ) + return -1; + return isom_write_terminator( bs, wave->terminator ); +} + +static int isom_write_chan( lsmash_bs_t *bs, isom_chan_t *chan ) +{ + if( !chan ) + return 0; + isom_bs_put_box_common( bs, chan ); + lsmash_bs_put_be32( bs, chan->channelLayoutTag ); + lsmash_bs_put_be32( bs, chan->channelBitmap ); + lsmash_bs_put_be32( bs, chan->numberChannelDescriptions ); + if( chan->channelDescriptions ) + for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ ) + { + isom_channel_description_t *channelDescriptions = (isom_channel_description_t *)(&chan->channelDescriptions[i]); + if( !channelDescriptions ) + return -1; + lsmash_bs_put_be32( bs, channelDescriptions->channelLabel ); + lsmash_bs_put_be32( bs, channelDescriptions->channelFlags ); + lsmash_bs_put_be32( bs, channelDescriptions->coordinates[0] ); + lsmash_bs_put_be32( bs, channelDescriptions->coordinates[1] ); + lsmash_bs_put_be32( bs, channelDescriptions->coordinates[2] ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_audio_extensions( lsmash_bs_t *bs, isom_audio_entry_t *audio ) +{ + if( !audio ) + return 0; + if( isom_write_esds( bs, audio->esds ) + || isom_write_wave( bs, audio->wave ) + || isom_write_chan( bs, audio->chan ) ) + return -1; + return 0; +} + +static int isom_write_visual_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_visual_entry_t *data = (isom_visual_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + lsmash_bs_put_be16( bs, data->version ); + lsmash_bs_put_be16( bs, data->revision_level ); + lsmash_bs_put_be32( bs, data->vendor ); + lsmash_bs_put_be32( bs, data->temporalQuality ); + lsmash_bs_put_be32( bs, data->spatialQuality ); + lsmash_bs_put_be16( bs, data->width ); + lsmash_bs_put_be16( bs, data->height ); + lsmash_bs_put_be32( bs, data->horizresolution ); + lsmash_bs_put_be32( bs, data->vertresolution ); + lsmash_bs_put_be32( bs, data->dataSize ); + lsmash_bs_put_be16( bs, data->frame_count ); + lsmash_bs_put_bytes( bs, data->compressorname, 32 ); + lsmash_bs_put_be16( bs, data->depth ); + lsmash_bs_put_be16( bs, data->color_table_ID ); + if( lsmash_bs_write_data( bs ) ) + return -1; + return isom_write_visual_extensions( bs, data ); +} + +static int isom_write_audio_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_audio_entry_t *data = (isom_audio_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + lsmash_bs_put_be16( bs, data->version ); + lsmash_bs_put_be16( bs, data->revision_level ); + lsmash_bs_put_be32( bs, data->vendor ); + lsmash_bs_put_be16( bs, data->channelcount ); + lsmash_bs_put_be16( bs, data->samplesize ); + lsmash_bs_put_be16( bs, data->compression_ID ); + lsmash_bs_put_be16( bs, data->packet_size ); + lsmash_bs_put_be32( bs, data->samplerate ); + if( data->version == 1 ) + { + lsmash_bs_put_be32( bs, data->samplesPerPacket ); + lsmash_bs_put_be32( bs, data->bytesPerPacket ); + lsmash_bs_put_be32( bs, data->bytesPerFrame ); + lsmash_bs_put_be32( bs, data->bytesPerSample ); + } + else if( data->version == 2 ) + { + lsmash_bs_put_be32( bs, data->sizeOfStructOnly ); + lsmash_bs_put_be64( bs, data->audioSampleRate ); + lsmash_bs_put_be32( bs, data->numAudioChannels ); + lsmash_bs_put_be32( bs, data->always7F000000 ); + lsmash_bs_put_be32( bs, data->constBitsPerChannel ); + lsmash_bs_put_be32( bs, data->formatSpecificFlags ); + lsmash_bs_put_be32( bs, data->constBytesPerAudioPacket ); + lsmash_bs_put_be32( bs, data->constLPCMFramesPerAudioPacket ); + } + lsmash_bs_put_bytes( bs, data->exdata, data->exdata_length ); + if( lsmash_bs_write_data( bs ) ) + return -1; + return isom_write_audio_extensions( bs, data ); +} + +#if 0 +static int isom_write_hint_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_hint_entry_t *data = (isom_hint_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + if( data->data && data->data_length ) + lsmash_bs_put_bytes( bs, data->data, data->data_length ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_metadata_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_metadata_entry_t *data = (isom_metadata_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + return lsmash_bs_write_data( bs ); +} +#endif + +static int isom_write_text_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_text_entry_t *data = (isom_text_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + lsmash_bs_put_be32( bs, data->displayFlags ); + lsmash_bs_put_be32( bs, data->textJustification ); + for( uint32_t i = 0; i < 3; i++ ) + lsmash_bs_put_be16( bs, data->bgColor[i] ); + lsmash_bs_put_be16( bs, data->top ); + lsmash_bs_put_be16( bs, data->left ); + lsmash_bs_put_be16( bs, data->bottom ); + lsmash_bs_put_be16( bs, data->right ); + lsmash_bs_put_be32( bs, data->scrpStartChar ); + lsmash_bs_put_be16( bs, data->scrpHeight ); + lsmash_bs_put_be16( bs, data->scrpAscent ); + lsmash_bs_put_be16( bs, data->scrpFont ); + lsmash_bs_put_be16( bs, data->scrpFace ); + lsmash_bs_put_be16( bs, data->scrpSize ); + for( uint32_t i = 0; i < 3; i++ ) + lsmash_bs_put_be16( bs, data->scrpColor[i] ); + lsmash_bs_put_byte( bs, data->font_name_length ); + if( data->font_name && data->font_name_length ) + lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length ); + return lsmash_bs_write_data( bs ); +} + +static int isom_put_ftab( lsmash_bs_t *bs, isom_ftab_t *ftab ) +{ + if( !ftab || !ftab->list ) + return -1; + isom_bs_put_box_common( bs, ftab ); + lsmash_bs_put_be16( bs, ftab->list->entry_count ); + for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next ) + { + isom_font_record_t *data = (isom_font_record_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be16( bs, data->font_ID ); + lsmash_bs_put_byte( bs, data->font_name_length ); + if( data->font_name && data->font_name_length ) + lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length ); + } + return 0; +} + +static int isom_write_tx3g_entry( lsmash_bs_t *bs, lsmash_entry_t *entry ) +{ + isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)entry->data; + if( !data ) + return -1; + isom_bs_put_box_common( bs, data ); + lsmash_bs_put_bytes( bs, data->reserved, 6 ); + lsmash_bs_put_be16( bs, data->data_reference_index ); + lsmash_bs_put_be32( bs, data->displayFlags ); + lsmash_bs_put_byte( bs, data->horizontal_justification ); + lsmash_bs_put_byte( bs, data->vertical_justification ); + for( uint32_t i = 0; i < 4; i++ ) + lsmash_bs_put_byte( bs, data->background_color_rgba[i] ); + lsmash_bs_put_be16( bs, data->top ); + lsmash_bs_put_be16( bs, data->left ); + lsmash_bs_put_be16( bs, data->bottom ); + lsmash_bs_put_be16( bs, data->right ); + lsmash_bs_put_be16( bs, data->startChar ); + lsmash_bs_put_be16( bs, data->endChar ); + lsmash_bs_put_be16( bs, data->font_ID ); + lsmash_bs_put_byte( bs, data->face_style_flags ); + lsmash_bs_put_byte( bs, data->font_size ); + for( uint32_t i = 0; i < 4; i++ ) + lsmash_bs_put_byte( bs, data->text_color_rgba[i] ); + isom_put_ftab( bs, data->ftab ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stsd( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd; + if( !stsd || !stsd->list || !stsd->list->head ) + return -1; + isom_bs_put_box_common( bs, stsd ); + lsmash_bs_put_be32( bs, stsd->list->entry_count ); + int ret = -1; + for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next ) + { + isom_sample_entry_t *sample = (isom_sample_entry_t *)entry->data; + if( !sample ) + return -1; + switch( sample->type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : +#ifdef LSMASH_DEMUXER_ENABLED + case ISOM_CODEC_TYPE_MP4V_VIDEO : +#endif +#if 0 + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + case ISOM_CODEC_TYPE_SVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : +#endif + ret = isom_write_visual_entry( bs, entry ); + break; + case ISOM_CODEC_TYPE_MP4A_AUDIO : + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + case ISOM_CODEC_TYPE_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 : +#ifdef LSMASH_DEMUXER_ENABLED + case ISOM_CODEC_TYPE_EC_3_AUDIO : +#endif +#if 0 + case ISOM_CODEC_TYPE_DRA1_AUDIO : + case ISOM_CODEC_TYPE_DTSC_AUDIO : + case ISOM_CODEC_TYPE_DTSH_AUDIO : + case ISOM_CODEC_TYPE_DTSL_AUDIO : + case ISOM_CODEC_TYPE_ENCA_AUDIO : + case ISOM_CODEC_TYPE_G719_AUDIO : + case ISOM_CODEC_TYPE_G726_AUDIO : + case ISOM_CODEC_TYPE_M4AE_AUDIO : + case ISOM_CODEC_TYPE_MLPA_AUDIO : + case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAWP_AUDIO : + case ISOM_CODEC_TYPE_SEVC_AUDIO : + case ISOM_CODEC_TYPE_SQCP_AUDIO : + case ISOM_CODEC_TYPE_SSMV_AUDIO : + case ISOM_CODEC_TYPE_TWOS_AUDIO : +#endif + ret = isom_write_audio_entry( bs, entry ); + break; +#if 0 + case ISOM_CODEC_TYPE_FDP_HINT : + case ISOM_CODEC_TYPE_M2TS_HINT : + case ISOM_CODEC_TYPE_PM2T_HINT : + case ISOM_CODEC_TYPE_PRTP_HINT : + case ISOM_CODEC_TYPE_RM2T_HINT : + case ISOM_CODEC_TYPE_RRTP_HINT : + case ISOM_CODEC_TYPE_RSRP_HINT : + case ISOM_CODEC_TYPE_RTP_HINT : + case ISOM_CODEC_TYPE_SM2T_HINT : + case ISOM_CODEC_TYPE_SRTP_HINT : + ret = isom_write_hint_entry( bs, entry ); + break; + case ISOM_CODEC_TYPE_IXSE_META : + case ISOM_CODEC_TYPE_METT_META : + case ISOM_CODEC_TYPE_METX_META : + case ISOM_CODEC_TYPE_MLIX_META : + case ISOM_CODEC_TYPE_OKSD_META : + case ISOM_CODEC_TYPE_SVCM_META : + case ISOM_CODEC_TYPE_TEXT_META : + case ISOM_CODEC_TYPE_URIM_META : + case ISOM_CODEC_TYPE_XML_META : + ret = isom_write_metadata_entry( bs, entry ); + break; +#endif + case ISOM_CODEC_TYPE_TX3G_TEXT : + ret = isom_write_tx3g_entry( bs, entry ); + break; + case QT_CODEC_TYPE_TEXT_TEXT : + ret = isom_write_text_entry( bs, entry ); + break; + default : + break; + } + if( ret ) + break; + } + return ret; +} + +static int isom_write_stts( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stts_t *stts = trak->mdia->minf->stbl->stts; + if( !stts || !stts->list ) + return -1; + isom_bs_put_box_common( bs, stts ); + lsmash_bs_put_be32( bs, stts->list->entry_count ); + for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next ) + { + isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->sample_delta ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_ctts( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_ctts_t *ctts = trak->mdia->minf->stbl->ctts; + if( !ctts ) + return 0; + if( !ctts->list ) + return -1; + isom_bs_put_box_common( bs, ctts ); + lsmash_bs_put_be32( bs, ctts->list->entry_count ); + for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next ) + { + isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->sample_offset ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_cslg( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_cslg_t *cslg = trak->mdia->minf->stbl->cslg; + if( !cslg ) + return 0; + isom_bs_put_box_common( bs, cslg ); + lsmash_bs_put_be32( bs, cslg->compositionToDTSShift ); + lsmash_bs_put_be32( bs, cslg->leastDecodeToDisplayDelta ); + lsmash_bs_put_be32( bs, cslg->greatestDecodeToDisplayDelta ); + lsmash_bs_put_be32( bs, cslg->compositionStartTime ); + lsmash_bs_put_be32( bs, cslg->compositionEndTime ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stsz( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stsz_t *stsz = trak->mdia->minf->stbl->stsz; + if( !stsz ) + return -1; + isom_bs_put_box_common( bs, stsz ); + lsmash_bs_put_be32( bs, stsz->sample_size ); + lsmash_bs_put_be32( bs, stsz->sample_count ); + if( stsz->sample_size == 0 && stsz->list ) + for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->entry_size ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stss( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stss_t *stss = trak->mdia->minf->stbl->stss; + if( !stss ) + return 0; /* If the sync sample box is not present, every sample is a random access point. */ + if( !stss->list ) + return -1; + isom_bs_put_box_common( bs, stss ); + lsmash_bs_put_be32( bs, stss->list->entry_count ); + for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next ) + { + isom_stss_entry_t *data = (isom_stss_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->sample_number ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stps( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stps_t *stps = trak->mdia->minf->stbl->stps; + if( !stps ) + return 0; + if( !stps->list ) + return -1; + isom_bs_put_box_common( bs, stps ); + lsmash_bs_put_be32( bs, stps->list->entry_count ); + for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next ) + { + isom_stps_entry_t *data = (isom_stps_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->sample_number ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_sdtp( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_sdtp_t *sdtp = trak->mdia->minf->stbl->sdtp; + if( !sdtp ) + return 0; + if( !sdtp->list ) + return -1; + isom_bs_put_box_common( bs, sdtp ); + for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next ) + { + isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data; + if( !data ) + return -1; + uint8_t temp = (data->is_leading << 6) + | (data->sample_depends_on << 4) + | (data->sample_is_depended_on << 2) + | data->sample_has_redundancy; + lsmash_bs_put_byte( bs, temp ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stsc( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc; + if( !stsc || !stsc->list ) + return -1; + isom_bs_put_box_common( bs, stsc ); + lsmash_bs_put_be32( bs, stsc->list->entry_count ); + for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next ) + { + isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->first_chunk ); + lsmash_bs_put_be32( bs, data->samples_per_chunk ); + lsmash_bs_put_be32( bs, data->sample_description_index ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_co64( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stco_t *co64 = trak->mdia->minf->stbl->stco; + if( !co64 || !co64->list ) + return -1; + isom_bs_put_box_common( bs, co64 ); + lsmash_bs_put_be32( bs, co64->list->entry_count ); + for( lsmash_entry_t *entry = co64->list->head; entry; entry = entry->next ) + { + isom_co64_entry_t *data = (isom_co64_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be64( bs, data->chunk_offset ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stco( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stco_t *stco = trak->mdia->minf->stbl->stco; + if( !stco || !stco->list ) + return -1; + if( stco->large_presentation ) + return isom_write_co64( bs, trak ); + isom_bs_put_box_common( bs, stco ); + lsmash_bs_put_be32( bs, stco->list->entry_count ); + for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next ) + { + isom_stco_entry_t *data = (isom_stco_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->chunk_offset ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_sgpd( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number ) +{ + isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sgpd_list, grouping_number ); + if( !sgpd || !sgpd->list ) + return -1; + isom_bs_put_box_common( bs, sgpd ); + lsmash_bs_put_be32( bs, sgpd->grouping_type ); + if( sgpd->version == 1 ) + lsmash_bs_put_be32( bs, sgpd->default_length ); + lsmash_bs_put_be32( bs, sgpd->list->entry_count ); + for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next ) + { + if( !entry->data ) + return -1; + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + { + isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data; + uint8_t temp = (rap->num_leading_samples_known << 7) + | rap->num_leading_samples; + lsmash_bs_put_byte( bs, temp ); + break; + } + case ISOM_GROUP_TYPE_ROLL : + lsmash_bs_put_be16( bs, ((isom_roll_entry_t *)entry->data)->roll_distance ); + break; + default : + /* We don't consider other grouping types currently. */ + // if( sgpd->version == 1 && !sgpd->default_length ) + // lsmash_bs_put_be32( bs, ((isom_sgpd_entry_t *)entry->data)->description_length ); + break; + } + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_sbgp( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number ) +{ + isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sbgp_list, grouping_number ); + if( !sbgp || !sbgp->list ) + return -1; + isom_bs_put_box_common( bs, sbgp ); + lsmash_bs_put_be32( bs, sbgp->grouping_type ); + if( sbgp->version == 1 ) + lsmash_bs_put_be32( bs, sbgp->grouping_type_parameter ); + lsmash_bs_put_be32( bs, sbgp->list->entry_count ); + for( lsmash_entry_t *entry = sbgp->list->head; entry; entry = entry->next ) + { + isom_group_assignment_entry_t *data = (isom_group_assignment_entry_t *)entry->data; + if( !data ) + return -1; + lsmash_bs_put_be32( bs, data->sample_count ); + lsmash_bs_put_be32( bs, data->group_description_index ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_stbl( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl ) + return -1; + isom_bs_put_box_common( bs, stbl ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_stsd( bs, trak ) + || isom_write_stts( bs, trak ) + || isom_write_ctts( bs, trak ) + || isom_write_cslg( bs, trak ) + || isom_write_stss( bs, trak ) + || isom_write_stps( bs, trak ) + || isom_write_sdtp( bs, trak ) + || isom_write_stsc( bs, trak ) + || isom_write_stsz( bs, trak ) + || isom_write_stco( bs, trak ) ) + return -1; + if( stbl->sgpd_list ) + for( uint32_t i = 1; i <= stbl->sgpd_list->entry_count; i++ ) + if( isom_write_sgpd( bs, trak, i ) ) + return -1; + if( stbl->sbgp_list ) + for( uint32_t i = 1; i <= stbl->sbgp_list->entry_count; i++ ) + if( isom_write_sbgp( bs, trak, i ) ) + return -1; + return 0; +} + +static int isom_write_minf( lsmash_bs_t *bs, isom_trak_entry_t *trak ) +{ + isom_minf_t *minf = trak->mdia->minf; + if( !minf ) + return -1; + isom_bs_put_box_common( bs, minf ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( (minf->vmhd && isom_write_vmhd( bs, trak )) + || (minf->smhd && isom_write_smhd( bs, trak )) + || (minf->hmhd && isom_write_hmhd( bs, trak )) + || (minf->nmhd && isom_write_nmhd( bs, trak )) + || (minf->gmhd && isom_write_gmhd( bs, trak )) ) + return -1; + if( isom_write_hdlr( bs, 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 void isom_bs_put_sample_flags( lsmash_bs_t *bs, isom_sample_flags_t *flags ) +{ + uint32_t temp = (flags->reserved << 28) + | (flags->is_leading << 26) + | (flags->sample_depends_on << 24) + | (flags->sample_is_depended_on << 22) + | (flags->sample_has_redundancy << 20) + | (flags->sample_padding_value << 17) + | (flags->sample_is_non_sync_sample << 16) + | flags->sample_degradation_priority; + lsmash_bs_put_be32( bs, temp ); +} + +static int isom_write_mehd( lsmash_bs_t *bs, isom_mehd_t *mehd ) +{ + if( !mehd ) + return -1; + isom_bs_put_box_common( bs, mehd ); + if( mehd->version == 1 ) + lsmash_bs_put_be64( bs, mehd->fragment_duration ); + else + lsmash_bs_put_be32( bs, (uint32_t)mehd->fragment_duration ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_trex( lsmash_bs_t *bs, isom_trex_entry_t *trex ) +{ + if( !trex ) + return -1; + isom_bs_put_box_common( bs, trex ); + lsmash_bs_put_be32( bs, trex->track_ID ); + lsmash_bs_put_be32( bs, trex->default_sample_description_index ); + lsmash_bs_put_be32( bs, trex->default_sample_duration ); + lsmash_bs_put_be32( bs, trex->default_sample_size ); + isom_bs_put_sample_flags( bs, &trex->default_sample_flags ); + return lsmash_bs_write_data( bs ); +} + +static int isom_bs_write_movie_extends_placeholder( lsmash_bs_t *bs ) +{ + /* The following will be overwritten by Movie Extends Header Box. + * We use version 1 Movie Extends Header Box since it causes extra 4 bytes region + * we cannot replace with empty Free Space Box as we place version 0 one. */ + lsmash_bs_put_be32( bs, ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8 ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE ); + lsmash_bs_put_be32( bs, 0 ); + lsmash_bs_put_be64( bs, 0 ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mvex( lsmash_bs_t *bs, isom_mvex_t *mvex ) +{ + if( !mvex ) + return 0; + isom_bs_put_box_common( bs, mvex ); + if( lsmash_bs_write_data( bs ) ) + return -1; + /* Movie Extends Header Box is not written immediately. + * It's done after finishing all movie fragments. */ + if( mvex->mehd ) + { + if( isom_write_mehd( bs, mvex->mehd ) ) + return -1; + } + else if( bs->stream != stdout ) + { + /* + [ROOT] + |--[ftyp] + |--[moov] + |--[mvhd] + |--[trak] + * + * + * + |--[mvex] + |--[mehd] <--- mehd->pos == mvex->placeholder_pos + */ + mvex->placeholder_pos = mvex->root->bs->written; + if( isom_bs_write_movie_extends_placeholder( bs ) ) + return -1; + } + if( mvex->trex_list ) + for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next ) + if( isom_write_trex( bs, (isom_trex_entry_t *)entry->data ) ) + return -1; + return 0; +} + +static int isom_write_mfhd( lsmash_bs_t *bs, isom_mfhd_t *mfhd ) +{ + if( !mfhd ) + return -1; + isom_bs_put_box_common( bs, mfhd ); + lsmash_bs_put_be32( bs, mfhd->sequence_number ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_tfhd( lsmash_bs_t *bs, isom_tfhd_t *tfhd ) +{ + if( !tfhd ) + return -1; + isom_bs_put_box_common( bs, tfhd ); + lsmash_bs_put_be32( bs, tfhd->track_ID ); + if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) lsmash_bs_put_be64( bs, tfhd->base_data_offset ); + if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_bs_put_be32( bs, tfhd->sample_description_index ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_duration ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_size ); + if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &tfhd->default_sample_flags ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_trun( lsmash_bs_t *bs, isom_trun_entry_t *trun ) +{ + if( !trun ) + return -1; + isom_bs_put_box_common( bs, trun ); + lsmash_bs_put_be32( bs, trun->sample_count ); + if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, trun->data_offset ); + if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &trun->first_sample_flags ); + if( trun->optional ) + for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next ) + { + isom_trun_optional_row_t *data = (isom_trun_optional_row_t *)entry->data; + if( !data ) + return -1; + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, data->sample_duration ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, data->sample_size ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &data->sample_flags ); + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, data->sample_composition_time_offset ); + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_traf( lsmash_bs_t *bs, isom_traf_entry_t *traf ) +{ + if( !traf ) + return -1; + isom_bs_put_box_common( bs, traf ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_tfhd( bs, traf->tfhd ) ) + return -1; + if( traf->trun_list ) + for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next ) + if( isom_write_trun( bs, (isom_trun_entry_t *)entry->data ) ) + return -1; + return 0; +} + +static int isom_write_moof( lsmash_bs_t *bs, isom_moof_entry_t *moof ) +{ + if( !moof ) + return -1; + isom_bs_put_box_common( bs, moof ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( isom_write_mfhd( bs, moof->mfhd ) ) + return -1; + if( moof->traf_list ) + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + if( isom_write_traf( bs, (isom_traf_entry_t *)entry->data ) ) + return -1; + return 0; +} + +static int isom_write_tfra( lsmash_bs_t *bs, isom_tfra_entry_t *tfra ) +{ + if( !tfra ) + return -1; + isom_bs_put_box_common( bs, tfra ); + uint32_t temp = (tfra->reserved << 6) + | (tfra->length_size_of_traf_num << 4) + | (tfra->length_size_of_trun_num << 2) + | tfra->length_size_of_sample_num; + lsmash_bs_put_be32( bs, tfra->track_ID ); + lsmash_bs_put_be32( bs, temp ); + lsmash_bs_put_be32( bs, tfra->number_of_entry ); + if( tfra->list ) + { + void (*bs_put_funcs[5])( lsmash_bs_t *, uint64_t ) = + { + lsmash_bs_put_byte_from_64, + lsmash_bs_put_be16_from_64, + lsmash_bs_put_be24_from_64, + lsmash_bs_put_be32_from_64, + lsmash_bs_put_be64 + }; + void (*bs_put_time) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ]; + void (*bs_put_moof_offset) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ]; + void (*bs_put_traf_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_traf_num ]; + void (*bs_put_trun_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_trun_num ]; + void (*bs_put_sample_number)( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_sample_num ]; + for( lsmash_entry_t *entry = tfra->list->head; entry; entry = entry->next ) + { + isom_tfra_location_time_entry_t *data = (isom_tfra_location_time_entry_t *)entry->data; + if( !data ) + return -1; + bs_put_time ( bs, data->time ); + bs_put_moof_offset ( bs, data->moof_offset ); + bs_put_traf_number ( bs, data->traf_number ); + bs_put_trun_number ( bs, data->trun_number ); + bs_put_sample_number( bs, data->sample_number ); + } + } + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mfro( lsmash_bs_t *bs, isom_mfro_t *mfro ) +{ + if( !mfro ) + return -1; + isom_bs_put_box_common( bs, mfro ); + lsmash_bs_put_be32( bs, mfro->length ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mfra( lsmash_bs_t *bs, isom_mfra_t *mfra ) +{ + if( !mfra ) + return -1; + isom_bs_put_box_common( bs, mfra ); + if( lsmash_bs_write_data( bs ) ) + return -1; + if( mfra->tfra_list ) + for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next ) + if( isom_write_tfra( bs, (isom_tfra_entry_t *)entry->data ) ) + return -1; + return isom_write_mfro( bs, mfra->mfro ); +} + +static int isom_bs_write_largesize_placeholder( lsmash_bs_t *bs ) +{ + lsmash_bs_put_be32( bs, ISOM_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, uint64_t media_size ) +{ + if( !root || !root->bs || !root->mdat ) + return -1; + isom_mdat_t *mdat = root->mdat; + lsmash_bs_t *bs = root->bs; + if( media_size ) + { + mdat->size = ISOM_DEFAULT_BOX_HEADER_SIZE + media_size; + if( mdat->size > UINT32_MAX ) + mdat->size += 8; /* large_size */ + isom_bs_put_box_common( bs, mdat ); + return 0; + } + mdat->placeholder_pos = lsmash_ftell( bs->stream ); + if( isom_bs_write_largesize_placeholder( bs ) ) + return -1; + mdat->size = ISOM_DEFAULT_BOX_HEADER_SIZE; + isom_bs_put_box_common( bs, mdat ); + return lsmash_bs_write_data( bs ); +} + +static int isom_write_mdat_size( lsmash_root_t *root ) +{ + if( !root || !root->bs || !root->bs->stream ) + return -1; + if( !root->mdat ) + return 0; + isom_mdat_t *mdat = root->mdat; + uint8_t large_flag = mdat->size > UINT32_MAX; + lsmash_bs_t *bs = root->bs; + FILE *stream = bs->stream; + uint64_t current_pos = lsmash_ftell( stream ); + if( large_flag ) + { + lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ); + lsmash_bs_put_be32( bs, 1 ); + lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT ); + lsmash_bs_put_be64( bs, mdat->size + ISOM_DEFAULT_BOX_HEADER_SIZE ); + } + else + { + lsmash_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 ); + } + int ret = lsmash_bs_write_data( bs ); + lsmash_fseek( stream, current_pos, SEEK_SET ); + return ret; +} + +/* We put a placeholder for 64-bit media data if the media_size of the argument is set to 0. + * If a Media Data Box already exists and we don't pick movie fragments structure, + * write the actual size of the current one and start a new one. */ +static int isom_new_mdat( lsmash_root_t *root, uint64_t media_size ) +{ + if( !root ) + return 0; + if( root->mdat ) + { + /* Write the actual size of the current Media Data Box. */ + if( !root->fragment && isom_write_mdat_size( root ) ) + return -1; + } + else + { + isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT ); + root->mdat = mdat; + } + /* Start a new Media Data Box. */ + return isom_write_mdat_header( root, media_size ); +} + +int isom_check_compatibility( lsmash_root_t *root ) +{ + if( !root ) + return -1; + root->qt_compatible = 0; + /* Check brand to decide mandatory boxes. */ + if( !root->ftyp || !root->ftyp->brand_count ) + { + /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */ + if( root->moov && root->moov->iods ) + { + root->mp4_version1 = 1; + root->isom_compatible = 1; + } + else + root->qt_compatible = 1; + return 0; + } + for( uint32_t i = 0; i < root->ftyp->brand_count; i++ ) + { + switch( root->ftyp->compatible_brands[i] ) + { + case ISOM_BRAND_TYPE_QT : + root->qt_compatible = 1; + break; + case ISOM_BRAND_TYPE_MP41 : + root->mp4_version1 = 1; + break; + case ISOM_BRAND_TYPE_MP42 : + root->mp4_version2 = 1; + break; + case ISOM_BRAND_TYPE_ISOM : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 1 ); + break; + case ISOM_BRAND_TYPE_ISO2 : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 2 ); + break; + case ISOM_BRAND_TYPE_ISO3 : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 3 ); + break; + case ISOM_BRAND_TYPE_ISO4 : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 4 ); + break; + case ISOM_BRAND_TYPE_ISO5 : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 5 ); + break; + case ISOM_BRAND_TYPE_ISO6 : + root->max_isom_version = LSMASH_MAX( root->max_isom_version, 6 ); + break; + case ISOM_BRAND_TYPE_M4A : + case ISOM_BRAND_TYPE_M4B : + 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; + default : + break; + } + switch( root->ftyp->compatible_brands[i] ) + { + case ISOM_BRAND_TYPE_AVC1 : + case ISOM_BRAND_TYPE_ISO2 : + case ISOM_BRAND_TYPE_ISO3 : + case ISOM_BRAND_TYPE_ISO4 : + case ISOM_BRAND_TYPE_ISO5 : + case ISOM_BRAND_TYPE_ISO6 : + root->avc_extensions = 1; + break; + default : + break; + } + } + root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_audio || root->max_3gpp_version; + return 0; +} + +static uint32_t isom_get_sample_count( isom_trak_entry_t *trak ) +{ + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsz ) + return 0; + return trak->mdia->minf->stbl->stsz->sample_count; +} + +static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number ) +{ + if( !stts || !stts->list ) + return 0; + uint64_t dts = 0; + uint32_t i = 1; + lsmash_entry_t *entry; + isom_stts_entry_t *data; + for( entry = stts->list->head; entry; entry = entry->next ) + { + data = (isom_stts_entry_t *)entry->data; + if( !data ) + return 0; + if( i + data->sample_count > sample_number ) + break; + dts += (uint64_t)data->sample_delta * data->sample_count; + i += data->sample_count; + } + if( !entry ) + return 0; + dts += (uint64_t)data->sample_delta * (sample_number - i); + return dts; +} + +#if 0 +static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number ) +{ + if( !stts || !stts->list ) + return 0; + if( !ctts ) + return isom_get_dts( stts, sample_number ); + uint32_t i = 1; /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */ + lsmash_entry_t *entry; + isom_ctts_entry_t *data; + if( sample_number == 0 ) + return 0; + for( entry = ctts->list->head; entry; entry = entry->next ) + { + data = (isom_ctts_entry_t *)entry->data; + if( !data ) + return 0; + if( i + data->sample_count > sample_number ) + break; + i += data->sample_count; + } + if( !entry ) + return 0; + return isom_get_dts( stts, sample_number ) + data->sample_offset; +} +#endif + +static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta ) +{ + if( !stbl || !stbl->stts || !stbl->stts->list || !stbl->stts->list->tail || !stbl->stts->list->tail->data ) + return -1; + isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data; + if( sample_delta != last_stts_data->sample_delta ) + { + if( last_stts_data->sample_count > 1 ) + { + last_stts_data->sample_count -= 1; + if( isom_add_stts_entry( stbl, sample_delta ) ) + return -1; + } + else + last_stts_data->sample_delta = sample_delta; + } + return 0; +} + +static int isom_update_mdhd_duration( isom_trak_entry_t *trak, uint32_t last_sample_delta ) +{ + if( !trak || !trak->root || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list ) + return -1; + lsmash_root_t *root = trak->root; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stts_t *stts = stbl->stts; + isom_ctts_t *ctts = stbl->ctts; + isom_cslg_t *cslg = stbl->cslg; + mdhd->duration = 0; + uint32_t sample_count = isom_get_sample_count( trak ); + if( !sample_count ) + { + /* Return error if non-fragmented movie has no samples. */ + if( !root->fragment && !stts->list->entry_count ) + return -1; + return 0; + } + /* Now we have at least 1 sample, so do stts_entry. */ + lsmash_entry_t *last_stts = stts->list->tail; + isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data; + if( sample_count == 1 ) + mdhd->duration = last_stts_data->sample_delta; + /* Now we have at least 2 samples, + * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */ + else if( !ctts ) + { + /* use dts instead of cts */ + mdhd->duration = isom_get_dts( stts, sample_count ); + if( last_sample_delta ) + { + mdhd->duration += last_sample_delta; + if( isom_replace_last_sample_delta( stbl, last_sample_delta ) ) + return -1; + } + else if( last_stts_data->sample_count > 1 ) + mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */ + else + { + /* Remove the last entry. */ + if( lsmash_remove_entry( stts->list, stts->list->entry_count, NULL ) ) + return -1; + /* copy the previous sample_delta. */ + ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count; + mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta; + } + } + else + { + if( !ctts->list || ctts->list->entry_count == 0 ) + return -1; + uint64_t dts = 0; + uint64_t max_cts = 0, max2_cts = 0, min_cts = UINT64_MAX; + uint32_t max_offset = 0, min_offset = UINT32_MAX; + 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( root->fragment ) + /* Overall presentation is extended exceeding this initial movie. + * So, any players shall display the movie exceeding the durations + * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes. + * Samples up to the duration indicated in Movie Extends Header Box shall be displayed. + * In the absence of Movie Extends Header Box, all samples shall be displayed. */ + mdhd->duration += dts + last_sample_delta; + else + { + if( !last_sample_delta ) + { + /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */ + last_sample_delta = max_cts - max2_cts; + } + mdhd->duration = max_cts - min_cts + last_sample_delta; + /* To match dts and media duration, update stts and mdhd relatively. */ + if( mdhd->duration > dts ) + last_sample_delta = mdhd->duration - dts; + else + mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */ + } + if( isom_replace_last_sample_delta( stbl, last_sample_delta ) ) + return -1; + /* Explicit composition information and DTS shifting */ + if( cslg || root->qt_compatible || root->max_isom_version >= 4 ) + { + int64_t composition_end_time = max_cts + (max_cts - max2_cts); + if( !root->fragment + && (min_offset <= INT32_MAX) && (max_offset <= INT32_MAX) + && (min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) ) + { + if( !cslg ) + { + if( isom_add_cslg( trak->mdia->minf->stbl ) ) + return -1; + cslg = stbl->cslg; + } + cslg->compositionToDTSShift = 0; /* We don't consider DTS shifting at present. */ + cslg->leastDecodeToDisplayDelta = min_offset; + cslg->greatestDecodeToDisplayDelta = max_offset; + cslg->compositionStartTime = min_cts; + cslg->compositionEndTime = composition_end_time; + } + else + { + if( cslg ) + free( cslg ); + stbl->cslg = NULL; + } + } + } + if( mdhd->duration > UINT32_MAX ) + mdhd->version = 1; + return 0; +} + +static int isom_update_mvhd_duration( isom_moov_t *moov ) +{ + if( !moov || !moov->mvhd ) + return -1; + isom_mvhd_t *mvhd = moov->mvhd; + mvhd->duration = 0; + for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next ) + { + /* We pick maximum track duration as movie duration. */ + isom_trak_entry_t *data = (isom_trak_entry_t *)entry->data; + if( !data || !data->tkhd ) + return -1; + mvhd->duration = entry != moov->trak_list->head ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) : data->tkhd->duration; + } + if( mvhd->duration > UINT32_MAX ) + mvhd->version = 1; + return 0; +} + +static int isom_update_tkhd_duration( isom_trak_entry_t *trak ) +{ + if( !trak || !trak->tkhd || !trak->root || !trak->root->moov ) + return -1; + lsmash_root_t *root = trak->root; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->duration = 0; + if( root->fragment || !trak->edts || !trak->edts->elst ) + { + /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */ + if( !trak->mdia || !trak->mdia->mdhd || !root->moov->mvhd || !trak->mdia->mdhd->timescale ) + return -1; + if( !trak->mdia->mdhd->duration && isom_update_mdhd_duration( trak, 0 ) ) + return -1; + tkhd->duration = trak->mdia->mdhd->duration * ((double)root->moov->mvhd->timescale / trak->mdia->mdhd->timescale); + } + else + { + /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */ + for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next ) + { + isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data; + if( !data ) + return -1; + tkhd->duration += data->segment_duration; + } + } + if( tkhd->duration > UINT32_MAX ) + tkhd->version = 1; + if( !root->fragment && !tkhd->duration ) + tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff; + return isom_update_mvhd_duration( root->moov ); +} + +int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak ) + return -1; + if( isom_update_mdhd_duration( trak, last_sample_delta ) ) + return -1; + /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */ + return (!root->fragment && trak->edts && trak->edts->elst) + ? isom_update_mvhd_duration( root->moov ) /* Only update movie duration. */ + : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */ +} + +int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility, uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne, + uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list ) + return -1; + isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number ); + if( !data ) + return -1; + isom_avcC_t *avcC = (isom_avcC_t *)data->avcC; + if( !avcC ) + return -1; + avcC->configurationVersion = configurationVersion; + avcC->AVCProfileIndication = AVCProfileIndication; + avcC->profile_compatibility = profile_compatibility; + avcC->AVCLevelIndication = AVCLevelIndication; + avcC->lengthSizeMinusOne = lengthSizeMinusOne; + if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) ) + { + avcC->chroma_format = chroma_format; + avcC->bit_depth_luma_minus8 = bit_depth_luma_minus8; + avcC->bit_depth_chroma_minus8 = bit_depth_chroma_minus8; + } + return 0; +} + +static int isom_update_bitrate_info( isom_mdia_t *mdia ) +{ + if( !mdia || !mdia->mdhd || !mdia->minf || !mdia->minf->stbl + || !mdia->minf->stbl->stsd || !mdia->minf->stbl->stsd->list + || !mdia->minf->stbl->stsz || !mdia->minf->stbl->stts || !mdia->minf->stbl->stts->list ) + return -1; + /* Not supporting multi sample entries yet. */ + isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( mdia->minf->stbl->stsd->list, 1 ); + if( !sample_entry ) + return -1; + struct bitrate_info_t + { + uint32_t bufferSizeDB; + uint32_t maxBitrate; + uint32_t avgBitrate; + } info = { 0, 0, 0 }; + uint32_t i = 0; + uint32_t rate = 0; + uint32_t time_wnd = 0; + uint32_t timescale = mdia->mdhd->timescale; + uint64_t dts = 0; + isom_stsz_t *stsz = mdia->minf->stbl->stsz; + lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL; + lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head; + isom_stts_entry_t *stts_data = NULL; + while( stts_entry ) + { + uint32_t size; + if( stsz->list ) + { + if( !stsz_entry ) + break; + isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data; + if( !stsz_data ) + return -1; + size = stsz_data->entry_size; + stsz_entry = stsz_entry->next; + } + else + size = stsz->sample_size; + if( stts_data ) + dts += stts_data->sample_delta; + stts_data = (isom_stts_entry_t *)stts_entry->data; + if( ++i == stts_data->sample_count ) + { + stts_entry = stts_entry->next; + i = 0; + } + if( info.bufferSizeDB < size ) + info.bufferSizeDB = size; + info.avgBitrate += size; + rate += size; + if( dts > time_wnd + timescale ) + { + if( rate > info.maxBitrate ) + info.maxBitrate = rate; + time_wnd = dts; + rate = 0; + } + } + double duration = (double)mdia->mdhd->duration / timescale; + info.avgBitrate = (uint32_t)(info.avgBitrate / duration); + if( !info.maxBitrate ) + info.maxBitrate = info.avgBitrate; + /* move to bps */ + info.maxBitrate *= 8; + info.avgBitrate *= 8; + /* set bitrate info */ + switch( sample_entry->type ) + { + case ISOM_CODEC_TYPE_AVC1_VIDEO : + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + { + isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry; + if( !stsd_data ) + return -1; + //isom_btrt_t *btrt = (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 ) + { + /* MPEG-4 Audio in QTFF */ + isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry; + if( !stsd_data || !stsd_data->wave || !stsd_data->wave->esds || !stsd_data->wave->esds->ES ) + return -1; + esds = stsd_data->wave->esds; + } + else + { + isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry; + if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES ) + return -1; + esds = stsd_data->esds; + } + /* FIXME: avgBitrate is 0 only if VBR in proper. */ + if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) ) + return -1; + break; + } + case ISOM_CODEC_TYPE_ALAC_AUDIO : + { + isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry; + if( !alac ) + return -1; + if( alac->exdata_length < 36 || !alac->exdata ) + { + isom_wave_t *wave = alac->wave; + if( !wave || wave->exdata_length < 36 || !wave->exdata ) + return -1; + break; /* Apparently, average bitrate field is 0. */ + } + uint8_t *exdata = (uint8_t *)alac->exdata + 28; + exdata[0] = (info.avgBitrate >> 24) & 0xff; + exdata[1] = (info.avgBitrate >> 16) & 0xff; + exdata[2] = (info.avgBitrate >> 8) & 0xff; + exdata[3] = info.avgBitrate & 0xff; + break; + } + default : + break; + } + return 0; +} + +static int isom_check_mandatory_boxes( lsmash_root_t *root ) +{ + if( !root ) + return -1; + if( !root->moov || !root->moov->mvhd ) + return -1; + if( !root->moov->trak_list ) + return -1; + /* A movie requires at least one track. */ + if( !root->moov->trak_list->head ) + return -1; + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak + || !trak->tkhd + || !trak->mdia + || !trak->mdia->mdhd + || !trak->mdia->hdlr + || !trak->mdia->minf + || !trak->mdia->minf->dinf + || !trak->mdia->minf->dinf->dref + || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd + || !trak->mdia->minf->stbl->stsz + || !trak->mdia->minf->stbl->stts + || !trak->mdia->minf->stbl->stsc + || !trak->mdia->minf->stbl->stco ) + return -1; + if( root->qt_compatible && !trak->mdia->minf->hdlr ) + return -1; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stsd->list || !stbl->stsd->list->head ) + return -1; + if( !root->fragment + && (!stbl->stsd->list || !stbl->stsd->list->head + || !stbl->stts->list || !stbl->stts->list->head + || !stbl->stsc->list || !stbl->stsc->list->head + || !stbl->stco->list || !stbl->stco->list->head) ) + return -1; + } + if( !root->fragment ) + return 0; + if( !root->moov->mvex || !root->moov->mvex->trex_list ) + return -1; + for( lsmash_entry_t *entry = root->moov->mvex->trex_list->head; entry; entry = entry->next ) + if( !entry->data ) /* trex */ + return -1; + return 0; +} + +static inline uint64_t isom_get_current_mp4time( void ) +{ + return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET; +} + +static int isom_set_media_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time ) +{ + if( !trak->mdia || !trak->mdia->mdhd ) + return -1; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + if( !mdhd->creation_time ) + mdhd->creation_time = mdhd->modification_time = current_mp4time; + return 0; +} + +static int isom_set_track_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time ) +{ + if( !trak || !trak->tkhd ) + return -1; + isom_tkhd_t *tkhd = trak->tkhd; + if( !tkhd->creation_time ) + tkhd->creation_time = tkhd->modification_time = current_mp4time; + if( isom_set_media_creation_time( trak, current_mp4time ) ) + return -1; + return 0; +} + +static int isom_set_movie_creation_time( lsmash_root_t *root ) +{ + if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list ) + return -1; + uint64_t current_mp4time = isom_get_current_mp4time(); + for( uint32_t i = 1; i <= root->moov->trak_list->entry_count; i++ ) + if( isom_set_track_creation_time( isom_get_trak( root, i ), current_mp4time ) ) + return -1; + isom_mvhd_t *mvhd = root->moov->mvhd; + if( !mvhd->creation_time ) + mvhd->creation_time = mvhd->modification_time = current_mp4time; + return 0; +} + +#define CHECK_LARGESIZE( size ) if( (size) > UINT32_MAX ) (size) += 8 + +static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd ) +{ + if( !mvhd ) + return 0; + mvhd->version = 0; + if( mvhd->creation_time > UINT32_MAX || mvhd->modification_time > UINT32_MAX || mvhd->duration > UINT32_MAX ) + mvhd->version = 1; + mvhd->size = ISOM_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 > INT32_MAX || data->media_time < INT32_MIN ) + 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; + if( tref->ref_list ) + for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next ) + { + isom_tref_type_t *ref = (isom_tref_type_t *)entry->data; + ref->size = ISOM_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_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_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_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_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_visual_entry_size( isom_visual_entry_t *visual ) +{ + if( !visual ) + return 0; + visual->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 78 + + isom_update_clap_size( visual->clap ) + + isom_update_pasp_size( visual->pasp ) + + isom_update_colr_size( visual->colr ) + + isom_update_stsl_size( visual->stsl ) + + isom_update_esds_size( visual->esds ) + + isom_update_avcC_size( visual->avcC ) + + isom_update_btrt_size( visual->btrt ); + CHECK_LARGESIZE( visual->size ); + return visual->size; +} + +#if 0 +static uint64_t isom_update_mp4s_entry_size( isom_mp4s_entry_t *mp4s ) +{ + if( !mp4s || mp4s->type != ISOM_CODEC_TYPE_MP4S_SYSTEM ) + return 0; + mp4s->size = ISOM_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_enda_size( isom_enda_t *enda ) +{ + if( !enda ) + return 0; + enda->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 2; + CHECK_LARGESIZE( enda->size ); + return enda->size; +} + +static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a ) +{ + if( !mp4a ) + return 0; + mp4a->size = ISOM_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_enda_size( wave->enda ) + + isom_update_mp4a_size( wave->mp4a ) + + isom_update_esds_size( wave->esds ) + + isom_update_terminator_size( wave->terminator ) + + (uint64_t)wave->exdata_length; + CHECK_LARGESIZE( wave->size ); + return wave->size; +} + +static uint64_t isom_update_chan_size( isom_chan_t *chan ) +{ + if( !chan ) + return 0; + chan->size = ISOM_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_esds_size( audio->esds ) + + isom_update_wave_size( audio->wave ) + + isom_update_chan_size( audio->chan ) + + (uint64_t)audio->exdata_length; + if( audio->version == 1 ) + audio->size += 16; + else if( audio->version == 2 ) + audio->size += 36; + CHECK_LARGESIZE( audio->size ); + return audio->size; +} + +static uint64_t isom_update_text_entry_size( isom_text_entry_t *text ) +{ + if( !text ) + return 0; + text->size = ISOM_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 : +#ifdef LSMASH_DEMUXER_ENABLED + case ISOM_CODEC_TYPE_MP4V_VIDEO : +#endif +#if 0 + case ISOM_CODEC_TYPE_AVC2_VIDEO : + case ISOM_CODEC_TYPE_AVCP_VIDEO : + case ISOM_CODEC_TYPE_SVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC1_VIDEO : + case ISOM_CODEC_TYPE_MVC2_VIDEO : + case ISOM_CODEC_TYPE_DRAC_VIDEO : + case ISOM_CODEC_TYPE_ENCV_VIDEO : + case ISOM_CODEC_TYPE_MJP2_VIDEO : + case ISOM_CODEC_TYPE_S263_VIDEO : + case ISOM_CODEC_TYPE_VC_1_VIDEO : +#endif + size += isom_update_visual_entry_size( (isom_visual_entry_t *)data ); + break; +#if 0 + case ISOM_CODEC_TYPE_MP4S_SYSTEM : + size += isom_update_mp4s_entry_size( (isom_mp4s_entry_t *)data ); + break; +#endif + case ISOM_CODEC_TYPE_MP4A_AUDIO : + case ISOM_CODEC_TYPE_AC_3_AUDIO : + case ISOM_CODEC_TYPE_ALAC_AUDIO : + case ISOM_CODEC_TYPE_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 : +#ifdef LSMASH_DEMUXER_ENABLED + case ISOM_CODEC_TYPE_EC_3_AUDIO : +#endif +#if 0 + case ISOM_CODEC_TYPE_DRA1_AUDIO : + case ISOM_CODEC_TYPE_DTSC_AUDIO : + case ISOM_CODEC_TYPE_DTSH_AUDIO : + case ISOM_CODEC_TYPE_DTSL_AUDIO : + case ISOM_CODEC_TYPE_ENCA_AUDIO : + case ISOM_CODEC_TYPE_G719_AUDIO : + case ISOM_CODEC_TYPE_G726_AUDIO : + case ISOM_CODEC_TYPE_M4AE_AUDIO : + case ISOM_CODEC_TYPE_MLPA_AUDIO : + case ISOM_CODEC_TYPE_RAW_AUDIO : + case ISOM_CODEC_TYPE_SAWP_AUDIO : + case ISOM_CODEC_TYPE_SEVC_AUDIO : + case ISOM_CODEC_TYPE_SQCP_AUDIO : + case ISOM_CODEC_TYPE_SSMV_AUDIO : + case ISOM_CODEC_TYPE_TWOS_AUDIO : +#endif + size += isom_update_audio_entry_size( (isom_audio_entry_t *)data ); + break; + case ISOM_CODEC_TYPE_TX3G_TEXT : + size += isom_update_tx3g_entry_size( (isom_tx3g_entry_t *)data ); + break; + case QT_CODEC_TYPE_TEXT_TEXT : + size += isom_update_text_entry_size( (isom_text_entry_t *)data ); + break; + default : + break; + } + } + stsd->size = size; + CHECK_LARGESIZE( stsd->size ); + return stsd->size; +} + +static uint64_t isom_update_stts_size( isom_stts_t *stts ) +{ + if( !stts || !stts->list ) + return 0; + stts->size = ISOM_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_entry_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_entry_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_RAP : + size += sgpd->list->entry_count; + break; + case ISOM_GROUP_TYPE_ROLL : + size += (uint64_t)sgpd->list->entry_count * 2; + break; + default : + /* We don't consider other grouping types currently. */ + break; + } + sgpd->size = size; + CHECK_LARGESIZE( sgpd->size ); + return sgpd->size; +} + +static uint64_t isom_update_stbl_size( isom_stbl_t *stbl ) +{ + if( !stbl ) + return 0; + stbl->size = ISOM_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 ); + if( stbl->sgpd_list ) + for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next ) + stbl->size += isom_update_sgpd_size( (isom_sgpd_entry_t *)entry->data ); + if( stbl->sbgp_list ) + for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next ) + stbl->size += isom_update_sbgp_size( (isom_sbgp_entry_t *)entry->data ); + CHECK_LARGESIZE( stbl->size ); + return stbl->size; +} + +static uint64_t isom_update_minf_size( isom_minf_t *minf ) +{ + if( !minf ) + return 0; + minf->size = ISOM_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 uint64_t isom_update_mehd_size( isom_mehd_t *mehd ) +{ + if( !mehd ) + return 0; + if( mehd->fragment_duration > UINT32_MAX ) + mehd->version = 1; + mehd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4 * (1 + (mehd->version == 1)); + CHECK_LARGESIZE( mehd->size ); + return mehd->size; +} + +static uint64_t isom_update_trex_entry_size( isom_trex_entry_t *trex ) +{ + if( !trex ) + return 0; + trex->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 20; + CHECK_LARGESIZE( trex->size ); + return trex->size; +} + +static uint64_t isom_update_mvex_size( isom_mvex_t *mvex ) +{ + if( !mvex ) + return 0; + mvex->size = ISOM_DEFAULT_BOX_HEADER_SIZE; + if( mvex->trex_list ) + for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next ) + { + isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data; + mvex->size += isom_update_trex_entry_size( trex ); + } + if( mvex->root->bs->stream != stdout ) + mvex->size += mvex->mehd ? isom_update_mehd_size( mvex->mehd ) : 20; /* 20 bytes is of placeholder. */ + CHECK_LARGESIZE( mvex->size ); + return mvex->size; +} + +static int isom_update_moov_size( isom_moov_t *moov ) +{ + if( !moov ) + return -1; + moov->size = ISOM_DEFAULT_BOX_HEADER_SIZE + + isom_update_mvhd_size( moov->mvhd ) + + isom_update_iods_size( moov->iods ) + + isom_update_udta_size( moov->udta, NULL ) + + isom_update_mvex_size( moov->mvex ); + if( moov->trak_list ) + for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + moov->size += isom_update_trak_entry_size( trak ); + } + CHECK_LARGESIZE( moov->size ); + return 0; +} + +static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd ) +{ + if( !mfhd ) + return 0; + mfhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4; + CHECK_LARGESIZE( mfhd->size ); + return mfhd->size; +} + +static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd ) +{ + if( !tfhd ) + return 0; + tfhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + + 4 + + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) + + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) + + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) + + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) + + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ); + CHECK_LARGESIZE( tfhd->size ); + return tfhd->size; +} + +static uint64_t isom_update_trun_entry_size( isom_trun_entry_t *trun ) +{ + if( !trun ) + return 0; + trun->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + + 4 + + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) + + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ); + uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) + + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) + + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) + + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ); + trun->size += row_size * trun->sample_count; + CHECK_LARGESIZE( trun->size ); + return trun->size; +} + +static uint64_t isom_update_traf_entry_size( isom_traf_entry_t *traf ) +{ + if( !traf ) + return 0; + traf->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_tfhd_size( traf->tfhd ); + if( traf->trun_list ) + for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next ) + { + isom_trun_entry_t *trun = (isom_trun_entry_t *)entry->data; + traf->size += isom_update_trun_entry_size( trun ); + } + CHECK_LARGESIZE( traf->size ); + return traf->size; +} + +static int isom_update_moof_entry_size( isom_moof_entry_t *moof ) +{ + if( !moof ) + return -1; + moof->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_mfhd_size( moof->mfhd ); + if( moof->traf_list ) + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + { + isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data; + moof->size += isom_update_traf_entry_size( traf ); + } + CHECK_LARGESIZE( moof->size ); + return 0; +} + +static uint64_t isom_update_tfra_entry_size( isom_tfra_entry_t *tfra ) +{ + if( !tfra ) + return 0; + tfra->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 12; + uint32_t entry_size = 8 * (1 + (tfra->version == 1)) + + tfra->length_size_of_traf_num + 1 + + tfra->length_size_of_trun_num + 1 + + tfra->length_size_of_sample_num + 1; + tfra->size += entry_size * tfra->number_of_entry; + CHECK_LARGESIZE( tfra->size ); + return tfra->size; +} + +static uint64_t isom_update_mfro_size( isom_mfro_t *mfro ) +{ + if( !mfro ) + return 0; + mfro->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4; + CHECK_LARGESIZE( mfro->size ); + return mfro->size; +} + +static int isom_update_mfra_size( isom_mfra_t *mfra ) +{ + if( !mfra ) + return -1; + mfra->size = ISOM_DEFAULT_BOX_HEADER_SIZE; + if( mfra->tfra_list ) + for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next ) + { + isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data; + mfra->size += isom_update_tfra_entry_size( tfra ); + } + CHECK_LARGESIZE( mfra->size ); + if( mfra->mfro ) + { + mfra->size += isom_update_mfro_size( mfra->mfro ); + mfra->mfro->length = mfra->size; + } + return 0; +} + +/******************************* + public interfaces +*******************************/ + +/*---- track manipulators ----*/ + +void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID ) +{ + if( !root || !root->moov || !root->moov->trak_list ) + return; + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak || !trak->tkhd ) + return; + if( trak->tkhd->track_ID == track_ID ) + { + lsmash_entry_t *next = entry->next; + lsmash_entry_t *prev = entry->prev; + isom_remove_trak( trak ); + free( entry ); + entry = next; + if( entry ) + { + if( prev ) + prev->next = entry; + entry->prev = prev; + } + return; + } + } +} + +uint32_t lsmash_create_track( lsmash_root_t *root, 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 ) ) + return 0; + if( root->qt_compatible && isom_add_hdlr( NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL ) ) + return 0; + switch( media_type ) + { + case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK : + if( isom_add_vmhd( trak->mdia->minf ) ) + return 0; + break; + case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK : + if( isom_add_smhd( trak->mdia->minf ) ) + return 0; + break; + case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK : + if( isom_add_hmhd( trak->mdia->minf ) ) + return 0; + break; + case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK : + if( root->qt_compatible || root->itunes_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; +} + +uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number ) +{ + if( !root || !root->moov ) + return 0; + isom_trak_entry_t *trak = (isom_trak_entry_t *)lsmash_get_entry_data( root->moov->trak_list, track_number ); + if( !trak || !trak->tkhd ) + return 0; + return trak->tkhd->track_ID; +} + +void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param ) +{ + memset( param, 0, sizeof(lsmash_track_parameters_t) ); + param->audio_volume = 0x0100; + param->matrix[0] = 0x00010000; + param->matrix[4] = 0x00010000; + param->matrix[8] = 0x40000000; +} + +int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->hdlr || !root->moov->mvhd ) + return -1; + /* Prepare Track Aperture Modes if required. */ + if( root->qt_compatible && param->aperture_modes ) + { + if( !trak->tapt && isom_add_tapt( trak ) ) + return -1; + isom_tapt_t *tapt = trak->tapt; + if( (!tapt->clef && isom_add_clef( tapt )) + || (!tapt->prof && isom_add_prof( tapt )) + || (!tapt->enof && isom_add_enof( tapt )) ) + return -1; + } + else + isom_remove_tapt( trak->tapt ); + /* Set up Track Header. */ + uint32_t media_type = trak->mdia->hdlr->componentSubtype; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->flags = param->mode; + tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID; + tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration; + tkhd->alternate_group = root->qt_compatible || root->itunes_audio || root->max_3gpp_version >= 4 ? param->alternate_group : 0; + if( root->qt_compatible || root->itunes_audio ) + { + tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0; + tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0; + } + else + { + tkhd->layer = 0; + tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0; + } + for( int i = 0; i < 9; i++ ) + tkhd->matrix[i] = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->matrix[i] : 0; + tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0; + tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0; + /* Update next_track_ID if needed. */ + if( root->moov->mvhd->next_track_ID <= tkhd->track_ID ) + root->moov->mvhd->next_track_ID = tkhd->track_ID + 1; + return 0; +} + +int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak ) + return -1; + isom_tkhd_t *tkhd = trak->tkhd; + param->mode = tkhd->flags; + param->track_ID = tkhd->track_ID; + param->duration = tkhd->duration; + param->video_layer = tkhd->layer; + param->alternate_group = tkhd->alternate_group; + param->audio_volume = tkhd->volume; + for( int i = 0; i < 9; i++ ) + param->matrix[i] = tkhd->matrix[i]; + param->display_width = tkhd->width; + param->display_height = tkhd->height; + param->aperture_modes = !!trak->tapt; + return 0; +} + +static int isom_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->hdlr ) + return -1; + isom_hdlr_t *hdlr = trak->mdia->hdlr; + uint8_t *name = NULL; + uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible; + if( root->qt_compatible ) + name_length = LSMASH_MIN( name_length, 255 ); + if( name_length > hdlr->componentName_length && hdlr->componentName ) + name = realloc( hdlr->componentName, name_length ); + else if( !hdlr->componentName ) + name = malloc( name_length ); + else + name = hdlr->componentName; + if( !name ) + return -1; + if( root->qt_compatible ) + name[0] = name_length & 0xff; + memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) ); + if( root->isom_compatible ) + name[name_length - 1] = 0; + hdlr->componentName = name; + hdlr->componentName_length = name_length; + return 0; +} + +static int isom_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->hdlr ) + return -1; + isom_hdlr_t *hdlr = trak->mdia->minf->hdlr; + uint8_t *name = NULL; + uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible; + if( root->qt_compatible ) + name_length = LSMASH_MIN( name_length, 255 ); + if( name_length > hdlr->componentName_length && hdlr->componentName ) + name = realloc( hdlr->componentName, name_length ); + else if( !hdlr->componentName ) + name = malloc( name_length ); + else + name = hdlr->componentName; + if( !name ) + return -1; + if( root->qt_compatible ) + name[0] = name_length & 0xff; + memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) ); + if( root->isom_compatible ) + name[name_length - 1] = 0; + hdlr->componentName = name; + hdlr->componentName_length = name_length; + return 0; +} + +uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd ) + return 0; + return trak->mdia->mdhd->timescale; +} + +uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd ) + return 0; + return trak->mdia->mdhd->duration; +} + +uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return 0; + return trak->tkhd->duration; +} + +uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list + || !trak->mdia->minf->stbl->stts->list->head || !trak->mdia->minf->stbl->stts->list->head->data ) + return 0; + return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->head->data)->sample_delta; +} + +uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->ctts || !trak->mdia->minf->stbl->ctts->list + || !trak->mdia->minf->stbl->ctts->list->head || !trak->mdia->minf->stbl->ctts->list->head->data ) + return 0; + return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset; +} + +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 int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language ) +{ + if( !MAC_language ) + return -1; + int i = 0; + for( ; isom_languages[i].iso_name; i++ ) + if( ISO_language == isom_languages[i].iso_name ) + break; + if( !isom_languages[i].iso_name ) + return -1; + *MAC_language = isom_languages[i].mac_value; + return 0; +} + +static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language ) +{ + if( !ISO_language ) + return -1; + int i = 0; + for( ; isom_languages[i].iso_name; i++ ) + if( MAC_language == isom_languages[i].mac_value ) + break; + *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED; + return 0; +} + +static int isom_set_media_language( lsmash_root_t *root, uint32_t track_ID, 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; +} + +static int isom_create_grouping( isom_trak_entry_t *trak, lsmash_grouping_type_code grouping_type ) +{ + lsmash_root_t *root = trak->root; + switch( grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + assert( root->max_isom_version >= 6 ); + break; + case ISOM_GROUP_TYPE_ROLL : + assert( root->avc_extensions ); + break; + default : + assert( 0 ); + break; + } + if( !isom_add_sgpd( trak->mdia->minf->stbl, grouping_type ) + || !isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) ) + return -1; + return 0; +} + +void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param ) +{ + memset( param, 0, sizeof(lsmash_media_parameters_t) ); + param->timescale = 1; +} + +int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + trak->mdia->mdhd->timescale = param->timescale; + if( isom_set_media_language( root, track_ID, param->ISO_language, param->MAC_language ) ) + return -1; + if( param->media_handler_name + && isom_set_media_handler_name( root, track_ID, param->media_handler_name ) ) + return -1; + if( root->qt_compatible && param->data_handler_name + && isom_set_data_handler_name( root, track_ID, param->data_handler_name ) ) + return -1; + if( root->avc_extensions && param->roll_grouping + && isom_create_grouping( trak, ISOM_GROUP_TYPE_ROLL ) ) + return -1; + if( (root->max_isom_version >= 6) && param->rap_grouping + && isom_create_grouping( trak, ISOM_GROUP_TYPE_RAP ) ) + return -1; + return 0; +} + +int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr + || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_sbgp_entry_t *sbgp; + isom_sgpd_entry_t *sgpd; + param->timescale = mdhd->timescale; + param->handler_type = trak->mdia->hdlr->componentSubtype; + param->duration = mdhd->duration; + /* Whether sample grouping present. */ + sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); + sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL ); + param->roll_grouping = sbgp && sgpd; + sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP ); + sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); + param->rap_grouping = sbgp && sgpd; + /* Get media language. */ + if( mdhd->language >= 0x800 ) + { + param->MAC_language = 0; + param->ISO_language = isom_unpack_iso_language( mdhd->language ); + memcpy( param->language_shadow, param->ISO_language, sizeof(param->language_shadow) ); + param->ISO_language = param->language_shadow; + } + else + { + param->MAC_language = mdhd->language; + param->ISO_language = NULL; + memset( param->language_shadow, 0, sizeof(param->language_shadow) ); + } + /* Get handler name(s). */ + isom_hdlr_t *hdlr = trak->mdia->hdlr; + int length = LSMASH_MIN( 255, hdlr->componentName_length ); + if( length ) + { + memcpy( param->media_handler_name_shadow, hdlr->componentName + root->qt_compatible, length ); + param->media_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0'; + param->media_handler_name = param->media_handler_name_shadow; + } + else + { + param->media_handler_name = NULL; + memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) ); + } + if( trak->mdia->minf->hdlr ) + { + hdlr = trak->mdia->minf->hdlr; + length = LSMASH_MIN( 255, hdlr->componentName_length ); + if( length ) + { + memcpy( param->data_handler_name_shadow, hdlr->componentName + root->qt_compatible, length ); + param->data_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0'; + param->data_handler_name = param->data_handler_name_shadow; + } + else + { + param->data_handler_name = NULL; + memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) ); + } + } + else + { + param->data_handler_name = NULL; + memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) ); + } + return 0; +} + +/*---- movie manipulators ----*/ + +lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode_code mode ) +{ + char open_mode[4] = { 0 }; + if( mode & LSMASH_FILE_MODE_WRITE ) + memcpy( open_mode, "w+b", 4 ); +#ifdef LSMASH_DEMUXER_ENABLED + else if( mode & LSMASH_FILE_MODE_READ ) + memcpy( open_mode, "rb", 3 ); +#endif + if( !open_mode[0] ) + return NULL; + lsmash_root_t *root = malloc( sizeof(lsmash_root_t) ); + if( !root ) + return NULL; + memset( root, 0, sizeof(lsmash_root_t) ); + root->root = root; + root->bs = malloc( sizeof(lsmash_bs_t) ); + if( !root->bs ) + goto fail; + memset( root->bs, 0, sizeof(lsmash_bs_t) ); + if( !strcmp( filename, "-" ) ) + { + if( mode & LSMASH_FILE_MODE_READ ) + root->bs->stream = stdin; + else if( (mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_FRAGMENTED) ) + root->bs->stream = stdout; + } + else + root->bs->stream = fopen( filename, open_mode ); + if( !root->bs->stream ) + goto fail; + root->flags = mode; + if( mode & LSMASH_FILE_MODE_WRITE ) + { + if( isom_add_moov( root ) || isom_add_mvhd( root->moov ) ) + goto fail; + root->qt_compatible = 1; /* QTFF is default file format. */ + } +#ifdef LSMASH_DEMUXER_ENABLED + if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) ) + { + if( isom_read_root( root ) ) + goto fail; + root->max_read_size = 4 * 1024 * 1024; + } +#endif + if( mode & LSMASH_FILE_MODE_FRAGMENTED ) + { + root->fragment = malloc( sizeof(isom_fragment_manager_t) ); + if( !root->fragment ) + goto fail; + memset( root->fragment, 0, sizeof(isom_fragment_manager_t) ); + root->fragment->pool = lsmash_create_entry_list(); + if( !root->fragment->pool ) + goto fail; + } + return root; +fail: + lsmash_destroy_root( root ); + return NULL; +} + +static int isom_finish_fragment_movie( lsmash_root_t *root ); + +/* A movie fragment cannot switch a sample description to another. + * So you must call this function before switching sample descriptions. */ +int lsmash_create_fragment_movie( lsmash_root_t *root ) +{ + if( !root || !root->bs || !root->fragment || !root->moov || !root->moov->trak_list ) + return -1; + /* Finish the previous movie fragment before starting a new one. */ + if( isom_finish_fragment_movie( root ) ) + return -1; + /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */ + if( root->fragment->movie && root->moof_list->entry_count != 1 ) + return -1; + isom_moof_entry_t *moof = isom_add_moof( root ); + if( isom_add_mfhd( moof ) ) + return -1; + root->fragment->movie = moof; + moof->mfhd->sequence_number = ++ root->fragment->fragment_count; + if( root->moof_list->entry_count == 1 ) + return 0; + /* Remove the previous movie fragment. */ + return lsmash_remove_entry( root->moof_list, 1, isom_remove_moof ); +} + +static int isom_set_brands( lsmash_root_t *root, lsmash_brand_type_code major_brand, uint32_t minor_version, lsmash_brand_type_code *brands, uint32_t brand_count ) +{ + if( brand_count > 50 ) + return -1; /* We support setting brands up to 50. */ + if( !brand_count ) + { + /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */ + if( root->ftyp ) + { + if( root->ftyp->compatible_brands ) + free( root->ftyp->compatible_brands ); + free( root->ftyp ); + root->ftyp = NULL; + } + return 0; + } + if( !root->ftyp && isom_add_ftyp( root ) ) + return -1; + isom_ftyp_t *ftyp = root->ftyp; + ftyp->major_brand = major_brand; + ftyp->minor_version = minor_version; + lsmash_brand_type_code *compatible_brands; + if( !ftyp->compatible_brands ) + compatible_brands = malloc( brand_count * sizeof(uint32_t) ); + else + compatible_brands = realloc( ftyp->compatible_brands, brand_count * sizeof(uint32_t) ); + if( !compatible_brands ) + return -1; + ftyp->compatible_brands = compatible_brands; + for( uint32_t i = 0; i < brand_count; i++ ) + { + ftyp->compatible_brands[i] = brands[i]; + ftyp->size += 4; + } + ftyp->brand_count = brand_count; + return isom_check_compatibility( root ); +} + +void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param ) +{ + memset( param, 0, sizeof(lsmash_movie_parameters_t) ); + param->max_chunk_duration = 0.5; + param->max_async_tolerance = 2.0; + param->max_chunk_size = 4 * 1024 * 1024; + param->max_read_size = 4 * 1024 * 1024; + param->timescale = 600; + param->playback_rate = 0x00010000; + param->playback_volume = 0x0100; +} + +int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ) +{ + if( !root || !root->moov || !root->moov->mvhd + || isom_set_brands( root, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) ) + return -1; + isom_mvhd_t *mvhd = root->moov->mvhd; + root->max_chunk_duration = param->max_chunk_duration; + root->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration ); + root->max_chunk_size = param->max_chunk_size; + root->max_read_size = param->max_read_size; + mvhd->timescale = param->timescale; + if( root->qt_compatible || root->itunes_audio ) + { + mvhd->rate = param->playback_rate; + mvhd->volume = param->playback_volume; + mvhd->previewTime = param->preview_time; + mvhd->previewDuration = param->preview_duration; + mvhd->posterTime = param->poster_time; + } + else + { + mvhd->rate = 0x00010000; + mvhd->volume = 0x0100; + mvhd->previewTime = 0; + mvhd->previewDuration = 0; + mvhd->posterTime = 0; + } + return 0; +} + +int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ) +{ + if( !root || !root->moov || !root->moov->mvhd ) + return -1; + isom_mvhd_t *mvhd = root->moov->mvhd; + if( root->ftyp ) + { + isom_ftyp_t *ftyp = root->ftyp; + uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 ); /* brands up to 50 */ + for( uint32_t i = 0; i < brand_count; i++ ) + param->brands_shadow[i] = ftyp->compatible_brands[i]; + param->major_brand = ftyp->major_brand; + param->brands = param->brands_shadow; + param->number_of_brands = brand_count; + param->minor_version = ftyp->minor_version; + } + param->max_chunk_duration = root->max_chunk_duration; + param->max_async_tolerance = root->max_async_tolerance; + param->max_chunk_size = root->max_chunk_size; + param->max_read_size = root->max_read_size; + param->timescale = mvhd->timescale; + param->duration = mvhd->duration; + param->playback_rate = mvhd->rate; + param->playback_volume = mvhd->volume; + param->preview_time = mvhd->previewTime; + param->preview_duration = mvhd->previewDuration; + param->poster_time = mvhd->posterTime; + param->number_of_tracks = root->moov->trak_list ? root->moov->trak_list->entry_count : 0; + return 0; +} + +uint32_t lsmash_get_movie_timescale( lsmash_root_t *root ) +{ + if( !root || !root->moov || !root->moov->mvhd ) + return 0; + return root->moov->mvhd->timescale; +} + +static int isom_write_ftyp( lsmash_root_t *root ) +{ + isom_ftyp_t *ftyp = root->ftyp; + if( !ftyp || !ftyp->brand_count ) + return 0; + lsmash_bs_t *bs = root->bs; + isom_bs_put_box_common( bs, ftyp ); + lsmash_bs_put_be32( bs, ftyp->major_brand ); + lsmash_bs_put_be32( bs, ftyp->minor_version ); + for( uint32_t i = 0; i < ftyp->brand_count; i++ ) + lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] ); + if( lsmash_bs_write_data( bs ) ) + return -1; + root->size += ftyp->size; + root->file_type_written = 1; + return 0; +} + +static int isom_write_moov( lsmash_root_t *root ) +{ + if( !root || !root->moov ) + return -1; + lsmash_bs_t *bs = root->bs; + isom_bs_put_box_common( bs, root->moov ); + if( lsmash_bs_write_data( 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( bs, (isom_trak_entry_t *)entry->data ) ) + return -1; + if( isom_write_udta( bs, root->moov, NULL ) ) + return -1; + return isom_write_mvex( bs, root->moov->mvex ); +} + +int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length ) +{ + if( !root || !root->free || !data || !data_length ) + return -1; + isom_free_t *skip = root->free; + uint8_t *tmp = NULL; + if( !skip->data ) + tmp = malloc( data_length ); + else if( skip->length < data_length ) + tmp = realloc( skip->data, data_length ); + if( !tmp ) + return -1; + memcpy( tmp, data, data_length ); + skip->data = tmp; + skip->length = data_length; + return 0; +} + +int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length ) +{ + if( !root ) + return -1; + if( !root->free ) + { + isom_create_box( skip, root, ISOM_BOX_TYPE_FREE ); + root->free = skip; + } + if( data && data_length ) + return lsmash_set_free( root, data, data_length ); + return 0; +} + +int lsmash_write_free( lsmash_root_t *root ) +{ + if( !root || !root->bs || !root->free ) + return -1; + isom_free_t *skip = root->free; + lsmash_bs_t *bs = root->bs; + skip->size = 8 + skip->length; + isom_bs_put_box_common( bs, skip ); + if( skip->data && skip->length ) + lsmash_bs_put_bytes( bs, skip->data, skip->length ); + return lsmash_bs_write_data( bs ); +} + +int lsmash_create_object_descriptor( lsmash_root_t *root ) +{ + if( !root ) + return -1; + /* Return error if this file is not compatible with MP4 file format. */ + if( !root->mp4_version1 && !root->mp4_version2 ) + return -1; + return isom_add_iods( root->moov ); +} + +/*---- finishing functions ----*/ + +static int isom_set_fragment_overall_duration( lsmash_root_t *root ) +{ + if( root->bs->stream == stdout ) + return 0; + isom_mvex_t *mvex = root->moov->mvex; + if( isom_add_mehd( mvex ) ) + return -1; + /* Get the longest duration of the tracks. */ + uint64_t longest_duration = 0; + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak || !trak->cache || !trak->cache->fragment || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale ) + return -1; + uint64_t duration; + if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list ) + { + duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration; + duration = (uint64_t)(((double)duration / trak->mdia->mdhd->timescale) * root->moov->mvhd->timescale); + } + else + { + duration = 0; + for( lsmash_entry_t *elst_entry = trak->edts->elst->list->head; elst_entry; elst_entry = elst_entry->next ) + { + isom_elst_entry_t *data = (isom_elst_entry_t *)elst_entry->data; + if( !data ) + return -1; + duration += data->segment_duration; + } + } + longest_duration = LSMASH_MAX( duration, longest_duration ); + } + mvex->mehd->fragment_duration = longest_duration; + mvex->mehd->version = 1; + isom_update_mehd_size( mvex->mehd ); + /* Write Movie Extends Header Box here. */ + lsmash_bs_t *bs = root->bs; + FILE *stream = bs->stream; + uint64_t current_pos = lsmash_ftell( stream ); + lsmash_fseek( stream, mvex->placeholder_pos, SEEK_SET ); + int ret = isom_write_mehd( bs, mvex->mehd ); + if( !ret ) + ret = lsmash_bs_write_data( bs ); + lsmash_fseek( stream, current_pos, SEEK_SET ); + return ret; +} + +static int isom_write_fragment_random_access_info( lsmash_root_t *root ) +{ + if( root->bs->stream == stdout ) + return 0; + if( isom_update_mfra_size( root->mfra ) ) + return -1; + return isom_write_mfra( root->bs, root->mfra ); +} + +int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux ) +{ + if( !root || !root->bs || !root->moov || !root->moov->trak_list ) + return -1; + if( root->fragment ) + { + /* Output the final movie fragment. */ + if( isom_finish_fragment_movie( root ) ) + return -1; + /* Write the overall random access information at the tail of the movie. */ + if( isom_write_fragment_random_access_info( root ) ) + return -1; + /* Set overall duration of the movie. */ + return isom_set_fragment_overall_duration( root ); + } + isom_moov_t *moov = root->moov; + for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + uint32_t track_ID = trak->tkhd->track_ID; + uint32_t related_track_ID = trak->related_track_ID; + /* Disable the track if the track is a track reference chapter. */ + if( trak->is_chapter ) + trak->tkhd->flags &= ~ISOM_TRACK_ENABLED; + if( trak->is_chapter && related_track_ID ) + { + /* In order that the track duration of the chapter track doesn't exceed that of the related track. */ + uint64_t track_duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) ); + if( lsmash_create_explicit_timeline_map( root, track_ID, track_duration, 0, ISOM_EDIT_MODE_NORMAL ) ) + return -1; + } + /* Add stss box if any samples aren't sync sample. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) ) + return -1; + if( isom_update_bitrate_info( trak->mdia ) ) + return -1; + } + if( root->mp4_version1 == 1 && isom_add_iods( moov ) ) + return -1; + if( isom_check_mandatory_boxes( root ) + || isom_set_movie_creation_time( root ) + || isom_update_moov_size( moov ) + || isom_write_mdat_size( root ) ) + return -1; + + if( !remux ) + { + if( isom_write_moov( root ) ) + return -1; + root->size += moov->size; + return 0; + } + + /* stco->co64 conversion, depending on last chunk's offset */ + for( lsmash_entry_t* entry = moov->trak_list->head; entry; ) + { + isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data; + isom_stco_t* stco = trak->mdia->minf->stbl->stco; + if( !stco->list->tail ) + return -1; + if( stco->large_presentation + || ((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size <= UINT32_MAX ) + { + entry = entry->next; + continue; /* no need to convert stco into co64 */ + } + /* stco->co64 conversion */ + if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) + || isom_update_moov_size( moov ) ) + return -1; + entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */ + } + + /* now the amount of offset is fixed. */ + + /* buffer size must be at least sizeof(moov)*2 */ + remux->buffer_size = LSMASH_MAX( remux->buffer_size, 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 = 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 += 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 += moov->size; + } + + FILE *stream = root->bs->stream; + isom_mdat_t *mdat = root->mdat; + uint64_t total = lsmash_ftell( stream ) + moov->size; // FIXME: + uint64_t readnum; + /* backup starting area of mdat and write moov there instead */ + if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) ) + goto fail; + readnum = fread( buf[0], 1, size, stream ); + uint64_t read_pos = lsmash_ftell( stream ); + + /* write moov there instead */ + if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) + || isom_write_moov( root ) ) + goto fail; + uint64_t write_pos = lsmash_ftell( stream ); + + mdat->placeholder_pos += moov->size; /* update placeholder */ + + /* copy-pastan */ + int buf_switch = 1; + while( readnum == size ) + { + if( lsmash_fseek( stream, read_pos, SEEK_SET ) ) + goto fail; + readnum = fread( buf[buf_switch], 1, size, stream ); + read_pos = lsmash_ftell( stream ); + + buf_switch ^= 0x1; + + if( lsmash_fseek( stream, write_pos, SEEK_SET ) + || fwrite( buf[buf_switch], 1, size, stream ) != size ) + goto fail; + write_pos = lsmash_ftell( stream ); + if( remux->func ) remux->func( remux->param, write_pos, total ); // FIXME: + } + if( fwrite( buf[buf_switch^0x1], 1, readnum, stream ) != readnum ) + goto fail; + if( remux->func ) remux->func( remux->param, total, total ); // FIXME: + + root->size += moov->size; + free( buf[0] ); + return 0; + +fail: + free( buf[0] ); + return -1; +} + +#define GET_MOST_USED( box_name, index, flag_name ) \ + if( most_used[index] < stats.flag_name[i] ) \ + { \ + most_used[index] = stats.flag_name[i]; \ + box_name->default_sample_flags.flag_name = i; \ + } + +static int isom_create_fragment_overall_default_settings( lsmash_root_t *root ) +{ + if( isom_add_mvex( root->moov ) ) + return -1; + for( lsmash_entry_t *trak_entry = root->moov->trak_list->head; trak_entry; trak_entry = trak_entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)trak_entry->data; + if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stts || !stbl->stts->list || !stbl->stsz + || (stbl->stts->list->tail && !stbl->stts->list->tail->data) + || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) ) + return -1; + isom_trex_entry_t *trex = isom_add_trex( root->moov->mvex ); + if( !trex ) + return -1; + trex->track_ID = trak->tkhd->track_ID; + /* Set up defaults. */ + trex->default_sample_description_index = trak->cache->chunk.sample_description_index ? trak->cache->chunk.sample_description_index : 1; + trex->default_sample_duration = stbl->stts->list->tail ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta : 1; + trex->default_sample_size = !stbl->stsz->list + ? stbl->stsz->sample_size : stbl->stsz->list->head + ? ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size : 0; + if( stbl->sdtp && stbl->sdtp->list ) + { + struct sample_flags_stats_t + { + uint32_t is_leading [4]; + uint32_t sample_depends_on [4]; + uint32_t sample_is_depended_on[4]; + uint32_t sample_has_redundancy[4]; + } stats = { { 0 }, { 0 }, { 0 }, { 0 } }; + for( lsmash_entry_t *sdtp_entry = stbl->sdtp->list->head; sdtp_entry; sdtp_entry = sdtp_entry->next ) + { + isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)sdtp_entry->data; + if( !data ) + return -1; + ++ stats.is_leading [ data->is_leading ]; + ++ stats.sample_depends_on [ data->sample_depends_on ]; + ++ stats.sample_is_depended_on[ data->sample_is_depended_on ]; + ++ stats.sample_has_redundancy[ data->sample_has_redundancy ]; + } + uint32_t most_used[4] = { 0, 0, 0, 0 }; + for( int i = 0; i < 4; i++ ) + { + GET_MOST_USED( trex, 0, is_leading ); + GET_MOST_USED( trex, 1, sample_depends_on ); + GET_MOST_USED( trex, 2, sample_is_depended_on ); + GET_MOST_USED( trex, 3, sample_has_redundancy ); + } + } + trex->default_sample_flags.sample_is_non_sync_sample = !trak->cache->all_sync; + } + return 0; +} + +static int isom_prepare_random_access_info( lsmash_root_t *root ) +{ + if( root->bs->stream == stdout ) + return 0; + if( isom_add_mfra( root ) + || isom_add_mfro( root->mfra ) ) + return -1; + return 0; +} + +static int isom_output_fragment_media_data( lsmash_root_t *root ) +{ + isom_fragment_manager_t *fragment = root->fragment; + if( !fragment->pool->entry_count ) + { + /* no need to write media data */ + lsmash_remove_entries( fragment->pool, lsmash_delete_sample ); + fragment->pool_size = 0; + return 0; + } + /* If there is no available Media Data Box to write samples, add and write a new one. */ + if( isom_new_mdat( root, fragment->pool_size ) ) + return -1; + /* Write samples in the current movie fragment. */ + for( lsmash_entry_t* entry = fragment->pool->head; entry; entry = entry->next ) + { + lsmash_sample_t *sample = (lsmash_sample_t *)entry->data; + if( !sample || !sample->data ) + return -1; + lsmash_bs_put_bytes( root->bs, sample->data, sample->length ); + } + if( lsmash_bs_write_data( root->bs ) ) + return -1; + root->size += root->mdat->size; + lsmash_remove_entries( fragment->pool, lsmash_delete_sample ); + fragment->pool_size = 0; + return 0; +} + +static int isom_finish_fragment_initial_movie( lsmash_root_t *root ) +{ + if( !root->moov || !root->moov->trak_list ) + return -1; + isom_moov_t *moov = root->moov; + for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data; + if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl ) + return -1; + if( isom_get_sample_count( trak ) ) + { + /* Add stss box if any samples aren't sync sample. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) ) + return -1; + } + else + trak->tkhd->duration = 0; + if( isom_update_bitrate_info( trak->mdia ) ) + return -1; + } + if( root->mp4_version1 == 1 && isom_add_iods( moov ) ) + return -1; + if( isom_create_fragment_overall_default_settings( root ) + || isom_prepare_random_access_info( root ) + || isom_check_mandatory_boxes( root ) + || isom_set_movie_creation_time( root ) + || isom_update_moov_size( moov ) ) + return -1; + /* stco->co64 conversion, depending on last chunk's offset */ + for( lsmash_entry_t* entry = moov->trak_list->head; entry; ) + { + isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data; + isom_stco_t* stco = trak->mdia->minf->stbl->stco; + if( !stco->list->tail /* no samples */ + || stco->large_presentation + || ((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size <= UINT32_MAX ) + { + entry = entry->next; + continue; /* no need to convert stco into co64 */ + } + /* stco->co64 conversion */ + if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) + || isom_update_moov_size( moov ) ) + return -1; + entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */ + } + /* Now, the amount of offset is fixed. Apply that to stco/co64. */ + for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next ) + { + isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco; + if( stco->large_presentation ) + for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next ) + ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += 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 += moov->size; + } + /* Write File Type Box here if it was not written yet. */ + if( !root->file_type_written && isom_write_ftyp( root ) ) + return -1; + /* Write Movie Box. */ + if( isom_write_moov( root ) ) + return -1; + root->size += moov->size; + /* Output samples. */ + return isom_output_fragment_media_data( root ); +} + +static int isom_finish_fragment_movie( lsmash_root_t *root ) +{ + if( !root->moov || !root->moov->trak_list || !root->fragment || !root->fragment->pool ) + return -1; + isom_moof_entry_t *moof = root->fragment->movie; + if( !moof ) + return isom_finish_fragment_initial_movie( root ); + /* Calculate appropriate default_sample_flags of each Track Fragment Header Box. + * And check whether that default_sample_flags is useful or not. */ + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + { + isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data; + if( !traf || !traf->tfhd || !traf->root || !traf->root->moov || !traf->root->moov->mvex ) + return -1; + isom_tfhd_t *tfhd = traf->tfhd; + isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, tfhd->track_ID ); + if( !trex ) + return -1; + struct sample_flags_stats_t + { + uint32_t is_leading [4]; + uint32_t sample_depends_on [4]; + uint32_t sample_is_depended_on [4]; + uint32_t sample_has_redundancy [4]; + uint32_t sample_is_non_sync_sample[2]; + } stats = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; + for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next ) + { + isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data; + if( !trun || !trun->sample_count ) + return -1; + isom_sample_flags_t *sample_flags; + if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) + { + if( !trun->optional ) + return -1; + for( lsmash_entry_t *optional_entry = trun->optional->head; optional_entry; optional_entry = optional_entry->next ) + { + isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data; + if( !row ) + return -1; + sample_flags = &row->sample_flags; + ++ stats.is_leading [ sample_flags->is_leading ]; + ++ stats.sample_depends_on [ sample_flags->sample_depends_on ]; + ++ stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ]; + ++ stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ]; + ++ stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ]; + } + } + else + { + sample_flags = &tfhd->default_sample_flags; + stats.is_leading [ sample_flags->is_leading ] += trun->sample_count; + stats.sample_depends_on [ sample_flags->sample_depends_on ] += trun->sample_count; + stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ] += trun->sample_count; + stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ] += trun->sample_count; + stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ] += trun->sample_count; + } + } + uint32_t most_used[5] = { 0, 0, 0, 0, 0 }; + for( int i = 0; i < 4; i++ ) + { + GET_MOST_USED( tfhd, 0, is_leading ); + GET_MOST_USED( tfhd, 1, sample_depends_on ); + GET_MOST_USED( tfhd, 2, sample_is_depended_on ); + GET_MOST_USED( tfhd, 3, sample_has_redundancy ); + if( i < 2 ) + GET_MOST_USED( tfhd, 4, sample_is_non_sync_sample ); + } + int useful_default_sample_duration = 0; + int useful_default_sample_size = 0; + for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next ) + { + isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data; + if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) ) + useful_default_sample_duration = 1; + if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) ) + useful_default_sample_size = 1; + int useful_first_sample_flags = 1; + int useful_default_sample_flags = 1; + if( trun->sample_count == 1 ) + { + /* It is enough to check only if first_sample_flags equals default_sample_flags or not. + * If it is equal, just use default_sample_flags. + * If not, just use first_sample_flags of this run. */ + if( !memcmp( &trun->first_sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) ) + useful_first_sample_flags = 0; + } + else if( trun->optional && trun->optional->head ) + { + lsmash_entry_t *optional_entry = trun->optional->head->next; + isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data; + isom_sample_flags_t representative_sample_flags = row->sample_flags; + if( memcmp( &tfhd->default_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) ) + useful_default_sample_flags = 0; + if( !memcmp( &trun->first_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) ) + useful_first_sample_flags = 0; + if( useful_default_sample_flags ) + for( optional_entry = optional_entry->next; optional_entry; optional_entry = optional_entry->next ) + { + row = (isom_trun_optional_row_t *)optional_entry->data; + if( memcmp( &representative_sample_flags, &row->sample_flags, sizeof(isom_sample_flags_t) ) ) + { + useful_default_sample_flags = 0; + break; + } + } + } + if( useful_default_sample_flags ) + { + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT; + trun->flags &= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT; + } + else + { + useful_first_sample_flags = 0; + trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT; + } + if( useful_first_sample_flags ) + trun->flags |= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT; + } + if( useful_default_sample_duration && tfhd->default_sample_duration != trex->default_sample_duration ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT; + else + tfhd->default_sample_duration = trex->default_sample_duration; /* This might be redundant, but is to be more natural. */ + if( useful_default_sample_size && tfhd->default_sample_size != trex->default_sample_size ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT; + else + tfhd->default_sample_size = trex->default_sample_size; /* This might be redundant, but is to be more natural. */ + if( !(tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) ) + tfhd->default_sample_flags = trex->default_sample_flags; /* This might be redundant, but is to be more natural. */ + else if( !memcmp( &tfhd->default_sample_flags, &trex->default_sample_flags, sizeof(isom_sample_flags_t) ) ) + tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT; + } + /* When using for live streaming, setting explicit base_data_offset is not preferable. + * However, it's OK because we haven't supported this yet. + * Implicit base_data_offsets that originate in the first byte of each Movie Fragment Box will be implemented + * by the feature of ISO Base Media File Format version 5 or later. + * Media Data Box starts immediately after Movie Fragment Box. */ + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + { + isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data; + traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT; + } + /* Consider the update of tf_flags here. */ + if( isom_update_moof_entry_size( moof ) ) + return -1; + /* Now, we can calculate offsets in the current movie fragment, so do it. */ + for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next ) + { + isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data; + traf->tfhd->base_data_offset = root->size + moof->size + ISOM_DEFAULT_BOX_HEADER_SIZE; + } + if( isom_write_moof( root->bs, moof ) ) + return -1; + root->size += moof->size; + /* Output samples. */ + return isom_output_fragment_media_data( root ); +} + +#undef GET_MOST_USED + +static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_entry_t *trun, isom_tfhd_t *tfhd, uint32_t sample_number ) +{ + isom_trun_optional_row_t *row = NULL; + if( !trun->optional ) + { + trun->optional = lsmash_create_entry_list(); + if( !trun->optional ) + return NULL; + } + if( trun->optional->entry_count < sample_number ) + { + while( trun->optional->entry_count < sample_number ) + { + row = malloc( sizeof(isom_trun_optional_row_t) ); + if( !row ) + return NULL; + /* Copy from default. */ + row->sample_duration = tfhd->default_sample_duration; + row->sample_size = tfhd->default_sample_size; + row->sample_flags = tfhd->default_sample_flags; + row->sample_composition_time_offset = 0; + if( lsmash_add_entry( trun->optional, row ) ) + { + free( row ); + return NULL; + } + } + return row; + } + uint32_t i = 0; + for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next ) + { + row = (isom_trun_optional_row_t *)entry->data; + if( !row ) + return NULL; + if( ++i == sample_number ) + return row; + } + return NULL; +} + +int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration ) +{ + if( !root || !root->fragment || !root->fragment->movie || !root->moov ) + return -1; + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, track_ID ); + if( !trex ) + return -1; + isom_moof_entry_t *moof = root->fragment->movie; + isom_traf_entry_t *traf = isom_get_traf( moof, track_ID ); + if( traf ) + return -1; + traf = isom_add_traf( root, moof ); + if( isom_add_tfhd( traf ) ) + return -1; + isom_tfhd_t *tfhd = traf->tfhd; + tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */ + tfhd->track_ID = trak->tkhd->track_ID; + tfhd->default_sample_duration = duration; + if( duration != trex->default_sample_duration ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT; + traf->cache = trak->cache; + traf->cache->fragment->traf_number = moof->traf_list->entry_count; + traf->cache->fragment->last_duration += duration; /* The duration of the last sample includes this empty-duration. */ + return 0; +} + +static int isom_set_fragment_last_duration( isom_traf_entry_t *traf, uint32_t last_duration ) +{ + isom_tfhd_t *tfhd = traf->tfhd; + if( !traf->trun_list || !traf->trun_list->tail || !traf->trun_list->tail->data ) + { + /* There are no track runs in this track fragment, so it is a empty-duration. */ + isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID ); + if( !trex ) + return -1; + tfhd->flags |= ISOM_TF_FLAGS_DURATION_IS_EMPTY; + if( last_duration != trex->default_sample_duration ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT; + tfhd->default_sample_duration = last_duration; + traf->cache->fragment->last_duration = last_duration; + return 0; + } + /* Update the last sample_duration if needed. */ + isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data; + if( trun->sample_count == 1 && traf->trun_list->entry_count == 1 ) + { + isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID ); + if( !trex ) + return -1; + if( last_duration != trex->default_sample_duration ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT; + tfhd->default_sample_duration = last_duration; + } + else if( last_duration != tfhd->default_sample_duration ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT; + if( trun->flags ) + { + isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count ); + if( !row ) + return -1; + row->sample_duration = last_duration; + } + traf->cache->fragment->last_duration = last_duration; + return 0; +} + +int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta ) +{ + if( !root || !track_ID ) + return -1; + if( root->fragment && root->fragment->movie ) + { + isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID ); + if( !traf || !traf->cache || !traf->tfhd || !traf->trun_list ) + return -1; + return isom_set_fragment_last_duration( traf, sample_delta ); + } + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsz || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list ) + return -1; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stts_t *stts = stbl->stts; + uint32_t sample_count = isom_get_sample_count( trak ); + if( !stts->list->tail ) + { + if( !sample_count ) + return 0; /* no samples */ + if( sample_count > 1 ) + return -1; /* irregular sample_count */ + if( isom_add_stts_entry( stbl, sample_delta ) ) + return -1; + return lsmash_update_track_duration( root, track_ID, 0 ); + } + uint32_t i = 0; + for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next ) + i += ((isom_stts_entry_t *)entry->data)->sample_count; + if( sample_count < i ) + return -1; + isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data; + if( !last_stts_data ) + return -1; + if( sample_count > i ) + { + if( sample_count - i > 1 ) + return -1; + /* Add a sample_delta. */ + if( sample_delta == last_stts_data->sample_delta ) + ++ last_stts_data->sample_count; + else if( isom_add_stts_entry( stbl, sample_delta ) ) + return -1; + } + else if( sample_count == i && isom_replace_last_sample_delta( stbl, sample_delta ) ) + return -1; + return lsmash_update_track_duration( root, track_ID, sample_delta ); +} + +void lsmash_discard_boxes( lsmash_root_t *root ) +{ + if( !root ) + return; + isom_remove_ftyp( root->ftyp ); + isom_remove_moov( root ); + lsmash_remove_list( root->moof_list, isom_remove_moof ); + isom_remove_mdat( root->mdat ); + isom_remove_free( root->free ); + isom_remove_mfra( root->mfra ); + root->ftyp = NULL; + root->moov = NULL; + root->moof_list = NULL; + root->mdat = NULL; + root->free = NULL; + root->mfra = NULL; +} + +void lsmash_destroy_root( lsmash_root_t *root ) +{ + if( !root ) + return; +#ifdef LSMASH_DEMUXER_ENABLED + isom_remove_print_funcs( root ); + isom_remove_timelines( root ); +#endif + lsmash_discard_boxes( root ); + if( root->bs ) + { + if( root->bs->stream ) + fclose( root->bs->stream ); + if( root->bs->data ) + free( root->bs->data ); + free( root->bs ); + } + if( root->fragment ) + { + lsmash_remove_list( root->fragment->pool, lsmash_delete_sample ); + free( root->fragment ); + } + free( root ); +} + +/*---- timeline manipulator ----*/ + +int lsmash_modify_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint64_t segment_duration, int64_t media_time, int32_t media_rate ) +{ + if( !segment_duration || media_time < -1 ) + return -1; + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list ) + return -1; + isom_elst_t *elst = trak->edts->elst; + isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, entry_number ); + if( !data ) + return -1; + data->segment_duration = segment_duration; + data->media_time = media_time; + data->media_rate = media_rate; + if( !elst->pos || !root->fragment || root->bs->stream == stdout ) + return isom_update_tkhd_duration( trak ); + /* Rewrite the specified entry. + * Note: we don't update the version of the Edit List Box. */ + lsmash_bs_t *bs = root->bs; + FILE *stream = bs->stream; + uint64_t current_pos = lsmash_ftell( stream ); + uint64_t entry_pos = elst->pos + ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + ((uint64_t)entry_number - 1) * (elst->version == 1 ? 20 : 12); + lsmash_fseek( stream, entry_pos, SEEK_SET ); + if( elst->version ) + { + lsmash_bs_put_be64( bs, data->segment_duration ); + lsmash_bs_put_be64( bs, data->media_time ); + } + else + { + lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration ); + lsmash_bs_put_be32( bs, (uint32_t)data->media_time ); + } + lsmash_bs_put_be32( bs, data->media_rate ); + int ret = lsmash_bs_write_data( bs ); + lsmash_fseek( stream, current_pos, SEEK_SET ); + return ret; +} + +int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint64_t segment_duration, int64_t media_time, int32_t media_rate ) +{ + if( media_time < -1 ) + return -1; + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + segment_duration = (segment_duration || root->fragment) ? segment_duration + : trak->tkhd->duration ? trak->tkhd->duration + : isom_update_tkhd_duration( trak ) ? 0 + : trak->tkhd->duration; + if( isom_add_edts( trak ) + || isom_add_elst( trak->edts ) + || isom_add_elst_entry( trak->edts->elst, segment_duration, media_time, media_rate ) ) + return -1; + return isom_update_tkhd_duration( trak ); +} + +/*---- create / modification time fields manipulators ----*/ + +int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->mdia || !trak->mdia->mdhd ) + return -1; + isom_mdhd_t *mdhd = trak->mdia->mdhd; + mdhd->modification_time = isom_get_current_mp4time(); + /* overwrite strange creation_time */ + if( mdhd->creation_time > mdhd->modification_time ) + mdhd->creation_time = mdhd->modification_time; + return 0; +} + +int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->tkhd ) + return -1; + isom_tkhd_t *tkhd = trak->tkhd; + tkhd->modification_time = isom_get_current_mp4time(); + /* overwrite strange creation_time */ + if( tkhd->creation_time > tkhd->modification_time ) + tkhd->creation_time = tkhd->modification_time; + return 0; +} + +int lsmash_update_movie_modification_time( lsmash_root_t *root ) +{ + if( !root || !root->moov || !root->moov->mvhd ) + return -1; + isom_mvhd_t *mvhd = root->moov->mvhd; + mvhd->modification_time = isom_get_current_mp4time(); + /* overwrite strange creation_time */ + if( mvhd->creation_time > mvhd->modification_time ) + mvhd->creation_time = mvhd->modification_time; + return 0; +} + +/*---- sample manipulators ----*/ +lsmash_sample_t *lsmash_create_sample( uint32_t size ) +{ + lsmash_sample_t *sample = malloc( sizeof(lsmash_sample_t) ); + if( !sample ) + return NULL; + memset( sample, 0, sizeof(lsmash_sample_t) ); + if( size ) + { + sample->data = malloc( size ); + if( !sample->data ) + { + free( sample ); + return NULL; + } + sample->length = size; + } + return sample; +} + +int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size ) +{ + if( !sample ) + return -1; + if( !size ) + { + if( sample->data ) + free( sample->data ); + sample->data = NULL; + sample->length = 0; + return 0; + } + if( size == sample->length ) + return 0; + uint8_t *data; + if( !sample->data ) + data = malloc( size ); + else + data = realloc( sample->data, size ); + if( !data ) + return -1; + sample->data = data; + sample->length = size; + return 0; +} + +void lsmash_delete_sample( lsmash_sample_t *sample ) +{ + if( !sample ) + return; + if( sample->data ) + free( sample->data ); + free( sample ); +} + +static uint32_t isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size ) +{ + if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) ) + return 0; + return isom_get_sample_count( trak ); +} + +static uint32_t isom_add_dts( isom_trak_entry_t *trak, uint64_t dts ) +{ + if( !trak->cache || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list ) + return 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_stts_t *stts = stbl->stts; + isom_timestamp_t *cache = &trak->cache->timestamp; + if( !stts->list->entry_count ) + { + if( isom_add_stts_entry( stbl, dts ) ) + return 0; + cache->dts = dts; + return dts; + } + if( dts <= cache->dts ) + return 0; + uint32_t sample_delta = dts - cache->dts; + isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data; + if( data->sample_delta == sample_delta ) + ++ data->sample_count; + else if( isom_add_stts_entry( stbl, sample_delta ) ) + return 0; + cache->dts = dts; + return sample_delta; +} + +static int isom_add_cts( isom_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; + isom_timestamp_t *cache = &trak->cache->timestamp; + if( !ctts ) + { + if( cts == cache->dts ) + { + cache->cts = cts; + return 0; + } + /* Add ctts box and the first ctts entry. */ + if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) ) + return -1; + 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 - cache->dts ) ) + return -1; + } + else + data->sample_offset = cts; + cache->cts = cts; + return 0; + } + if( !ctts->list ) + return -1; + isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data; + uint32_t sample_offset = cts - cache->dts; + if( data->sample_offset == sample_offset ) + ++ data->sample_count; + else if( isom_add_ctts_entry( stbl, sample_offset ) ) + return -1; + cache->cts = cts; + return 0; +} + +static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts ) +{ + if( cts < dts ) + return -1; + uint32_t sample_count = isom_get_sample_count( trak ); + uint32_t sample_delta = sample_count > 1 ? isom_add_dts( trak, dts ) : 0; + if( sample_count > 1 && !sample_delta ) + return -1; + if( isom_add_cts( trak, cts ) ) + return -1; + if( trak->cache->fragment ) + { + isom_cache_t *cache = trak->cache; + cache->fragment->last_duration = sample_delta; + cache->fragment->largest_cts = LSMASH_MAX( cache->timestamp.cts, cache->fragment->largest_cts ); + } + return 0; +} + +static int isom_add_sync_point( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_cache_t *cache = trak->cache; + if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC ) /* no null check for prop */ + { + if( !cache->all_sync ) + return 0; + if( !stbl->stss && isom_add_stss( stbl ) ) + return -1; + if( isom_add_stss_entry( stbl, 1 ) ) /* Declare here the first sample is a sync sample. */ + return -1; + cache->all_sync = 0; + return 0; + } + if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */ + return 0; + if( !stbl->stss ) + { + if( isom_get_sample_count( trak ) == 1 ) + { + cache->all_sync = 1; /* Also the first sample is a sync sample. */ + return 0; + } + if( isom_add_stss( stbl ) ) + return -1; + } + return isom_add_stss_entry( stbl, sample_number ); +} + +static int isom_add_partial_sync( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop ) +{ + if( !trak->root->qt_compatible ) + return 0; + if( prop->random_access_type != QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC + && !(prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete) ) + return 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->stps && isom_add_stps( stbl ) ) + return -1; + return isom_add_stps_entry( stbl, sample_number ); +} + +static int isom_add_dependency_type( isom_trak_entry_t *trak, lsmash_sample_property_t *prop ) +{ + if( !trak->root->qt_compatible && !trak->root->avc_extensions ) + return 0; + uint8_t avc_extensions = trak->root->avc_extensions; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( stbl->sdtp ) + return isom_add_sdtp_entry( stbl, prop, avc_extensions ); + if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant ) /* no null check for prop */ + return 0; + if( isom_add_sdtp( stbl ) ) + return -1; + uint32_t count = isom_get_sample_count( trak ); + /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */ + lsmash_sample_property_t null_prop = { 0 }; + for( uint32_t i = 1; i < count; i++ ) + if( isom_add_sdtp_entry( stbl, &null_prop, avc_extensions ) ) + return -1; + return isom_add_sdtp_entry( stbl, prop, avc_extensions ); +} + +static int isom_group_random_access( isom_trak_entry_t *trak, lsmash_sample_property_t *prop ) +{ + if( trak->root->max_isom_version < 6 ) + return 0; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP ); + isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); + if( !sbgp || !sgpd ) + return 0; + uint8_t is_rap = prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP + || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP + || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP + || (prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete); + isom_rap_group_t *group = trak->cache->rap; + if( !group ) + { + /* This sample is the first sample, create a grouping cache. */ + assert( isom_get_sample_count( trak ) == 1 ); + group = malloc( sizeof(isom_rap_group_t) ); + if( !group ) + return -1; + if( is_rap ) + { + group->random_access = isom_add_rap_group_entry( sgpd ); + group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count ); + } + else + { + /* The first sample is not always random access point. */ + group->random_access = NULL; + group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + } + if( !group->assignment ) + { + free( group ); + return -1; + } + /* No need checking if group->assignment exists from here. */ + group->is_prev_rap = is_rap; + trak->cache->rap = group; + return 0; + } + if( group->is_prev_rap ) + { + /* OK. here, the previous sample is a menber of 'rap '. */ + if( !is_rap ) + { + /* This sample isn't a member of 'rap ' and the previous sample is. + * So we create a new group and set 0 on its group_description_index. */ + group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + if( !group->assignment ) + { + free( group ); + return -1; + } + } + else if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP ) + { + /* Create a new group since there is the possibility the next sample is a leading sample. + * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */ + if( group->random_access ) + group->random_access->num_leading_samples_known = 1; + group->random_access = isom_add_rap_group_entry( sgpd ); + group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count ); + if( !group->assignment ) + { + free( group ); + return -1; + } + } + else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */ + ++ group->assignment->sample_count; + } + else if( is_rap ) + { + /* This sample is a member of 'rap ' and the previous sample isn't. + * So we create a new group and set appropriate value on its group_description_index. */ + if( group->random_access ) + group->random_access->num_leading_samples_known = 1; + group->random_access = isom_add_rap_group_entry( sgpd ); + group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count ); + if( !group->assignment ) + { + free( group ); + return -1; + } + } + else /* The previous and current sample aren't a member of 'rap '. */ + ++ group->assignment->sample_count; + /* Obtain the property of the latest random access point group. */ + if( !is_rap && group->random_access ) + { + if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN ) + { + /* We can no longer know num_leading_samples in this group. */ + group->random_access->num_leading_samples_known = 0; + group->random_access = NULL; + } + else + { + if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING ) + ++ group->random_access->num_leading_samples; + else + { + /* no more consecutive leading samples in this group */ + group->random_access->num_leading_samples_known = 1; + group->random_access = NULL; + } + } + } + group->is_prev_rap = is_rap; + return 0; +} + +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_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL ); + isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL ); + if( !sbgp || !sgpd ) + return 0; + lsmash_entry_list_t *pool = trak->cache->roll.pool; + if( !pool ) + { + pool = lsmash_create_entry_list(); + if( !pool ) + return -1; + trak->cache->roll.pool = pool; + } + isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count ); + uint32_t sample_count = isom_get_sample_count( trak ); + if( !group || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY ) + { + if( group ) + group->delimited = 1; + else + assert( sample_count == 1 ); + /* Create a new group. This group is not 'roll' yet, so we set 0 on its group_description_index. */ + group = 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->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 ); + if( !group->assignment || lsmash_add_entry( pool, group ) ) + { + free( group ); + return -1; + } + } + else + ++ group->assignment->sample_count; + /* If encountered a sync sample, all recoveries are completed here. */ + if( prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP ) + { + 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; + if( prop->recovery.identifier == group->recovery_point ) + { + group->described = 1; + int16_t distance = sample_count - group->first_sample; + /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */ + if( distance ) + { + /* Now, this group is a 'roll'. */ + if( !isom_add_roll_group_entry( sgpd, distance ) ) + return -1; + group->assignment->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. */ + } + } + /* Remove pooled caches that has become unnecessary. */ + 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_append_sample for detail. */ +static int isom_add_chunk( isom_trak_entry_t *trak, lsmash_sample_t *sample ) +{ + if( !trak->root || !trak->cache || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return -1; + lsmash_root_t *root = trak->root; + isom_chunk_t *current = &trak->cache->chunk; + if( !current->pool ) + { + /* Very initial settings, just once per track */ + current->pool = lsmash_create_entry_list(); + if( !current->pool ) + return -1; + } + if( !current->pool->entry_count ) + { + /* Cannot decide whether we should flush the current sample or not here yet. */ + ++ current->chunk_number; + current->sample_description_index = sample->index; + current->first_dts = sample->dts; + return 0; + } + if( sample->dts < current->first_dts ) + return -1; /* easy error check. */ + if( (root->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale)) + && (root->max_chunk_size >= current->pool_size + sample->length) + && (current->sample_description_index == sample->index) ) + return 0; /* No need to flush current cached chunk, the current sample must be put into that. */ + /* NOTE: chunk relative stuff must be pushed into root after a chunk is fully determined with its contents. */ + /* now current cached chunk is fixed, actually add chunk relative properties to root accordingly. */ + isom_stbl_t *stbl = trak->mdia->minf->stbl; + lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail; + /* Create a new chunk sequence in this track if needed. */ + if( (!last_stsc_entry || current->pool->entry_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk) + && 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. */ + uint64_t offset = root->size; + if( root->fragment ) + offset += ISOM_DEFAULT_BOX_HEADER_SIZE + root->fragment->pool_size; + if( isom_add_stco_entry( stbl, offset ) ) + return -1; + /* update cache information */ + ++ current->chunk_number; + /* re-initialize cache, using the current sample */ + current->sample_description_index = sample->index; + current->first_dts = sample->dts; + /* current->pool must be flushed in isom_append_sample_internal() */ + return 1; +} + +static int isom_write_pooled_samples( lsmash_root_t *root, isom_chunk_t *chunk ) +{ + if( !root || !root->mdat || !root->bs || !root->bs->stream ) + return -1; + uint64_t chunk_size = 0; + for( lsmash_entry_t *entry = chunk->pool->head; entry; entry = entry->next ) + { + lsmash_sample_t *sample = (lsmash_sample_t *)entry->data; + if( !sample || !sample->data ) + return -1; + lsmash_bs_put_bytes( root->bs, sample->data, sample->length ); + chunk_size += sample->length; + } + if( lsmash_bs_write_data( root->bs ) ) + return -1; + root->mdat->size += chunk_size; + root->size += chunk_size; + lsmash_remove_entries( chunk->pool, lsmash_delete_sample ); + chunk->pool_size = 0; + return 0; +} + +static int isom_update_sample_tables( isom_trak_entry_t *trak, lsmash_sample_t *sample ) +{ + /* Add a sample_size and increment sample_count. */ + uint32_t sample_count = isom_add_size( trak, sample->length ); + if( !sample_count ) + return -1; + /* Add a decoding timestamp and a composition timestamp. */ + if( isom_add_timestamp( trak, sample->dts, sample->cts ) ) + return -1; + /* Add a sync point if needed. */ + if( isom_add_sync_point( trak, sample_count, &sample->prop ) ) + return -1; + /* Add a partial sync point if needed. */ + if( isom_add_partial_sync( trak, sample_count, &sample->prop ) ) + return -1; + /* Add leading, independent, disposable and redundant information if needed. */ + if( isom_add_dependency_type( trak, &sample->prop ) ) + return -1; + /* Group samples into random access point type if needed. */ + if( isom_group_random_access( trak, &sample->prop ) ) + return -1; + /* Group samples into random access recovery point type if needed. */ + if( isom_group_roll_recovery( trak, &sample->prop ) ) + return -1; + /* Add a chunk if needed. */ + return isom_add_chunk( trak, sample ); +} + +static void isom_append_fragment_track_run( lsmash_root_t *root, isom_chunk_t *chunk ) +{ + if( !chunk->pool || !chunk->pool->head ) + return; + isom_fragment_manager_t *fragment = root->fragment; + /* Move samples in the pool of the current track fragment to the pool of the current movie fragment. + * Empty the pool of current track. We don't delete data of samples here. */ + if( fragment->pool->tail ) + { + fragment->pool->tail->next = chunk->pool->head; + fragment->pool->tail->next->prev = fragment->pool->tail; + } + else + fragment->pool->head = chunk->pool->head; + fragment->pool->tail = chunk->pool->tail; + fragment->pool->entry_count += chunk->pool->entry_count; + fragment->pool_size += chunk->pool_size; + chunk->pool_size = 0; + chunk->pool->entry_count = 0; + chunk->pool->head = NULL; + chunk->pool->tail = NULL; +} + +static int isom_output_cached_chunk( isom_trak_entry_t *trak ) +{ + lsmash_root_t *root = trak->root; + isom_chunk_t *chunk = &trak->cache->chunk; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail; + /* Create a new chunk sequence in this track if needed. */ + if( (!last_stsc_entry || chunk->pool->entry_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk) + && isom_add_stsc_entry( stbl, chunk->chunk_number, chunk->pool->entry_count, chunk->sample_description_index ) ) + return -1; + if( root->fragment ) + { + /* Add a new chunk offset in this track. */ + if( isom_add_stco_entry( stbl, root->size + ISOM_DEFAULT_BOX_HEADER_SIZE + root->fragment->pool_size ) ) + return -1; + isom_append_fragment_track_run( root, chunk ); + return 0; + } + /* Add a new chunk offset in this track. */ + if( isom_add_stco_entry( stbl, root->size ) ) + return -1; + /* Output pooled samples in this track. */ + return isom_write_pooled_samples( root, chunk ); +} + +static int isom_append_sample_internal( isom_trak_entry_t *trak, lsmash_sample_t *sample ) +{ + int flush = isom_update_sample_tables( trak, sample ); + if( flush < 0 ) + return -1; + /* flush == 1 means pooled samples must be flushed. */ + lsmash_root_t *root = trak->root; + isom_chunk_t *current = &trak->cache->chunk; + if( flush == 1 && isom_write_pooled_samples( root, current ) ) + return -1; + /* Arbitration system between tracks with extremely scattering dts. + * Here, we check whether asynchronization between the tracks exceeds the tolerance. + * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed. + * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track. + * Note: you needn't read this loop until you grasp the basic handling of chunks. */ + double tolerance = root->max_async_tolerance; + for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next ) + { + isom_trak_entry_t *other = (isom_trak_entry_t *)entry->data; + if( trak == other ) + continue; + if( !other || !other->cache || !other->mdia || !other->mdia->mdhd || !other->mdia->mdhd->timescale + || !other->mdia->minf || !other->mdia->minf->stbl || !other->mdia->minf->stbl->stsc || !other->mdia->minf->stbl->stsc->list ) + return -1; + isom_chunk_t *chunk = &other->cache->chunk; + if( !chunk->pool || !chunk->pool->entry_count ) + continue; + double diff = ((double)sample->dts / trak->mdia->mdhd->timescale) + - ((double)chunk->first_dts / other->mdia->mdhd->timescale); + if( diff > tolerance && isom_output_cached_chunk( other ) ) + return -1; + /* Note: we don't flush the cached chunk in the current track and the current sample here + * even if the conditional expression of '-diff > tolerance' meets. + * That's useless because appending a sample to another track would be a good equivalent. + * It's even harmful because it causes excess chunk division by calling + * isom_output_cached_chunk() which always generates a new chunk. + * Anyway some excess chunk division will be there, but rather less without it. + * To completely avoid this, we need to observe at least whether the current sample will be placed + * right next to the previous chunk of the same track or not. */ + } + /* anyway the current sample must be pooled. */ + if( lsmash_add_entry( current->pool, sample ) ) + return -1; + current->pool_size += sample->length; + return 0; +} + +static int isom_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) +{ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->root || !trak->cache || !trak->mdia + || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale + || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return -1; + /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */ + if( !root->mdat ) + { + if( isom_new_mdat( root, 0 ) ) + return -1; + /* Add the size of the Media Data Box and the placeholder. */ + root->size += 2 * ISOM_DEFAULT_BOX_HEADER_SIZE; + } + isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index ); + if( !sample_entry ) + return -1; + if( isom_is_lpcm_audio( sample_entry->type ) ) + { + uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; + uint64_t dts = sample->dts; + uint64_t cts = sample->cts; + /* Append samples splitted into each LPCMFrame. */ + for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) + { + lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); + if( !lpcm_sample ) + return -1; + memcpy( lpcm_sample->data, sample->data + offset, frame_size ); + lpcm_sample->dts = dts++; + lpcm_sample->cts = cts++; + lpcm_sample->prop = sample->prop; + lpcm_sample->index = sample->index; + if( isom_append_sample_internal( trak, lpcm_sample ) ) + { + lsmash_delete_sample( lpcm_sample ); + return -1; + } + } + lsmash_delete_sample( sample ); + return 0; + } + return isom_append_sample_internal( trak, sample ); +} + +static int isom_output_cache( isom_trak_entry_t *trak ) +{ + if( trak->cache->chunk.pool && trak->cache->chunk.pool->entry_count + && isom_output_cached_chunk( trak ) ) + return -1; + isom_stbl_t *stbl = trak->mdia->minf->stbl; + if( !stbl->sgpd_list ) + return 0; + for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next ) + { + isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data; + if( !sgpd ) + return -1; + switch( sgpd->grouping_type ) + { + case ISOM_GROUP_TYPE_RAP : + { + isom_rap_group_t *group = trak->cache->rap; + if( !group ) + { + if( trak->root->fragment ) + continue; + else + return -1; + } + if( !group->random_access ) + continue; + group->random_access->num_leading_samples_known = 1; + break; + } + case ISOM_GROUP_TYPE_ROLL : + if( !trak->cache->roll.pool ) + { + if( trak->root->fragment ) + continue; + else + return -1; + } + for( lsmash_entry_t *roll_entry = trak->cache->roll.pool->head; roll_entry; roll_entry = roll_entry->next ) + { + isom_roll_group_t *group = (isom_roll_group_t *)roll_entry->data; + if( !group ) + return -1; + group->described = 1; + } + break; + default : + break; + } + } + return 0; +} + +static int isom_flush_fragment_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_duration ) +{ + isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID ); + if( !traf ) + return 0; /* no samples */ + if( !traf->cache || !traf->cache->fragment ) + return -1; + if( traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data ) + { + /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later. + * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */ + isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data; + if( root->fragment->pool_size ) + trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; + trun->data_offset = root->fragment->pool_size; + } + isom_append_fragment_track_run( root, &traf->cache->chunk ); + return isom_set_fragment_last_duration( traf, last_sample_duration ); +} + +int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ) +{ + if( !root ) + return -1; + if( root->fragment && root->fragment->movie ) + return isom_flush_fragment_pooled_samples( root, track_ID, last_sample_delta ); + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return -1; + if( isom_output_cache( trak ) ) + return -1; + return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta ); +} + +/* This function doesn't update sample_duration of the last sample in the previous movie fragment. + * Instead of this, isom_finish_movie_fragment undertakes this task. */ +static int isom_update_fragment_previous_sample_duration( isom_traf_entry_t *traf, isom_trex_entry_t *trex, uint32_t duration ) +{ + isom_tfhd_t *tfhd = traf->tfhd; + isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data; + int previous_run_has_previous_sample = 0; + if( trun->sample_count == 1 ) + { + if( traf->trun_list->entry_count == 1 ) + return 0; /* The previous track run belongs to the previous movie fragment if it exists. */ + if( !traf->trun_list->tail->prev || !traf->trun_list->tail->prev->data ) + return -1; + /* OK. The previous sample exists in the previous track run in the same track fragment. */ + trun = (isom_trun_entry_t *)traf->trun_list->tail->prev->data; + previous_run_has_previous_sample = 1; + } + /* Update default_sample_duration of the Track Fragment Header Box + * if this duration is what the first sample in the current track fragment owns. */ + if( (trun->sample_count == 2 && traf->trun_list->entry_count == 1) + || (trun->sample_count == 1 && traf->trun_list->entry_count == 2) ) + { + if( duration != trex->default_sample_duration ) + tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT; + tfhd->default_sample_duration = duration; + } + /* Update the previous sample_duration if needed. */ + if( duration != tfhd->default_sample_duration ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT; + if( trun->flags ) + { + uint32_t sample_number = trun->sample_count - !previous_run_has_previous_sample; + isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, sample_number ); + if( !row ) + return -1; + row->sample_duration = duration; + } + traf->cache->fragment->last_duration = duration; + return 0; +} + +static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample ) +{ + isom_sample_flags_t flags; + flags.reserved = 0; + flags.is_leading = sample->prop.leading & 0x3; + flags.sample_depends_on = sample->prop.independent & 0x3; + flags.sample_is_depended_on = sample->prop.disposable & 0x3; + flags.sample_has_redundancy = sample->prop.redundant & 0x3; + flags.sample_padding_value = 0; + flags.sample_is_non_sync_sample = sample->prop.random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC; + flags.sample_degradation_priority = 0; + return flags; +} + +static int isom_update_fragment_sample_tables( isom_traf_entry_t *traf, lsmash_sample_t *sample ) +{ + isom_tfhd_t *tfhd = traf->tfhd; + isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID ); + if( !trex ) + return -1; + lsmash_root_t *root = traf->root; + isom_cache_t *cache = traf->cache; + /* Create a new track run if the duration exceeds max_chunk_duration. + * Old one will be appended to the pool of this movie fragment. */ + int delimit = (root->max_chunk_duration < ((double)(sample->dts - traf->cache->chunk.first_dts) / lsmash_get_media_timescale( root, tfhd->track_ID ))) + || (root->max_chunk_size < (cache->chunk.pool_size + sample->length)); + isom_trun_entry_t *trun = NULL; + if( !traf->trun_list || !traf->trun_list->entry_count || delimit ) + { + if( delimit && traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data ) + { + /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later. + * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */ + trun = (isom_trun_entry_t *)traf->trun_list->tail->data; + if( root->fragment->pool_size ) + trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT; + trun->data_offset = root->fragment->pool_size; + } + trun = isom_add_trun( traf ); + if( !trun ) + return -1; + if( !cache->chunk.pool ) + { + /* Very initial settings, just once per track */ + cache->chunk.pool = lsmash_create_entry_list(); + if( !cache->chunk.pool ) + return -1; + } + } + else + { + if( !traf->trun_list->tail || !traf->trun_list->tail->data ) + return -1; + trun = (isom_trun_entry_t *)traf->trun_list->tail->data; + } + uint32_t sample_composition_time_offset = sample->cts - sample->dts; + isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample ); + if( ++trun->sample_count == 1 ) + { + if( traf->trun_list->entry_count == 1 ) + { + /* This track fragment isn't empty-duration-fragment any more. */ + tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY; + /* Set up sample_description_index in this track fragment. */ + if( sample->index != trex->default_sample_description_index ) + tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT; + tfhd->sample_description_index = cache->chunk.sample_description_index = sample->index; + /* Set up default_sample_size used in this track fragment. */ + tfhd->default_sample_size = sample->length; + /* Set up default_sample_flags used in this track fragment. + * Note: we decide an appropriate default value at the end of this movie fragment. */ + tfhd->default_sample_flags = sample_flags; + /* Set up random access information if this sample is random accessible sample. + * We inform only the first sample in each movie fragment. */ + if( root->bs->stream != stdout && sample->prop.random_access_type ) + { + isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, tfhd->track_ID ); + if( !tfra ) + { + tfra = isom_add_tfra( root->mfra ); + if( !tfra ) + return -1; + tfra->track_ID = tfhd->track_ID; + } + if( !tfra->list ) + { + tfra->list = lsmash_create_entry_list(); + if( !tfra->list ) + return -1; + } + isom_tfra_location_time_entry_t *rap = malloc( sizeof(isom_tfra_location_time_entry_t) ); + if( !rap ) + return -1; + rap->time = sample->cts; /* If this is wrong, blame vague descriptions of 'presentation time' in the spec. */ + rap->moof_offset = root->size; /* We place Movie Fragment Box in the head of each movie fragment. */ + rap->traf_number = cache->fragment->traf_number; + rap->trun_number = traf->trun_list->entry_count; + rap->sample_number = trun->sample_count; + if( lsmash_add_entry( tfra->list, rap ) ) + return -1; + tfra->number_of_entry = tfra->list->entry_count; + int length; + for( length = 1; rap->traf_number >> (length * 8); length++ ); + tfra->length_size_of_traf_num = LSMASH_MAX( length - 1, tfra->length_size_of_traf_num ); + for( length = 1; rap->traf_number >> (length * 8); length++ ); + tfra->length_size_of_trun_num = LSMASH_MAX( length - 1, tfra->length_size_of_trun_num ); + for( length = 1; rap->sample_number >> (length * 8); length++ ); + tfra->length_size_of_sample_num = LSMASH_MAX( length - 1, tfra->length_size_of_sample_num ); + } + } + trun->first_sample_flags = sample_flags; + cache->chunk.first_dts = sample->dts; + } + /* Update the optional rows in the current track run except for sample_duration if needed. */ + if( sample->length != tfhd->default_sample_size ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT; + if( memcmp( &sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT; + if( sample_composition_time_offset ) + trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT; + if( trun->flags ) + { + isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count ); + if( !row ) + return -1; + row->sample_size = sample->length; + row->sample_flags = sample_flags; + row->sample_composition_time_offset = sample_composition_time_offset; + } + /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */ + if( cache->fragment->has_samples ) + { + /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts + * since that's trivial for such semi-permanent presentation. */ + uint64_t prev_dts = cache->timestamp.dts; + if( sample->dts <= prev_dts || sample->dts > prev_dts + UINT32_MAX ) + return -1; + uint32_t sample_duration = sample->dts - prev_dts; + if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) ) + return -1; + } + cache->timestamp.dts = sample->dts; + cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts ); + return delimit; +} + +static int isom_append_fragment_sample_internal_initial( isom_trak_entry_t *trak, lsmash_sample_t *sample ) +{ + int delimit = 0; + /* Update the sample tables of this track fragment. + * If a new chunk was created, append the previous one to the pool of this movie fragment. */ + delimit = isom_update_sample_tables( trak, sample ); + if( delimit < 0 ) + return -1; + else if( delimit == 1 ) + isom_append_fragment_track_run( trak->root, &trak->cache->chunk ); + /* Add a new sample into the pool of this track fragment. */ + if( lsmash_add_entry( trak->cache->chunk.pool, sample ) ) + return -1; + trak->cache->chunk.pool_size += sample->length; + trak->cache->fragment->has_samples = 1; + return 0; +} + +static int isom_append_fragment_sample_internal( isom_traf_entry_t *traf, lsmash_sample_t *sample ) +{ + int delimit = 0; + /* Update the sample tables of this track fragment. + * If a new track run was created, append the previous one to the pool of this movie fragment. */ + delimit = isom_update_fragment_sample_tables( traf, sample ); + if( delimit < 0 ) + return -1; + else if( delimit == 1 ) + isom_append_fragment_track_run( traf->root, &traf->cache->chunk ); + /* Add a new sample into the pool of this track fragment. */ + if( lsmash_add_entry( traf->cache->chunk.pool, sample ) ) + return -1; + traf->cache->chunk.pool_size += sample->length; + traf->cache->fragment->has_samples = 1; + return 0; +} + +static int isom_append_fragment_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) +{ + isom_fragment_manager_t *fragment = root->fragment; + if( !fragment || !fragment->pool ) + return -1; + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || !trak->root || !trak->cache || !trak->cache->fragment || !trak->tkhd || !trak->mdia + || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale + || !trak->mdia->minf || !trak->mdia->minf->stbl + || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list + || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list ) + return -1; + int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL; + void *track_fragment = NULL; + if( !fragment->movie ) + { + append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial; + track_fragment = trak; + } + else + { + isom_traf_entry_t *traf = isom_get_traf( fragment->movie, track_ID ); + if( !traf ) + { + traf = isom_add_traf( root, fragment->movie ); + if( isom_add_tfhd( traf ) ) + return -1; + traf->tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */ + traf->tfhd->track_ID = trak->tkhd->track_ID; + traf->cache = trak->cache; + traf->cache->fragment->traf_number = fragment->movie->traf_list->entry_count; + } + else if( !traf->root || !traf->root->moov || !traf->root->moov->mvex || !traf->cache || !traf->tfhd ) + return -1; + append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal; + track_fragment = traf; + } + isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index ); + if( !sample_entry ) + return -1; + if( isom_is_lpcm_audio( sample_entry->type ) ) + { + uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket; + uint64_t dts = sample->dts; + uint64_t cts = sample->cts; + /* Append samples splitted into each LPCMFrame. */ + for( uint32_t offset = 0; offset < sample->length; offset += frame_size ) + { + lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size ); + if( !lpcm_sample ) + return -1; + memcpy( lpcm_sample->data, sample->data + offset, frame_size ); + lpcm_sample->dts = dts++; + lpcm_sample->cts = cts++; + lpcm_sample->prop = sample->prop; + lpcm_sample->index = sample->index; + if( append_sample_func( track_fragment, lpcm_sample ) ) + { + lsmash_delete_sample( lpcm_sample ); + return -1; + } + } + lsmash_delete_sample( sample ); + return 0; + } + return append_sample_func( track_fragment, sample ); +} + +int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ) +{ + /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented. + * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */ + if( !root || !root->bs || !sample || !sample->data || !track_ID + || root->max_chunk_duration == 0 || root->max_async_tolerance == 0 ) + return -1; + /* Write File Type Box here if it was not written yet. */ + if( !root->file_type_written && isom_write_ftyp( root ) ) + return -1; + if( root->fragment && root->fragment->pool ) + return isom_append_fragment_sample( root, track_ID, sample ); + return isom_append_sample( root, track_ID, sample ); +} + +/*---- misc functions ----*/ + +#define CHAPTER_BUFSIZE 512 /* for chapter handling */ + +static int isom_get_start_time( char *chap_time, isom_chapter_entry_t *data ) +{ + uint64_t hh, mm; + double ss; + if( sscanf( chap_time, "%"SCNu64":%2"SCNu64":%lf", &hh, &mm, &ss ) != 3 ) + return -1; + /* check overflow */ + if( hh >= 5124095 || mm >= 60 || ss >= 60 ) + return -1; + /* 1ns timescale */ + data->start_time = (hh * 3600 + mm * 60 + ss) * 1e9; + return 0; +} + +static int isom_lumber_line( char *buff, int bufsize, FILE *chapter ) +{ + char *tail; + /* remove newline codes and skip empty line */ + do{ + if( fgets( buff, bufsize, chapter ) == NULL ) + return -1; + tail = &buff[ strlen( buff ) - 1 ]; + while( tail >= buff && ( *tail == '\n' || *tail == '\r' ) ) + *tail-- = '\0'; + }while( tail < buff ); + return 0; +} + +static int isom_read_simple_chapter( FILE *chapter, isom_chapter_entry_t *data ) +{ + char buff[CHAPTER_BUFSIZE]; + int len; + + /* get start_time */ + if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) + return -1; + char *chapter_time = strchr( buff, '=' ); /* find separator */ + if( !chapter_time++ + || isom_get_start_time( chapter_time, data ) + || isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) /* get chapter_name */ + return -1; + char *chapter_name = strchr( buff, '=' ); /* find separator */ + if( !chapter_name++ ) + return -1; + len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ + data->chapter_name = ( char* )malloc( len + 1 ); + if( !data->chapter_name ) + return -1; + memcpy( data->chapter_name, chapter_name, len ); + data->chapter_name[len] = '\0'; + return 0; +} + +static int isom_read_minimum_chapter( FILE *chapter, isom_chapter_entry_t *data ) +{ + char buff[CHAPTER_BUFSIZE]; + int len; + + if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) /* read newline */ + || isom_get_start_time( buff, data ) ) /* get start_time */ + return -1; + /* get chapter_name */ + char *chapter_name = strchr( buff, ' ' ); /* find separator */ + if( !chapter_name++ ) + return -1; + len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */ + data->chapter_name = ( char* )malloc( len + 1 ); + if( !data->chapter_name ) + return -1; + memcpy( data->chapter_name, chapter_name, len ); + data->chapter_name[len] = '\0'; + return 0; +} + +typedef int (*fn_get_chapter_data)( FILE *, isom_chapter_entry_t * ); + +static fn_get_chapter_data isom_check_chap_line( char *file_name ) +{ + char buff[CHAPTER_BUFSIZE]; + FILE *fp = fopen( file_name, "rb" ); + if( !fp ) + return NULL; + fn_get_chapter_data fnc = NULL; + if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL ) + { + if( strncmp( buff, "CHAPTER", 7 ) == 0 ) + fnc = isom_read_simple_chapter; + else if( isdigit( buff[0] ) && isdigit( buff[1] ) && buff[2] == ':' + && isdigit( buff[3] ) && isdigit( buff[4] ) && buff[5] == ':' ) + fnc = isom_read_minimum_chapter; + } + fclose( fp ); + return fnc; +} + +int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name ) +{ + /* This function should be called after updating of the latest movie duration. */ + if( !root || !root->moov || !root->moov->mvhd || !root->moov->mvhd->timescale || !root->moov->mvhd->duration ) + return -1; + /* check each line format */ + fn_get_chapter_data fnc = isom_check_chap_line( file_name ); + if( !fnc ) + return -1; + FILE *chapter = fopen( file_name, "rb" ); + if( !chapter ) + return -1; + if( isom_add_udta( root, 0 ) || isom_add_chpl( root->moov ) ) + goto fail; + isom_chapter_entry_t data; + while( !fnc( chapter, &data ) ) + { + data.start_time = (data.start_time + 50) / 100; /* convert to 100ns unit */ + if( data.start_time / 1e7 > (double)root->moov->mvhd->duration / root->moov->mvhd->timescale + || isom_add_chpl_entry( root->moov->udta->chpl, &data ) ) + goto fail; + free( data.chapter_name ); + data.chapter_name = NULL; + } + fclose( chapter ); + return 0; +fail: + if( data.chapter_name ) + free( data.chapter_name ); + fclose( chapter ); + return -1; +} + +int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name ) +{ + if( !root || (!root->qt_compatible && !root->itunes_audio) || !root->moov || !root->moov->mvhd ) + return -1; + FILE *chapter = NULL; /* shut up 'uninitialized' warning */ + /* Create a Track Reference Box. */ + isom_trak_entry_t *trak = isom_get_trak( root, track_ID ); + if( !trak || isom_add_tref( trak ) ) + return -1; + /* Create a track_ID for a new chapter track. */ + uint32_t *id = (uint32_t *)malloc( sizeof(uint32_t) ); + if( !id ) + return -1; + uint32_t chapter_track_ID = *id = root->moov->mvhd->next_track_ID; + /* Create a Track Reference Type Box. */ + isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP, 1, id ); + if( !chap ) + return -1; /* no need to free id */ + /* Create a reference chapter track. */ + if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) ) + return -1; + /* Set track parameters. */ + lsmash_track_parameters_t track_param; + lsmash_initialize_track_parameters( &track_param ); + track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW; + if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) ) + goto fail; + /* Set media parameters. */ + uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID ); + if( !media_timescale ) + goto fail; + lsmash_media_parameters_t media_param; + lsmash_initialize_media_parameters( &media_param ); + media_param.timescale = media_timescale; + media_param.ISO_language = root->max_3gpp_version >= 6 || root->itunes_audio ? "und" : NULL; + media_param.MAC_language = 0; + if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) ) + goto fail; + /* Create a sample description. */ + uint32_t sample_type = root->max_3gpp_version >= 6 || root->itunes_audio ? ISOM_CODEC_TYPE_TX3G_TEXT : QT_CODEC_TYPE_TEXT_TEXT; + uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, sample_type, NULL ); + if( !sample_entry ) + goto fail; + /* Check each line format. */ + fn_get_chapter_data fnc = isom_check_chap_line( file_name ); + if( !fnc ) + 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. */ + static const uint8_t encd[12] = + { + 0x00, 0x00, 0x00, 0x0C, /* size: 12 */ + 0x65, 0x6E, 0x63, 0x64, /* type: 'encd' */ + 0x00, 0x00, 0x01, 0x00 /* Unicode Encoding */ + }; + memcpy( sample->data + 2 + name_length, encd, 12 ); + } + sample->dts = sample->cts = data.start_time; + sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC; + sample->index = sample_entry; + if( lsmash_append_sample( root, chapter_track_ID, sample ) ) + 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..b3c5cd0 --- /dev/null +++ b/output/mp4/lsmash.h @@ -0,0 +1,1409 @@ +/***************************************************************************** + * 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 + +#include + +#define PRIVATE /* If this declaration is placed at a variable, any user shouldn't use it. */ + +#define LSMASH_4CC( a, b, c, d ) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)) + + +/* public constants */ +typedef enum +{ + LSMASH_FILE_MODE_WRITE = 1, + LSMASH_FILE_MODE_READ = 1<<1, + LSMASH_FILE_MODE_DUMP = 1<<2, + LSMASH_FILE_MODE_FRAGMENTED = 1<<16, + LSMASH_FILE_MODE_WRITE_FRAGMENTED = LSMASH_FILE_MODE_WRITE | LSMASH_FILE_MODE_FRAGMENTED, + LSMASH_FILE_MODE_READ_FRAGMENTED = LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_FRAGMENTED, +} lsmash_file_mode_code; + +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_ALAC = LSMASH_4CC( 'a', 'l', 'a', 'c' ), + ISOM_BOX_TYPE_DAC3 = LSMASH_4CC( 'd', 'a', 'c', '3' ), + ISOM_BOX_TYPE_DAMR = LSMASH_4CC( 'd', 'a', 'm', 'r' ), + ISOM_BOX_TYPE_DEC3 = LSMASH_4CC( 'd', 'e', 'c', '3' ), + + ISOM_BOX_TYPE_FTAB = LSMASH_4CC( 'f', 't', 'a', 'b' ), + + QT_BOX_TYPE_ALAC = LSMASH_4CC( 'a', 'l', 'a', 'c' ), + QT_BOX_TYPE_CHAN = LSMASH_4CC( 'c', 'h', 'a', 'n' ), + QT_BOX_TYPE_CLEF = LSMASH_4CC( 'c', 'l', 'e', 'f' ), + QT_BOX_TYPE_CLIP = LSMASH_4CC( 'c', 'l', 'i', 'p' ), + QT_BOX_TYPE_COLR = LSMASH_4CC( 'c', 'o', 'l', 'r' ), + QT_BOX_TYPE_CRGN = LSMASH_4CC( 'c', 'r', 'g', 'n' ), + QT_BOX_TYPE_CTAB = LSMASH_4CC( 'c', 't', 'a', 'b' ), + QT_BOX_TYPE_ENDA = LSMASH_4CC( 'e', 'n', 'd', 'a' ), + QT_BOX_TYPE_ENOF = LSMASH_4CC( 'e', 'n', 'o', 'f' ), + QT_BOX_TYPE_FRMA = LSMASH_4CC( 'f', 'r', 'm', 'a' ), + QT_BOX_TYPE_GMHD = LSMASH_4CC( 'g', 'm', 'h', 'd' ), + QT_BOX_TYPE_GMIN = LSMASH_4CC( 'g', 'm', 'i', 'n' ), + QT_BOX_TYPE_IMAP = LSMASH_4CC( 'i', 'm', 'a', 'p' ), + QT_BOX_TYPE_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_ISO5 = LSMASH_4CC( 'i', 's', 'o', '5' ), + ISOM_BRAND_TYPE_ISO6 = LSMASH_4CC( 'i', 's', 'o', '6' ), + ISOM_BRAND_TYPE_ISOM = LSMASH_4CC( 'i', 's', 'o', 'm' ), + ISOM_BRAND_TYPE_JPSI = LSMASH_4CC( 'j', 'p', 's', 'i' ), + ISOM_BRAND_TYPE_MJ2S = LSMASH_4CC( 'm', 'j', '2', 'j' ), + ISOM_BRAND_TYPE_MJP2 = LSMASH_4CC( 'm', 'j', 'p', '2' ), + ISOM_BRAND_TYPE_MP21 = LSMASH_4CC( 'm', 'p', '2', '1' ), + ISOM_BRAND_TYPE_MP41 = LSMASH_4CC( 'm', 'p', '4', '1' ), + ISOM_BRAND_TYPE_MP42 = LSMASH_4CC( 'm', 'p', '4', '2' ), + ISOM_BRAND_TYPE_MP71 = LSMASH_4CC( 'm', 'p', '7', '1' ), + ISOM_BRAND_TYPE_NIKO = LSMASH_4CC( 'n', 'i', 'k', 'o' ), + ISOM_BRAND_TYPE_ODCF = LSMASH_4CC( 'o', 'd', 'c', 'f' ), + ISOM_BRAND_TYPE_OPF2 = LSMASH_4CC( 'o', 'p', 'f', '2' ), + ISOM_BRAND_TYPE_OPX2 = LSMASH_4CC( 'o', 'p', 'x', '2' ), + ISOM_BRAND_TYPE_PANA = LSMASH_4CC( 'p', 'a', 'n', 'a' ), + ISOM_BRAND_TYPE_QT = LSMASH_4CC( 'q', 't', ' ', ' ' ), + ISOM_BRAND_TYPE_SDV = LSMASH_4CC( 's', 'd', 'v', ' ' ), +} lsmash_brand_type_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_RAP = LSMASH_4CC( 'r', 'a', 'p', ' ' ), /* Random Access Point / This grouping type hasn't been published yet. */ + ISOM_GROUP_TYPE_RASH = LSMASH_4CC( 'r', 'a', 's', 'h' ), /* Rate Share */ + ISOM_GROUP_TYPE_ROLL = LSMASH_4CC( 'r', 'o', 'l', 'l' ), /* Random Access Recovery Point */ + ISOM_GROUP_TYPE_SCIF = LSMASH_4CC( 's', 'c', 'i', 'f' ), /* SVC Scalability Information */ + ISOM_GROUP_TYPE_SCNM = LSMASH_4CC( 's', 'c', 'n', 'm' ), /* AVC/SVC/MVC map groups */ + ISOM_GROUP_TYPE_VIPR = LSMASH_4CC( 'v', 'i', 'p', 'r' ), /* View priority */ +} 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_YIDDISH = ISOM_LANG_T( 'y', 'i', 'd' ), + ISOM_LANGUAGE_CODE_SERBIAN = ISOM_LANG_T( 's', 'r', 'p' ), + ISOM_LANGUAGE_CODE_MACEDONIAN = ISOM_LANG_T( 'm', 'k', 'd' ), + ISOM_LANGUAGE_CODE_BULGARIAN = ISOM_LANG_T( 'b', 'u', 'l' ), + ISOM_LANGUAGE_CODE_UKRAINIAN = ISOM_LANG_T( 'u', 'k', 'r' ), + ISOM_LANGUAGE_CODE_BELARUSIAN = ISOM_LANG_T( 'b', 'e', 'l' ), + ISOM_LANGUAGE_CODE_UZBEK = ISOM_LANG_T( 'u', 'z', 'b' ), + ISOM_LANGUAGE_CODE_KAZAKH = ISOM_LANG_T( 'k', 'a', 'z' ), + ISOM_LANGUAGE_CODE_AZERBAIJANI = ISOM_LANG_T( 'a', 'z', 'e' ), + ISOM_LANGUAGE_CODE_ARMENIAN = ISOM_LANG_T( 'h', 'y', 'e' ), + ISOM_LANGUAGE_CODE_GEORGIAN = ISOM_LANG_T( 'k', 'a', 't' ), + ISOM_LANGUAGE_CODE_MOLDAVIAN = ISOM_LANG_T( 'r', 'o', 'n' ), + ISOM_LANGUAGE_CODE_KIRGHIZ = ISOM_LANG_T( 'k', 'i', 'r' ), + ISOM_LANGUAGE_CODE_TAJIK = ISOM_LANG_T( 't', 'g', 'k' ), + ISOM_LANGUAGE_CODE_TURKMEN = ISOM_LANG_T( 't', 'u', 'k' ), + ISOM_LANGUAGE_CODE_MONGOLIAN = ISOM_LANG_T( 'm', 'o', 'n' ), + ISOM_LANGUAGE_CODE_PASHTO = ISOM_LANG_T( 'p', 'u', 's' ), + ISOM_LANGUAGE_CODE_KURDISH = ISOM_LANG_T( 'k', 'u', 'r' ), + ISOM_LANGUAGE_CODE_KASHMIRI = ISOM_LANG_T( 'k', 'a', 's' ), + ISOM_LANGUAGE_CODE_SINDHI = ISOM_LANG_T( 's', 'n', 'd' ), + ISOM_LANGUAGE_CODE_TIBETAN = ISOM_LANG_T( 'b', 'o', 'd' ), + ISOM_LANGUAGE_CODE_NEPALI = ISOM_LANG_T( 'n', 'e', 'p' ), + ISOM_LANGUAGE_CODE_SANSKRIT = ISOM_LANG_T( 's', 'a', 'n' ), + ISOM_LANGUAGE_CODE_MARATHI = ISOM_LANG_T( 'm', 'a', 'r' ), + ISOM_LANGUAGE_CODE_BENGALI = ISOM_LANG_T( 'b', 'e', 'n' ), + ISOM_LANGUAGE_CODE_ASSAMESE = ISOM_LANG_T( 'a', 's', 'm' ), + ISOM_LANGUAGE_CODE_GUJARATI = ISOM_LANG_T( 'g', 'u', 'j' ), + ISOM_LANGUAGE_CODE_PUNJABI = ISOM_LANG_T( 'p', 'a', 'n' ), + ISOM_LANGUAGE_CODE_ORIYA = ISOM_LANG_T( 'o', 'r', 'i' ), + ISOM_LANGUAGE_CODE_MALAYALAM = ISOM_LANG_T( 'm', 'a', 'l' ), + ISOM_LANGUAGE_CODE_KANNADA = ISOM_LANG_T( 'k', 'a', 'n' ), + ISOM_LANGUAGE_CODE_TAMIL = ISOM_LANG_T( 't', 'a', 'm' ), + ISOM_LANGUAGE_CODE_TELUGU = ISOM_LANG_T( 't', 'e', 'l' ), + ISOM_LANGUAGE_CODE_SINHALESE = ISOM_LANG_T( 's', 'i', 'n' ), + ISOM_LANGUAGE_CODE_BURMESE = ISOM_LANG_T( 'm', 'y', 'a' ), + ISOM_LANGUAGE_CODE_KHMER = ISOM_LANG_T( 'k', 'h', 'm' ), + ISOM_LANGUAGE_CODE_LAO = ISOM_LANG_T( 'l', 'a', 'o' ), + ISOM_LANGUAGE_CODE_VIETNAMESE = ISOM_LANG_T( 'v', 'i', 'e' ), + ISOM_LANGUAGE_CODE_INDONESIAN = ISOM_LANG_T( 'i', 'n', 'd' ), + ISOM_LANGUAGE_CODE_TAGALOG = ISOM_LANG_T( 't', 'g', 'l' ), + ISOM_LANGUAGE_CODE_MALAY_ROMAN = ISOM_LANG_T( 'm', 's', 'a' ), + ISOM_LANGUAGE_CODE_MAYAY_ARABIC = ISOM_LANG_T( 'm', 's', 'a' ), + ISOM_LANGUAGE_CODE_AMHARIC = ISOM_LANG_T( 'a', 'm', 'h' ), + ISOM_LANGUAGE_CODE_OROMO = ISOM_LANG_T( 'o', 'r', 'm' ), + ISOM_LANGUAGE_CODE_SOMALI = ISOM_LANG_T( 's', 'o', 'm' ), + ISOM_LANGUAGE_CODE_SWAHILI = ISOM_LANG_T( 's', 'w', 'a' ), + ISOM_LANGUAGE_CODE_KINYARWANDA = ISOM_LANG_T( 'k', 'i', 'n' ), + ISOM_LANGUAGE_CODE_RUNDI = ISOM_LANG_T( 'r', 'u', 'n' ), + ISOM_LANGUAGE_CODE_CHEWA = ISOM_LANG_T( 'n', 'y', 'a' ), + ISOM_LANGUAGE_CODE_MALAGASY = ISOM_LANG_T( 'm', 'l', 'g' ), + ISOM_LANGUAGE_CODE_ESPERANTO = ISOM_LANG_T( 'e', 'p', 'o' ), + ISOM_LANGUAGE_CODE_WELSH = ISOM_LANG_T( 'c', 'y', 'm' ), + ISOM_LANGUAGE_CODE_BASQUE = ISOM_LANG_T( 'e', 'u', 's' ), + ISOM_LANGUAGE_CODE_CATALAN = ISOM_LANG_T( 'c', 'a', 't' ), + ISOM_LANGUAGE_CODE_LATIN = ISOM_LANG_T( 'l', 'a', 't' ), + ISOM_LANGUAGE_CODE_QUECHUA = ISOM_LANG_T( 'q', 'u', 'e' ), + ISOM_LANGUAGE_CODE_GUARANI = ISOM_LANG_T( 'g', 'r', 'n' ), + ISOM_LANGUAGE_CODE_AYMARA = ISOM_LANG_T( 'a', 'y', 'm' ), + ISOM_LANGUAGE_CODE_TATAR = ISOM_LANG_T( 'c', 'r', 'h' ), + ISOM_LANGUAGE_CODE_UIGHUR = ISOM_LANG_T( 'u', 'i', 'g' ), + ISOM_LANGUAGE_CODE_DZONGKHA = ISOM_LANG_T( 'd', 'z', 'o' ), + ISOM_LANGUAGE_CODE_JAVANESE = ISOM_LANG_T( 'j', 'a', '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 +{ + /* In MP4 and/or ISO base media file format, if in a presentation all tracks have neither track_in_movie nor track_in_preview set, + * then all tracks shall be treated as if both flags were set on all tracks. */ + ISOM_TRACK_ENABLED = 0x000001, /* Track_enabled: Indicates that the track is enabled. + * A disabled track is treated as if it were not present. */ + ISOM_TRACK_IN_MOVIE = 0x000002, /* Track_in_movie: Indicates that the track is used in the presentation. */ + ISOM_TRACK_IN_PREVIEW = 0x000004, /* Track_in_preview: Indicates that the track is used when previewing the presentation. */ + + QT_TRACK_IN_POSTER = 0x000008, /* Track_in_poster: Indicates that the track is used in the movie's poster. (only defined in QuickTime file format) */ +} lsmash_track_mode_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_UNKNOWN = 0, + ISOM_SAMPLE_IS_UNDECODABLE_LEADING = 1, + ISOM_SAMPLE_IS_NOT_LEADING = 2, + ISOM_SAMPLE_IS_DECODABLE_LEADING = 3, + /* independent */ + ISOM_SAMPLE_INDEPENDENCY_UNKNOWN = 0, + ISOM_SAMPLE_IS_NOT_INDEPENDENT = 1, + ISOM_SAMPLE_IS_INDEPENDENT = 2, + /* disposable */ + ISOM_SAMPLE_DISPOSABLE_UNKNOWN = 0, + ISOM_SAMPLE_IS_NOT_DISPOSABLE = 1, + ISOM_SAMPLE_IS_DISPOSABLE = 2, + /* redundant */ + ISOM_SAMPLE_REDUNDANCY_UNKNOWN = 0, + ISOM_SAMPLE_HAS_REDUNDANCY = 1, + ISOM_SAMPLE_HAS_NO_REDUNDANCY = 2, +} lsmash_sample_property_code; + +/* objectTypeIndication */ +typedef enum { + MP4SYS_OBJECT_TYPE_Forbidden = 0x00, /* Forbidden */ + MP4SYS_OBJECT_TYPE_Systems_ISO_14496_1 = 0x01, /* Systems ISO/IEC 14496-1 */ + /* For all 14496-1 streams unless specifically indicated to the contrary. + Scene Description scenes, which are identified with StreamType=0x03, using + this object type value shall use the BIFSConfig. */ + MP4SYS_OBJECT_TYPE_Systems_ISO_14496_1_BIFSv2 = 0x02, /* Systems ISO/IEC 14496-1 */ + /* This object type shall be used, with StreamType=0x03, for Scene + Description streams that use the BIFSv2Config. + Its use with other StreamTypes is reserved. */ + + MP4SYS_OBJECT_TYPE_Interaction_Stream = 0x03, /* Interaction Stream */ + MP4SYS_OBJECT_TYPE_Extended_BIFS = 0x04, /* Extended BIFS */ + /* Used, with StreamType=0x03, for Scene Description streams that use the BIFSConfigEx; its use with + other StreamTypes is reserved. (Was previously reserved for MUCommandStream but not used for that purpose.) */ + MP4SYS_OBJECT_TYPE_AFX_Stream = 0x05, /* AFX Stream */ + /* Used, with StreamType=0x03, for Scene Description streams that use the AFXConfig; + its use with other StreamTypes is reserved. */ + MP4SYS_OBJECT_TYPE_Font_Data_Stream = 0x06, /* Font Data Stream */ + MP4SYS_OBJECT_TYPE_Synthetised_Texture = 0x07, /* Synthetised Texture */ + MP4SYS_OBJECT_TYPE_Text_Stream = 0x08, /* Text Stream */ + + MP4SYS_OBJECT_TYPE_Visual_ISO_14496_2 = 0x20, /* Visual ISO/IEC 14496-2 */ + MP4SYS_OBJECT_TYPE_Visual_H264_ISO_14496_10 = 0x21, /* Visual ITU-T Recommendation H.264 | ISO/IEC 14496-10 */ + /* The actual object types are within the DecoderSpecificInfo and defined in H.264 | 14496-10. */ + MP4SYS_OBJECT_TYPE_Parameter_Sets_H_264_ISO_14496_10 = 0x22, /* Parameter Sets for ITU-T Recommendation H.264 | ISO/IEC 14496-10 */ + /* The actual object types are within the DecoderSpecificInfo and defined in 14496-2. */ + + MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 = 0x40, /* Audio ISO/IEC 14496-3 (MPEG-4 Audio) */ + //MP4SYS_OBJECT_TYPE_MP4A_AUDIO = 0x40, + /* The actual object types are defined in 14496-3 and are in the DecoderSpecificInfo as specified in 14496-3. */ + + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Simple_Profile = 0x60, /* Visual ISO/IEC 13818-2 Simple Profile (MPEG-2 Video) */ + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Main_Profile = 0x61, /* Visual ISO/IEC 13818-2 Main Profile */ + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_SNR_Profile = 0x62, /* Visual ISO/IEC 13818-2 SNR Profile */ + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_Spatial_Profile = 0x63, /* Visual ISO/IEC 13818-2 Spatial Profile */ + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_High_Profile = 0x64, /* Visual ISO/IEC 13818-2 High Profile */ + MP4SYS_OBJECT_TYPE_Visual_ISO_13818_2_422_Profile = 0x65, /* Visual ISO/IEC 13818-2 422 Profile */ + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile = 0x66, /* Audio ISO/IEC 13818-7 Main Profile (MPEG-2 Audio)(AAC) */ + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile = 0x67, /* Audio ISO/IEC 13818-7 LowComplexity Profile */ + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile = 0x68, /* Audio ISO/IEC 13818-7 Scaleable Sampling Rate Profile */ + /* For streams kinda 13818-7 the decoder specific information consists of the ADIF header if present + (or none if not present) and an access unit is a "raw_data_block()" as defined in 13818-7. */ + MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3 = 0x69, /* Audio ISO/IEC 13818-3 (MPEG-2 BC-Audio)(redefined MPEG-1 Audio in MPEG-2) */ + /* For streams kinda 13818-3 the decoder specific information is empty since all necessary data is in the bitstream frames itself. + The access units in this case are the "frame()" bitstream element as is defined in 11172-3. */ + MP4SYS_OBJECT_TYPE_Visual_ISO_11172_2 = 0x6A, /* Visual ISO/IEC 11172-2 (MPEG-1 Video) */ + MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3 = 0x6B, /* Audio ISO/IEC 11172-3 (MPEG-1 Audio) */ + MP4SYS_OBJECT_TYPE_Visual_ISO_10918_1 = 0x6C, /* Visual ISO/IEC 10918-1 (JPEG) */ + MP4SYS_OBJECT_TYPE_PNG = 0x6D, /* Portable Network Graphics */ + MP4SYS_OBJECT_TYPE_Visual_ISO_15444_1_JPEG2000 = 0x6E, /* Visual ISO/IEC 15444-1 (JPEG 2000) */ + + /* FIXME: rename these symbols to be explaining, rather than based on four cc */ + MP4SYS_OBJECT_TYPE_EVRC_AUDIO = 0xA0, /* EVRC Voice */ + MP4SYS_OBJECT_TYPE_SSMV_AUDIO = 0xA1, /* SMV Voice */ + MP4SYS_OBJECT_TYPE_3GPP2_CMF = 0xA2, /* 3GPP2 Compact Multimedia Format (CMF) */ + MP4SYS_OBJECT_TYPE_VC_1_VIDEO = 0xA3, /* SMPTE VC-1 Video */ + MP4SYS_OBJECT_TYPE_DRAC_VIDEO = 0xA4, /* Dirac Video Coder */ + MP4SYS_OBJECT_TYPE_AC_3_AUDIO = 0xA5, /* AC-3 Audio */ + MP4SYS_OBJECT_TYPE_EC_3_AUDIO = 0xA6, /* Enhanced AC-3 audio */ + MP4SYS_OBJECT_TYPE_DRA1_AUDIO = 0xA7, /* DRA Audio */ + MP4SYS_OBJECT_TYPE_G719_AUDIO = 0xA8, /* ITU G.719 Audio */ + MP4SYS_OBJECT_TYPE_DTSC_AUDIO = 0xA9, /* DTS Coherent Acoustics audio */ + MP4SYS_OBJECT_TYPE_DTSH_AUDIO = 0xAA, /* DTS-HD High Resolution Audio */ + MP4SYS_OBJECT_TYPE_DTSL_AUDIO = 0xAB, /* DTS-HD Master Audio */ + MP4SYS_OBJECT_TYPE_DTSE_AUDIO = 0xAC, /* DTS Express low bit rate audio, also known as DTS LBR */ + + MP4SYS_OBJECT_TYPE_SQCP_AUDIO = 0xE1, /* 13K Voice */ + + MP4SYS_OBJECT_TYPE_NONE = 0xFF, /* no object type specified */ + /* Streams with this value with a StreamType indicating a systems stream (values 1,2,3,6,7,8,9) + shall be treated as if the ObjectTypeIndication had been set to 0x01. */ +} lsmash_mp4sys_object_type_indication; + +/* streamType */ +typedef enum { + MP4SYS_STREAM_TYPE_Forbidden = 0x00, /* Forbidden */ + MP4SYS_STREAM_TYPE_ObjectDescriptorStream = 0x01, /* ObjectDescriptorStream */ + MP4SYS_STREAM_TYPE_ClockReferenceStream = 0x02, /* ClockReferenceStream */ + MP4SYS_STREAM_TYPE_SceneDescriptionStream = 0x03, /* SceneDescriptionStream */ + MP4SYS_STREAM_TYPE_VisualStream = 0x04, /* VisualStream */ + MP4SYS_STREAM_TYPE_AudioStream = 0x05, /* AudioStream */ + MP4SYS_STREAM_TYPE_MPEG7Stream = 0x06, /* MPEG7Stream */ + MP4SYS_STREAM_TYPE_IPMPStream = 0x07, /* IPMPStream */ + MP4SYS_STREAM_TYPE_ObjectContentInfoStream = 0x08, /* ObjectContentInfoStream */ + MP4SYS_STREAM_TYPE_MPEGJStream = 0x09, /* MPEGJStream */ + MP4SYS_STREAM_TYPE_InteractionStream = 0x0A, /* Interaction Stream */ + MP4SYS_STREAM_TYPE_IPMPToolStream = 0x0B, /* IPMPToolStream */ + MP4SYS_STREAM_TYPE_FontDataStream = 0x0C, /* FontDataStream */ + MP4SYS_STREAM_TYPE_StreamingText = 0x0D, /* StreamingText */ +} lsmash_mp4sys_stream_type; + +/* Audio Object Types */ +typedef enum { + MP4A_AUDIO_OBJECT_TYPE_NULL = 0, + MP4A_AUDIO_OBJECT_TYPE_AAC_MAIN = 1, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_AAC_LC = 2, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_AAC_SSR = 3, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_AAC_LTP = 4, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_SBR = 5, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_AAC_scalable = 6, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_TwinVQ = 7, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_CELP = 8, /* ISO/IEC 14496-3 subpart 3 */ + MP4A_AUDIO_OBJECT_TYPE_HVXC = 9, /* ISO/IEC 14496-3 subpart 2 */ + MP4A_AUDIO_OBJECT_TYPE_TTSI = 12, /* ISO/IEC 14496-3 subpart 6 */ + MP4A_AUDIO_OBJECT_TYPE_Main_synthetic = 13, /* ISO/IEC 14496-3 subpart 5 */ + MP4A_AUDIO_OBJECT_TYPE_Wavetable_synthesis = 14, /* ISO/IEC 14496-3 subpart 5 */ + MP4A_AUDIO_OBJECT_TYPE_General_MIDI = 15, /* ISO/IEC 14496-3 subpart 5 */ + MP4A_AUDIO_OBJECT_TYPE_Algorithmic_Synthesis_Audio_FX = 16, /* ISO/IEC 14496-3 subpart 5 */ + MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC = 17, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP = 19, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable = 20, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_Twin_VQ = 21, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_BSAC = 22, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD = 23, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_ER_CELP = 24, /* ISO/IEC 14496-3 subpart 3 */ + MP4A_AUDIO_OBJECT_TYPE_ER_HVXC = 25, /* ISO/IEC 14496-3 subpart 2 */ + MP4A_AUDIO_OBJECT_TYPE_ER_HILN = 26, /* ISO/IEC 14496-3 subpart 7 */ + MP4A_AUDIO_OBJECT_TYPE_ER_Parametric = 27, /* ISO/IEC 14496-3 subpart 2 and 7 */ + MP4A_AUDIO_OBJECT_TYPE_SSC = 28, /* ISO/IEC 14496-3 subpart 8 */ + MP4A_AUDIO_OBJECT_TYPE_PS = 29, /* ISO/IEC 14496-3 subpart 8 */ + MP4A_AUDIO_OBJECT_TYPE_MPEG_Surround = 30, /* ISO/IEC 23003-1 */ + MP4A_AUDIO_OBJECT_TYPE_ESCAPE = 31, + MP4A_AUDIO_OBJECT_TYPE_Layer_1 = 32, /* ISO/IEC 14496-3 subpart 9 */ + MP4A_AUDIO_OBJECT_TYPE_Layer_2 = 33, /* ISO/IEC 14496-3 subpart 9 */ + MP4A_AUDIO_OBJECT_TYPE_Layer_3 = 34, /* ISO/IEC 14496-3 subpart 9 */ + MP4A_AUDIO_OBJECT_TYPE_DST = 35, /* ISO/IEC 14496-3 subpart 10 */ + MP4A_AUDIO_OBJECT_TYPE_ALS = 36, /* ISO/IEC 14496-3 subpart 11 */ + MP4A_AUDIO_OBJECT_TYPE_SLS = 37, /* ISO/IEC 14496-3 subpart 12 */ + MP4A_AUDIO_OBJECT_TYPE_SLS_non_core = 38, /* ISO/IEC 14496-3 subpart 12 */ + MP4A_AUDIO_OBJECT_TYPE_ER_AAC_ELD = 39, /* ISO/IEC 14496-3 subpart 4 */ + MP4A_AUDIO_OBJECT_TYPE_SMR_Simple = 40, /* ISO/IEC 14496-23 */ + MP4A_AUDIO_OBJECT_TYPE_SMR_Main = 41, /* ISO/IEC 14496-23 */ + MP4A_AUDIO_OBJECT_TYPE_SAOC = 43, /* ISO/IEC 23003-2 */ +} lsmash_mp4a_AudioObjectType; + +/* see ISO/IEC 14496-3 Signaling of SBR, SBR Signaling and Corresponding Decoder Behavior */ +typedef enum { + MP4A_AAC_SBR_NOT_SPECIFIED = 0x0, /* not mention to SBR presence. Implicit signaling. */ + MP4A_AAC_SBR_NONE, /* explicitly signals SBR does not present. Useless in general. */ + MP4A_AAC_SBR_BACKWARD_COMPATIBLE, /* explicitly signals SBR present. Recommended method to signal SBR. */ + MP4A_AAC_SBR_HIERARCHICAL /* SBR exists. SBR dedicated method. */ +} lsmash_mp4a_aac_sbr_mode; + +typedef enum +{ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_NONE = 0, /* not random access point */ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC = 1, /* sync sample */ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP = 1, /* the first sample of a closed GOP */ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP = 2, /* the first sample of an open GOP */ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP = 3, /* the first sample of an open or closed GOP */ + ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY = 4, /* starting point of gradual decoder refresh */ + + QT_SAMPLE_RANDOM_ACCESS_TYPE_NONE = 0, /* not random access point */ + QT_SAMPLE_RANDOM_ACCESS_TYPE_SYNC = 1, /* sync sample */ + QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC = 2, /* partial sync sample */ + QT_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP = 1, /* the first sample of a closed GOP */ + QT_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP = 2, /* the first sample of an open GOP */ +} lsmash_random_access_type; + + +/* public data types */ +typedef struct +{ + uint32_t complete; /* recovery point: the identifier necessary for the recovery from its starting point to be completed */ + uint32_t identifier; /* the identifier for samples + * If this identifier equals a certain identifier of recovery point, + * then this sample is the recovery point of the earliest group in the pool. */ +} lsmash_recovery_t; + +typedef struct +{ + uint8_t allow_earlier; + uint8_t leading; + uint8_t independent; + uint8_t disposable; + uint8_t redundant; + lsmash_random_access_type random_access_type; + lsmash_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 */ +/* 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. */ + lsmash_channel_layout_tag_code layout_tag; /* channel layout */ + lsmash_channel_bitmap_code bitmap; /* Only available when layout_tag is set to QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP. */ + /* LPCM descriptions */ + uint8_t sample_format; /* 0: integer, 1: floating point */ + uint8_t endianness; /* 0: big endian, 1: little endian */ + uint8_t signedness; /* 0: unsigned, 1: signed / This is only valid when sample format is integer. */ + uint8_t packed; /* 0: unpacked, 1: packed i.e. the sample bits occupy the entire available bits for the channel */ + uint8_t alignment; /* 0: low bit placement, 1: high bit placement / This is only valid when unpacked. */ + uint8_t interleaved; /* 0: non-interleaved, 1: interleaved i.e. the samples for each channels are stored in one stream */ +} lsmash_audio_summary_t; + +typedef struct +{ + LSMASH_BASE_SUMMARY + // mp4sys_visualProfileLevelIndication pli ; /* I wonder we should have this or not. */ + // lsmash_mp4v_VideoObjectType vot; /* Detailed codec type. If not mp4v, just ignored. */ + uint32_t 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; /* If not set, video samples are scaled into the visual presentation region to fill it. */ + lsmash_color_parameter primaries; + lsmash_color_parameter transfer; + lsmash_color_parameter matrix; +} lsmash_video_summary_t; + +typedef struct +{ + uint32_t timescale; /* media timescale: timescale for this media */ + uint32_t handler_type; /* the nature of the media + * You can't change handler_type through this parameter manually. */ + uint64_t duration; /* the duration of this media, expressed in the media timescale + * You can't set this parameter manually. */ + uint8_t roll_grouping; /* roll recovery grouping present + * Require 'avc1' brand or ISO Base Media File Format version 2 or later. */ + uint8_t rap_grouping; /* random access point grouping present + * Require ISO Base Media File Format version 6 or later. */ + /* Use either type of language code. */ + uint16_t MAC_language; /* Macintosh language code for this media */ + char *ISO_language; /* ISO 639-2/T language code for this media */ + /* human-readable name for the track type (for debugging and inspection purposes) */ + char *media_handler_name; + char *data_handler_name; + /* Any user shouldn't use the following parameters. */ + PRIVATE char language_shadow[4]; + PRIVATE char media_handler_name_shadow[256]; + PRIVATE char data_handler_name_shadow[256]; +} lsmash_media_parameters_t; + +typedef struct +{ + lsmash_track_mode_code mode; + uint32_t track_ID; /* an integer that uniquely identifies the track + * Don't set to value already used except for zero value. + * Zero value don't override established track_ID. */ + uint64_t duration; /* the duration of this track expressed in the movie timescale units + * If there is any edit, your setting is ignored. */ + int16_t video_layer; /* the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer. */ + int16_t alternate_group; /* an integer that specifies a group or collection of tracks + * If this field is not 0, it should be the same for tracks that contain alternate data for one another + * and different for tracks belonging to different such groups. + * Only one track within an alternate group should be played or streamed at any one time. */ + int16_t audio_volume; /* fixed point 8.8 number. 0x0100 is full volume. */ + int32_t matrix[9]; /* transformation matrix for the video + * Each value represents, in order, a, b, u, c, d, v, x, y and w. + * All the values in a matrix are stored as 16.16 fixed-point values, + * except for u, v and w, which are stored as 2.30 fixed-point values. + * Not all derived specifications use matrices. + * If a matrix is used, the point (p, q) is transformed into (p', q') using the matrix as follows: + * | a b u | + * (p, q, 1) * | c d v | = z * (p', q', 1) + * | x y w | + * p' = (a * p + c * q + x) / z; q' = (b * p + d * q + y) / z; z = u * p + v * q + w + * Note: transformation matrix is applied after scaling to display size up to display_width and display_height. */ + uint32_t display_width; /* visual presentation region size of horizontal direction as fixed point 16.16 number. */ + uint32_t display_height; /* visual presentation region size of vertical direction as fixed point 16.16 number. */ + uint8_t aperture_modes; /* track aperture modes present + * This feature is only available under QuickTime file format. + * Automatically disabled if multiple sample description is present or scaling method is specified. */ +} lsmash_track_parameters_t; + +typedef struct +{ + lsmash_brand_type_code major_brand; /* the best used brand */ + lsmash_brand_type_code *brands; /* the list of compatible brands */ + uint32_t number_of_brands; /* the number of compatible brands used in the movie */ + uint32_t minor_version; /* minor version of best used brand */ + double max_chunk_duration; /* max duration per chunk in seconds. 0.5 is default value. */ + double max_async_tolerance; /* max tolerance, in seconds, for amount of interleaving asynchronization between tracks. + * 2.0 is default value. At least twice of max_chunk_duration is used. */ + uint64_t max_chunk_size; /* max size per chunk in bytes. 4*1024*1024 (4MiB) is default value. */ + uint64_t max_read_size; /* max size of reading from a chunk at a time. 4*1024*1024 (4MiB) is default value. */ + uint32_t timescale; /* movie timescale: timescale for the entire presentation */ + uint64_t duration; /* the duration, expressed in movie timescale, of the longest track + * You can't set this parameter manually. */ + int32_t playback_rate; /* fixed point 16.16 number. 0x00010000 is normal forward playback and default value. */ + int32_t playback_volume; /* fixed point 8.8 number. 0x0100 is full volume and default value. */ + int32_t preview_time; /* the time value in the movie at which the preview begins */ + int32_t preview_duration; /* the duration of the movie preview in movie timescale units */ + int32_t poster_time; /* the time value of the time of the movie poster */ + uint32_t number_of_tracks; /* the number of tracks in the movie + * You can't set this parameter manually. */ + /* Any user shouldn't use the following parameter. */ + PRIVATE lsmash_brand_type_code brands_shadow[50]; +} lsmash_movie_parameters_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_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size ); +int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_type, void* summary ); + +int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number ); +int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length ); + +int lsmash_write_free( lsmash_root_t *root ); + +uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID ); +uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID ); +uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID ); +uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID ); +uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID ); +uint32_t lsmash_get_movie_timescale( lsmash_root_t *root ); + +int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, + uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility, + uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne, + uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 ); +int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta ); +int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length ); +int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name ); + +int lsmash_create_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_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 ); + + +lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode_code mode ); +void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param ); +int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ); +int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param ); +uint32_t lsmash_create_track( lsmash_root_t *root, uint32_t handler_type ); +uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number ); +void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param ); +int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param ); +int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param ); +void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param ); +int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param ); +int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param ); +lsmash_sample_t *lsmash_create_sample( uint32_t size ); +int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size ); +void lsmash_delete_sample( lsmash_sample_t *sample ); +int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample ); +int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta ); +int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux ); +void lsmash_discard_boxes( lsmash_root_t *root ); +void lsmash_destroy_root( lsmash_root_t *root ); + +int lsmash_create_fragment_movie( lsmash_root_t *root ); +int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration ); + +void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID ); +void lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID ); +void lsmash_delete_tyrant_chapter( lsmash_root_t *root ); + +#ifdef LSMASH_DEMUXER_ENABLED +int lsmash_print_movie( lsmash_root_t *root ); + +int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID ); +int lsmash_copy_decoder_specific_info( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID ); +int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID ); +void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID ); +int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta ); +int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts ); +lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ); +int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number ); +#endif + +/* to facilitate to make exdata (typically DecoderSpecificInfo or AudioSpecificConfig). */ +int lsmash_setup_AudioSpecificConfig( lsmash_audio_summary_t* summary ); +int lsmash_summary_add_exdata( lsmash_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. */ +lsmash_audio_summary_t* lsmash_create_audio_summary(); +void lsmash_cleanup_audio_summary( lsmash_audio_summary_t* summary ); + +#undef PRIVATE + +#endif diff --git a/output/mp4/mp4a.c b/output/mp4/mp4a.c new file mode 100644 index 0000000..ed611f8 --- /dev/null +++ b/output/mp4/mp4a.c @@ -0,0 +1,646 @@ +/***************************************************************************** + * 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. */ + +#include "internal.h" /* must be placed first */ + +#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 samplingFrequencyIndex */ +/* ISO/IEC 14496-3 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 Interface to ISO/IEC 14496-1 (MPEG-4 Systems), Syntax of AudioSpecificConfig(). */ +/* This structure is represent of regularized AudioSpecificConfig. */ +/* for actual definition, see Syntax of GetAudioObjectType() for audioObjectType and extensionAudioObjectType. */ +typedef struct { + lsmash_mp4a_aac_sbr_mode sbr_mode; /* L-SMASH's original, including sbrPresent flag. */ + lsmash_mp4a_AudioObjectType audioObjectType; + uint8_t samplingFrequencyIndex; + uint32_t samplingFrequency; + uint8_t channelConfiguration; + lsmash_mp4a_AudioObjectType extensionAudioObjectType; + uint8_t extensionSamplingFrequencyIndex; + uint8_t extensionSamplingFrequency; + /* if( audioObjectType in + #[ 1, 2, 3, 4, 6, 7, *17, *19, *20, *21, *22, *23 ] // GASpecificConfig, AAC relatives and TwinVQ, BSAC + [ 8 ] // CelpSpecificConfig, not supported + [ 9 ] // HvxcSpecificConfig, not supported + [ 12 ] // TTSSpecificConfig, not supported + [ 13, 14, 15, 16 ] // StructuredAudioSpecificConfig, notsupported + [ 24 ] // ErrorResilientCelpSpecificConfig, notsupported + [ 25 ] // ErrorResilientHvxcSpecificConfig, notsupported + [ 26, 27 ] // ParametricSpecificConfig, notsupported + [ 28 ] // SSCSpecificConfig, notsupported + #[ 32, 33, 34 ] // MPEG_1_2_SpecificConfig + [ 35 ] // DSTSpecificConfig, notsupported + ){ */ + void* deepAudioSpecificConfig; // L-SMASH's original name, reperesents such as GASpecificConfig. */ + /* } */ + /* + // error resilient stuff, not supported + if( audioObjectType in [17, 19, 20, 21, 22, 23, 24, 25, 26, 27] ){ + uint8_t epConfig // 2bit + if( epConfig == 2 || epConfig == 3 ){ + ErrorProtectionSpecificConfig(); + } + if( epConfig == 3 ){ + uint8_t directMapping; // 1bit, currently always 1. + if( !directMapping ){ + // tbd + } + } + } + */ +} mp4a_AudioSpecificConfig_t; + +/* ISO/IEC 14496-3 Decoder configuration (GASpecificConfig), Syntax of GASpecificConfig() */ +/* ISO/IEC 14496-3 GASpecificConfig(), Sampling frequency mapping */ +typedef struct { + uint8_t frameLengthFlag; /* FIXME: AAC_SSR: shall be 0, Others: depends, but noramally 0. */ + uint8_t dependsOnCoreCoder; /* FIXME: used if scalable AAC. */ + /* + if( dependsOnCoreCoder ){ + uint16_t coreCoderDelay; // 14bits + } + */ + uint8_t extensionFlag; /* 1bit, 1 if ErrorResilience */ + /* if( !channelConfiguration ){ */ + void* program_config_element; /* currently not supported. */ + /* } */ + /* + // we do not support AAC_scalable + if( (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_AAC_scalable) || (audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable) ){ + uint8_t layerNr; // 3bits + } + */ + /* + // we do not support special AACs + if( extensionFlag ){ + if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_BSAC ){ + uint8_t numOfSubFrame; // 5bits + uint8_t layer_length; // 11bits + } + if( audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LC || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LTP + || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_scalable || audioObjectType == MP4A_AUDIO_OBJECT_TYPE_ER_AAC_LD + ){ + uint8_t aacSectionDataResilienceFlag; // 1bit + uint8_t aacScalefactorDataResilienceFlag; // 1bit + uint8_t aacSpectralDataResilienceFlag; // 1bit + } + uint8_t extensionFlag3; // 1bit + if( extensionFlag3 ){ + // tbd in version 3 + } + } + */ +} mp4a_GASpecificConfig_t; + +/* ISO/IEC 14496-3 MPEG_1_2_SpecificConfig */ +typedef struct { + uint8_t extension; /* shall be 0. */ +} mp4a_MPEG_1_2_SpecificConfig_t; + +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 Levels within the profiles / Levels for the High Efficiency AAC Profile */ + if( i < 0x3 ) + { + free( asc ); + return NULL; + } + asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_SBR; + } + else + asc->extensionAudioObjectType = MP4A_AUDIO_OBJECT_TYPE_NULL; + + if( sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE || sbr_mode == MP4A_AAC_SBR_BACKWARD_COMPATIBLE ) + { + asc->extensionSamplingFrequency = frequency * 2; + asc->extensionSamplingFrequencyIndex = i == 0xC ? 0xF : mp4a_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 || summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream ) + return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ + if( summary->object_type_indication != MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3 ) + return MP4A_AUDIO_PLI_NOT_SPECIFIED; /* This is of audio stream, but not described in ISO/IEC 14496-3. */ + if( summary->channels == 0 || summary->frequency == 0 ) + return MP4A_AUDIO_PLI_NONE_REQUIRED; /* means error. */ + + mp4a_audioProfileLevelIndication pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + switch( summary->aot ) + { + case MP4A_AUDIO_OBJECT_TYPE_AAC_LC: + if( summary->sbr_mode == MP4A_AAC_SBR_HIERARCHICAL ) + { + /* NOTE: This is not strictly preferable, but accurate; just possibly over-estimated. + We do not expect to use MP4A_AAC_SBR_HIERARCHICAL mode without SBR, nor downsampled mode with SBR. */ + if( summary->channels <= 2 && summary->frequency <= 24000 ) + pli = MP4A_AUDIO_PLI_HE_AAC_L2; + else if( summary->channels <= 5 && summary->frequency <= 48000 ) + pli = MP4A_AUDIO_PLI_HE_AAC_L5; + else + pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + break; + } + /* pretending plain AAC-LC, if actually HE-AAC. */ + static const uint32_t mp4sys_aac_pli_table[5][3] = { + /* channels, frequency, audioProfileLevelIndication */ + { 6, 96000, MP4A_AUDIO_PLI_AAC_L5 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ + { 6, 48000, MP4A_AUDIO_PLI_AAC_L4 }, /* FIXME: 6ch is not strictly correct, but works in many case. */ + { 2, 48000, MP4A_AUDIO_PLI_AAC_L2 }, + { 2, 24000, MP4A_AUDIO_PLI_AAC_L1 }, + { 0, 0, MP4A_AUDIO_PLI_NOT_SPECIFIED } + }; + for( int i = 0; summary->channels <= mp4sys_aac_pli_table[i][0] && summary->frequency <= mp4sys_aac_pli_table[i][1] ; i++ ) + pli = mp4sys_aac_pli_table[i][2]; + break; + case MP4A_AUDIO_OBJECT_TYPE_ALS: + if( summary->channels <= 2 && summary->frequency <= 48000 && summary->bit_depth <= 16 && summary->samples_in_frame <= 4096 ) + pli = MP4A_AUDIO_PLI_ALS_Simple_L1; + else + pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; + break; + case MP4A_AUDIO_OBJECT_TYPE_Layer_1: + case MP4A_AUDIO_OBJECT_TYPE_Layer_2: + case MP4A_AUDIO_OBJECT_TYPE_Layer_3: + pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; /* 14496-3, Audio profiles and levels, does not allow any pli. */ + break; + default: + pli = MP4A_AUDIO_PLI_NOT_SPECIFIED; /* something we don't know/support, or what the spec never covers. */ + break; + } + return pli; +} + +static int mp4sys_is_same_profile( mp4a_audioProfileLevelIndication a, mp4a_audioProfileLevelIndication b ) +{ + switch( a ) + { + case MP4A_AUDIO_PLI_Main_L1: + case MP4A_AUDIO_PLI_Main_L2: + case MP4A_AUDIO_PLI_Main_L3: + case MP4A_AUDIO_PLI_Main_L4: + if( MP4A_AUDIO_PLI_Main_L1 <= b && b <= MP4A_AUDIO_PLI_Main_L4 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_Scalable_L1: + case MP4A_AUDIO_PLI_Scalable_L2: + case MP4A_AUDIO_PLI_Scalable_L3: + case MP4A_AUDIO_PLI_Scalable_L4: + if( MP4A_AUDIO_PLI_Scalable_L1 <= b && b <= MP4A_AUDIO_PLI_Scalable_L4 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_Speech_L1: + case MP4A_AUDIO_PLI_Speech_L2: + if( MP4A_AUDIO_PLI_Speech_L1 <= b && b <= MP4A_AUDIO_PLI_Speech_L2 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_Synthetic_L1: + case MP4A_AUDIO_PLI_Synthetic_L2: + case MP4A_AUDIO_PLI_Synthetic_L3: + if( MP4A_AUDIO_PLI_Synthetic_L1 <= b && b <= MP4A_AUDIO_PLI_Synthetic_L3 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_HighQuality_L1: + case MP4A_AUDIO_PLI_HighQuality_L2: + case MP4A_AUDIO_PLI_HighQuality_L3: + case MP4A_AUDIO_PLI_HighQuality_L4: + case MP4A_AUDIO_PLI_HighQuality_L5: + case MP4A_AUDIO_PLI_HighQuality_L6: + case MP4A_AUDIO_PLI_HighQuality_L7: + case MP4A_AUDIO_PLI_HighQuality_L8: + if( MP4A_AUDIO_PLI_HighQuality_L1 <= b && b <= MP4A_AUDIO_PLI_HighQuality_L8 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_LowDelay_L1: + case MP4A_AUDIO_PLI_LowDelay_L2: + case MP4A_AUDIO_PLI_LowDelay_L3: + case MP4A_AUDIO_PLI_LowDelay_L4: + case MP4A_AUDIO_PLI_LowDelay_L5: + case MP4A_AUDIO_PLI_LowDelay_L6: + case MP4A_AUDIO_PLI_LowDelay_L7: + case MP4A_AUDIO_PLI_LowDelay_L8: + if( MP4A_AUDIO_PLI_LowDelay_L1 <= b && b <= MP4A_AUDIO_PLI_LowDelay_L8 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_Natural_L1: + case MP4A_AUDIO_PLI_Natural_L2: + case MP4A_AUDIO_PLI_Natural_L3: + case MP4A_AUDIO_PLI_Natural_L4: + if( MP4A_AUDIO_PLI_Natural_L1 <= b && b <= MP4A_AUDIO_PLI_Natural_L4 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_MobileInternetworking_L1: + case MP4A_AUDIO_PLI_MobileInternetworking_L2: + case MP4A_AUDIO_PLI_MobileInternetworking_L3: + case MP4A_AUDIO_PLI_MobileInternetworking_L4: + case MP4A_AUDIO_PLI_MobileInternetworking_L5: + case MP4A_AUDIO_PLI_MobileInternetworking_L6: + if( MP4A_AUDIO_PLI_MobileInternetworking_L1 <= b && b <= MP4A_AUDIO_PLI_MobileInternetworking_L6 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_AAC_L1: + case MP4A_AUDIO_PLI_AAC_L2: + case MP4A_AUDIO_PLI_AAC_L4: + case MP4A_AUDIO_PLI_AAC_L5: + if( MP4A_AUDIO_PLI_AAC_L1 <= b && b <= MP4A_AUDIO_PLI_AAC_L5 ) + return 1; + return 0; + break; + case MP4A_AUDIO_PLI_HE_AAC_L2: + case MP4A_AUDIO_PLI_HE_AAC_L3: + case MP4A_AUDIO_PLI_HE_AAC_L4: + case MP4A_AUDIO_PLI_HE_AAC_L5: + if( MP4A_AUDIO_PLI_HE_AAC_L2 <= b && b <= MP4A_AUDIO_PLI_HE_AAC_L5 ) + return 1; + return 0; + break; + default: + break; + } + return 0; +} + +/* NOTE: This function is not strictly preferable, but accurate. + The spec of audioProfileLevelIndication is too much complicated. */ +mp4a_audioProfileLevelIndication mp4a_max_audioProfileLevelIndication( mp4a_audioProfileLevelIndication a, mp4a_audioProfileLevelIndication b ) +{ + /* NONE_REQUIRED is minimal priotity, and NOT_SPECIFIED is max priority. */ + if( a == MP4A_AUDIO_PLI_NOT_SPECIFIED || b == MP4A_AUDIO_PLI_NONE_REQUIRED ) + return a; + if( a == MP4A_AUDIO_PLI_NONE_REQUIRED || b == MP4A_AUDIO_PLI_NOT_SPECIFIED ) + return b; + mp4a_audioProfileLevelIndication c, d; + if( a < b ) + { + c = a; + d = b; + } + else + { + c = b; + d = a; + } + /* AAC-LC and SBR specific; If mixtured there, use correspond HE_AAC profile. */ + if( MP4A_AUDIO_PLI_AAC_L1 <= c && c <= MP4A_AUDIO_PLI_AAC_L5 + && MP4A_AUDIO_PLI_HE_AAC_L2 <= d && d <= MP4A_AUDIO_PLI_HE_AAC_L5 ) + { + if( c <= MP4A_AUDIO_PLI_AAC_L2 ) + return d; + c += 4; /* upgrade to HE-AAC */ + return c > d ? c : d; + } + /* General */ + if( mp4sys_is_same_profile( c, d ) ) + return d; + return MP4A_AUDIO_PLI_NOT_SPECIFIED; +} diff --git a/output/mp4/mp4a.h b/output/mp4/mp4a.h new file mode 100644 index 0000000..a9532fe --- /dev/null +++ b/output/mp4/mp4a.h @@ -0,0 +1,120 @@ +/***************************************************************************** + * 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 "utils.h" + +/*************************************************************************** + MPEG-4 Systems for MPEG-4 Audio +***************************************************************************/ + +/* 14496-3 audioProfileLevelIndication */ +typedef enum { + MP4A_AUDIO_PLI_Reserved = 0x00, /* Reserved for ISO use */ + MP4A_AUDIO_PLI_Main_L1 = 0x01, /* Main Audio Profile L1 */ + MP4A_AUDIO_PLI_Main_L2 = 0x02, /* Main Audio Profile L2 */ + MP4A_AUDIO_PLI_Main_L3 = 0x03, /* Main Audio Profile L3 */ + MP4A_AUDIO_PLI_Main_L4 = 0x04, /* Main Audio Profile L4 */ + MP4A_AUDIO_PLI_Scalable_L1 = 0x05, /* Scalable Audio Profile L1 */ + MP4A_AUDIO_PLI_Scalable_L2 = 0x06, /* Scalable Audio Profile L2 */ + MP4A_AUDIO_PLI_Scalable_L3 = 0x07, /* Scalable Audio Profile L3 */ + MP4A_AUDIO_PLI_Scalable_L4 = 0x08, /* Scalable Audio Profile L4 */ + MP4A_AUDIO_PLI_Speech_L1 = 0x09, /* Speech Audio Profile L1 */ + MP4A_AUDIO_PLI_Speech_L2 = 0x0A, /* Speech Audio Profile L2 */ + MP4A_AUDIO_PLI_Synthetic_L1 = 0x0B, /* Synthetic Audio Profile L1 */ + MP4A_AUDIO_PLI_Synthetic_L2 = 0x0C, /* Synthetic Audio Profile L2 */ + MP4A_AUDIO_PLI_Synthetic_L3 = 0x0D, /* Synthetic Audio Profile L3 */ + MP4A_AUDIO_PLI_HighQuality_L1 = 0x0E, /* High Quality Audio Profile L1 */ + MP4A_AUDIO_PLI_HighQuality_L2 = 0x0F, /* High Quality Audio Profile L2 */ + MP4A_AUDIO_PLI_HighQuality_L3 = 0x10, /* High Quality Audio Profile L3 */ + MP4A_AUDIO_PLI_HighQuality_L4 = 0x11, /* High Quality Audio Profile L4 */ + MP4A_AUDIO_PLI_HighQuality_L5 = 0x12, /* High Quality Audio Profile L5 */ + MP4A_AUDIO_PLI_HighQuality_L6 = 0x13, /* High Quality Audio Profile L6 */ + MP4A_AUDIO_PLI_HighQuality_L7 = 0x14, /* High Quality Audio Profile L7 */ + MP4A_AUDIO_PLI_HighQuality_L8 = 0x15, /* High Quality Audio Profile L8 */ + MP4A_AUDIO_PLI_LowDelay_L1 = 0x16, /* Low Delay Audio Profile L1 */ + MP4A_AUDIO_PLI_LowDelay_L2 = 0x17, /* Low Delay Audio Profile L2 */ + MP4A_AUDIO_PLI_LowDelay_L3 = 0x18, /* Low Delay Audio Profile L3 */ + MP4A_AUDIO_PLI_LowDelay_L4 = 0x19, /* Low Delay Audio Profile L4 */ + MP4A_AUDIO_PLI_LowDelay_L5 = 0x1A, /* Low Delay Audio Profile L5 */ + MP4A_AUDIO_PLI_LowDelay_L6 = 0x1B, /* Low Delay Audio Profile L6 */ + MP4A_AUDIO_PLI_LowDelay_L7 = 0x1C, /* Low Delay Audio Profile L7 */ + MP4A_AUDIO_PLI_LowDelay_L8 = 0x1D, /* Low Delay Audio Profile L8 */ + MP4A_AUDIO_PLI_Natural_L1 = 0x1E, /* Natural Audio Profile L1 */ + MP4A_AUDIO_PLI_Natural_L2 = 0x1F, /* Natural Audio Profile L2 */ + MP4A_AUDIO_PLI_Natural_L3 = 0x20, /* Natural Audio Profile L3 */ + MP4A_AUDIO_PLI_Natural_L4 = 0x21, /* Natural Audio Profile L4 */ + MP4A_AUDIO_PLI_MobileInternetworking_L1 = 0x22, /* Mobile Audio Internetworking Profile L1 */ + MP4A_AUDIO_PLI_MobileInternetworking_L2 = 0x23, /* Mobile Audio Internetworking Profile L2 */ + MP4A_AUDIO_PLI_MobileInternetworking_L3 = 0x24, /* Mobile Audio Internetworking Profile L3 */ + MP4A_AUDIO_PLI_MobileInternetworking_L4 = 0x25, /* Mobile Audio Internetworking Profile L4 */ + MP4A_AUDIO_PLI_MobileInternetworking_L5 = 0x26, /* Mobile Audio Internetworking Profile L5 */ + MP4A_AUDIO_PLI_MobileInternetworking_L6 = 0x27, /* Mobile Audio Internetworking Profile L6 */ + MP4A_AUDIO_PLI_AAC_L1 = 0x28, /* AAC Profile L1 */ + MP4A_AUDIO_PLI_AAC_L2 = 0x29, /* AAC Profile L2 */ + MP4A_AUDIO_PLI_AAC_L4 = 0x2A, /* AAC Profile L4 */ + MP4A_AUDIO_PLI_AAC_L5 = 0x2B, /* AAC Profile L5 */ + MP4A_AUDIO_PLI_HE_AAC_L2 = 0x2C, /* High Efficiency AAC Profile L2 */ + MP4A_AUDIO_PLI_HE_AAC_L3 = 0x2D, /* High Efficiency AAC Profile L3 */ + MP4A_AUDIO_PLI_HE_AAC_L4 = 0x2E, /* High Efficiency AAC Profile L4 */ + MP4A_AUDIO_PLI_HE_AAC_L5 = 0x2F, /* High Efficiency AAC Profile L5 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L2 = 0x30, /* High Efficiency AAC v2 Profile L2 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L3 = 0x31, /* High Efficiency AAC v2 Profile L3 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L4 = 0x32, /* High Efficiency AAC v2 Profile L4 */ + MP4A_AUDIO_PLI_HE_AAC_v2_L5 = 0x33, /* High Efficiency AAC v2 Profile L5 */ + MP4A_AUDIO_PLI_LowDelay_AAC_L1 = 0x34, /* Low Delay AAC Profile L1 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L1= 0x35, /* Baseline MPEG Surround Profile L1 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L2= 0x36, /* Baseline MPEG Surround Profile L2 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L3= 0x37, /* Baseline MPEG Surround Profile L3 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L4= 0x38, /* Baseline MPEG Surround Profile L4 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L5= 0x39, /* Baseline MPEG Surround Profile L5 */ + MP4A_AUDIO_PLI_Baseline_MPEG_Surround_L6= 0x3A, /* Baseline MPEG Surround Profile L6 */ + MP4A_AUDIO_PLI_HD_AAC_L1 = 0x3B, /* High Definition AAC Profile L1 */ + MP4A_AUDIO_PLI_ALS_Simple_L1 = 0x3C, /* ALS Simple Profile L1 */ + MP4A_AUDIO_PLI_NOT_SPECIFIED = 0xFE, /* no audio profile specified */ + MP4A_AUDIO_PLI_NONE_REQUIRED = 0xFF, /* no audio capability required */ +} mp4a_audioProfileLevelIndication; + +#ifndef MP4A_INTERNAL + +typedef void mp4a_AudioSpecificConfig_t; + +/* export for mp4sys / importer */ +mp4a_AudioSpecificConfig_t* mp4a_create_AudioSpecificConfig( lsmash_mp4a_AudioObjectType aot, uint32_t frequency, uint32_t channels, lsmash_mp4a_aac_sbr_mode sbr_mode ); +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..b29b83b --- /dev/null +++ b/output/mp4/mp4sys.c @@ -0,0 +1,953 @@ +/***************************************************************************** + * 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 "internal.h" /* must be placed first */ + +#include "utils.h" + +#include +#include + +#include "mp4a.h" +#define MP4SYS_INTERNAL +#include "mp4sys.h" + +/*************************************************************************** + MPEG-4 Systems +***************************************************************************/ + +#define ALWAYS_28BITS_LENGTH_CODING 1 // for some weird (but originator's) devices + +/* List of Class Tags for Descriptors */ +typedef enum { + MP4SYS_DESCRIPTOR_TAG_Forbidden = 0x00, /* Forbidden */ + MP4SYS_DESCRIPTOR_TAG_ObjectDescrTag = 0x01, /* ObjectDescrTag */ + MP4SYS_DESCRIPTOR_TAG_InitialObjectDescrTag = 0x02, /* InitialObjectDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ES_DescrTag = 0x03, /* ES_DescrTag */ + MP4SYS_DESCRIPTOR_TAG_DecoderConfigDescrTag = 0x04, /* DecoderConfigDescrTag */ + MP4SYS_DESCRIPTOR_TAG_DecSpecificInfoTag = 0x05, /* DecSpecificInfoTag */ + MP4SYS_DESCRIPTOR_TAG_SLConfigDescrTag = 0x06, /* SLConfigDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ContentIdentDescrTag = 0x07, /* ContentIdentDescrTag */ + MP4SYS_DESCRIPTOR_TAG_SupplContentIdentDescrTag = 0x08, /* SupplContentIdentDescrTag */ + MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerTag = 0x09, /* IPI_DescrPointerTag */ + MP4SYS_DESCRIPTOR_TAG_IPMP_DescrPointerTag = 0x0A, /* IPMP_DescrPointerTag */ + MP4SYS_DESCRIPTOR_TAG_IPMP_DescrTag = 0x0B, /* IPMP_DescrTag */ + MP4SYS_DESCRIPTOR_TAG_QoS_DescrTag = 0x0C, /* QoS_DescrTag */ + MP4SYS_DESCRIPTOR_TAG_RegistrationDescrTag = 0x0D, /* RegistrationDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ES_ID_IncTag = 0x0E, /* ES_ID_IncTag */ + MP4SYS_DESCRIPTOR_TAG_ES_ID_RefTag = 0x0F, /* ES_ID_RefTag */ + MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag = 0x10, /* MP4_IOD_Tag, InitialObjectDescriptor for MP4 */ + MP4SYS_DESCRIPTOR_TAG_MP4_OD_Tag = 0x11, /* MP4_OD_Tag, ObjectDescriptor for MP4 */ + MP4SYS_DESCRIPTOR_TAG_IPI_DescrPointerRefTag = 0x12, /* IPI_DescrPointerRefTag */ + MP4SYS_DESCRIPTOR_TAG_ExtendedProfileLevelDescrTag = 0x13, /* ExtendedProfileLevelDescrTag */ + MP4SYS_DESCRIPTOR_TAG_profileLevelIndicationIndexDescrTag = 0x14, /* profileLevelIndicationIndexDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ContentClassificationDescrTag = 0x40, /* ContentClassificationDescrTag */ + MP4SYS_DESCRIPTOR_TAG_KeyWordDescrTag = 0x41, /* KeyWordDescrTag */ + MP4SYS_DESCRIPTOR_TAG_RatingDescrTag = 0x42, /* RatingDescrTag */ + MP4SYS_DESCRIPTOR_TAG_LanguageDescrTag = 0x43, /* LanguageDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ShortTextualDescrTag = 0x44, /* ShortTextualDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ExpandedTextualDescrTag = 0x45, /* ExpandedTextualDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ContentCreatorNameDescrTag = 0x46, /* ContentCreatorNameDescrTag */ + MP4SYS_DESCRIPTOR_TAG_ContentCreationDateDescrTag = 0x47, /* ContentCreationDateDescrTag */ + MP4SYS_DESCRIPTOR_TAG_OCICreatorNameDescrTag = 0x48, /* OCICreatorNameDescrTag */ + MP4SYS_DESCRIPTOR_TAG_OCICreationDateDescrTag = 0x49, /* OCICreationDateDescrTag */ + MP4SYS_DESCRIPTOR_TAG_SmpteCameraPositionDescrTag = 0x4A, /* SmpteCameraPositionDescrTag */ + MP4SYS_DESCRIPTOR_TAG_Forbidden1 = 0xFF, /* Forbidden */ +} mp4sys_descriptor_tag; +// MP4SYS_DESCRIPTOR_TAG_ES_DescrRemoveRefTag = 0x07, /* FIXME: (command tag), see 14496-14 Object Descriptors */ + +typedef struct { + uint32_t size; // 2^28 at most + mp4sys_descriptor_tag tag; +} mp4sys_descriptor_head_t; + +/* DecoderSpecificInfo */ +/* contents varies depends on ObjectTypeIndication and StreamType. */ +typedef struct { + mp4sys_descriptor_head_t header; + uint8_t* data; +} mp4sys_DecoderSpecificInfo_t; + +/* DecoderConfigDescriptor */ +typedef struct { + mp4sys_descriptor_head_t header; + lsmash_mp4sys_object_type_indication objectTypeIndication; + lsmash_mp4sys_stream_type streamType; + uint8_t upStream; /* bit(1), always 0 in this muxer, used for interactive contents. */ + uint8_t reserved; /* const bit(1), always 1. */ + uint32_t bufferSizeDB; /* maybe CPB size in bytes, NOT bits. */ + uint32_t maxBitrate; + uint32_t avgBitrate; /* 0 if VBR */ + mp4sys_DecoderSpecificInfo_t* decSpecificInfo; /* can be NULL. */ + /* 14496-1 seems to say if we are in IOD(InitialObjectDescriptor), we might use this. + See ExtensionProfileLevelDescr, The Initial Object Descriptor. + But I don't think this is mandatory despite 14496-1, because 14496-14 says, in OD or IOD, + we have to use ES_ID_Inc instead of ES_Descriptor, which does not have DecoderConfigDescriptor. */ + // profileLevelIndicationIndexDescriptor profileLevelIndicationIndexDescr [0..255]; +} mp4sys_DecoderConfigDescriptor_t; + +/* SLConfigDescriptor */ +typedef struct { + mp4sys_descriptor_head_t header; + uint8_t predefined; /* default the values from a set of predefined parameter sets as detailed below. + * 0x00 : Custum + * 0x01 : null SL packet header + * 0x02 : Reserved for use in MP4 files + * 0x03 - 0xFF : Reserved for ISO use + * MP4 file that does not use URL_Flag shall have constant value 0x02. */ + /* Custom values + * The following fields are placed if predefined == 0x00. */ + unsigned useAccessUnitStartFlag : 1; + unsigned useAccessUnitEndFlag : 1; + unsigned useRandomAccessPointFlag : 1; + unsigned hasRandomAccessUnitsOnlyFlag : 1; + unsigned usePaddingFlag : 1; + unsigned useTimeStampsFlag : 1; + unsigned useIdleFlag : 1; + unsigned durationFlag : 1; + uint32_t timeStampResolution; + uint32_t OCRResolution; + uint8_t timeStampLength; + uint8_t OCRLength; + uint8_t AU_Length; + uint8_t instantBitrateLength; + unsigned degradationPriorityLength : 4; + unsigned AU_seqNumLength : 5; + unsigned packetSeqNumLength : 5; + unsigned reserved : 2; + /* The following fields are placed if durationFlag is true. */ + uint32_t timeScale; + uint16_t accessUnitDuration; + uint16_t compositionUnitDuration; + /* The following fields are placed if useTimeStampsFlag is false. */ + uint64_t startDecodingTimeStamp; + uint64_t startCompositionTimeStamp; +} mp4sys_SLConfigDescriptor_t; + +/* ES_Descriptor */ +typedef struct +{ + mp4sys_descriptor_head_t header; + uint16_t ES_ID; + unsigned streamDependenceFlag : 1; /* no stream depencies between streams in this muxer, ES_ID of another elementary stream */ + unsigned URL_Flag : 1; /* no external URL referencing stream in MP4 */ + unsigned OCRstreamFlag : 1; /* no Object Clock Reference stream in this muxer (shall be false in MP4, useful if we're importing from MPEG-2?) */ + unsigned streamPriority : 5; /* no priority among streams in this muxer, higher is important */ + uint16_t dependsOn_ES_ID; + uint8_t URLlength; + uint8_t URLstring[255]; + uint16_t OCR_ES_Id; + mp4sys_DecoderConfigDescriptor_t *decConfigDescr; /* cannot be NULL. */ + mp4sys_SLConfigDescriptor_t *slConfigDescr; + /* descriptors below are not mandatory, I think Language Descriptor may somewhat useful */ + /* + IPI_DescrPointer ipiPtr[0 .. 1]; // used to indicate using other ES's IP_IdentificationDataSet + IP_IdentificationDataSet ipIDS[0 .. 255]; // abstract class, actually ContentIdentificationDescriptor(for commercial contents management), + // or SupplementaryContentIdentificationDescriptor(for embedding titles) + IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255]; // used to intellectual property / protection management + LanguageDescriptor langDescr[0 .. 255]; // used to identify the language of the audio/speech or text object + QoS_Descriptor qosDescr[0 .. 1]; // used to achieve QoS + RegistrationDescriptor regDescr[0 .. 1]; // used to carry elementary streams with data whose format is not recognized by ISO/IEC 14496-1 + ExtensionDescriptor extDescr[0 .. 255]; // abstract class, actually defined no subclass, maybe useless + */ +} mp4sys_ES_Descriptor_t; + +/* 14496-14 Object Descriptors (ES_ID_Inc) */ +typedef struct { + mp4sys_descriptor_head_t header; + uint32_t Track_ID; +} mp4sys_ES_ID_Inc_t; + +/* 14496-1 ObjectDescriptor / InitialObjectDescriptor */ +typedef struct { + mp4sys_descriptor_head_t header; + uint16_t ObjectDescriptorID; + // uint8_t URL_Flag; /* bit(1) */ + uint8_t includeInlineProfileLevelFlag; /* bit(1) */ + //const uint8_t reserved=0x0F(0b1111) or 0x1F(0b1.1111); /* bit(4 or 5), width is 4 for IOD, 5 for OD */ + /* if (URL_Flag) { + uint8_t URLlength; // bit(8) + char URLstring[256]; // bit(8)[] + }*/ + /* else { */ + mp4sys_ODProfileLevelIndication ODProfileLevelIndication; + mp4sys_sceneProfileLevelIndication sceneProfileLevelIndication; + mp4a_audioProfileLevelIndication audioProfileLevelIndication; + mp4sys_visualProfileLevelIndication visualProfileLevelIndication; + mp4sys_graphicsProfileLevelIndication graphicsProfileLevelIndication; + lsmash_entry_list_t* esDescr; /* List of ES_ID_Inc, not ES_Descriptor defined in 14496-1. 14496-14 overrides. */ + // OCI_Descriptor ociDescr[0 .. 255]; + // IPMP_DescriptorPointer ipmpDescrPtr[0 .. 255]; + /* } */ + // ExtensionDescriptor extDescr[0 .. 255]; +} mp4sys_ObjectDescriptor_t; + +int mp4sys_remove_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd || !esd->decConfigDescr ) + return -1; + if( !esd->decConfigDescr->decSpecificInfo ) + return 0; + if( esd->decConfigDescr->decSpecificInfo->data ) + free( esd->decConfigDescr->decSpecificInfo->data ); + free( esd->decConfigDescr->decSpecificInfo ); + esd->decConfigDescr->decSpecificInfo = NULL; + return 0; +} + +int mp4sys_remove_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd ) + return -1; + if( !esd->decConfigDescr ) + return 0; + mp4sys_remove_DecoderSpecificInfo( esd ); + free( esd->decConfigDescr ); + esd->decConfigDescr = NULL; + return 0; +} + +int mp4sys_remove_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd ) + return -1; + if( !esd->slConfigDescr ) + return 0; + free( esd->slConfigDescr ); + esd->slConfigDescr = NULL; + return 0; +} + +int mp4sys_remove_ES_Descriptor( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd ) + return 0; + mp4sys_remove_DecoderConfigDescriptor( esd ); + mp4sys_remove_SLConfigDescriptor( esd ); + free( esd ); + return 0; +} + +int mp4sys_remove_ES_ID_Incs( mp4sys_ObjectDescriptor_t* od ) +{ + if( !od ) + return -1; + if( od->esDescr ) + { + lsmash_remove_list( od->esDescr, NULL ); + od->esDescr = NULL; + } + return 0; +} + +int mp4sys_remove_ObjectDescriptor( mp4sys_ObjectDescriptor_t* od ) +{ + if( !od ) + return 0; + mp4sys_remove_ES_ID_Incs( od ); + free( od ); + return 0; +} + +int mp4sys_add_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd, void* dsi_payload, uint32_t dsi_payload_length ) +{ + if( !esd || !esd->decConfigDescr || dsi_payload == NULL || dsi_payload_length == 0 ) + return -1; + mp4sys_DecoderSpecificInfo_t* dsi = (mp4sys_DecoderSpecificInfo_t*)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 ) +{ +#if ALWAYS_28BITS_LENGTH_CODING + return payload_size_in_byte + 4 + 1; /* +4 means 28bits length coding, +1 means tag's space */ +#else + /* descriptor length will be split into 7bits + see 14496-1 Expandable classes and Length encoding of descriptors and commands */ + uint32_t i; + for( i = 1; payload_size_in_byte >> ( 7 * i ); i++ ); + return payload_size_in_byte + i + 1; /* +1 means tag's space */ +#endif +} + +static uint32_t mp4sys_update_DecoderSpecificInfo_size( mp4sys_ES_Descriptor_t* esd ) +{ + debug_if( !esd || !esd->decConfigDescr ) + return 0; + if( !esd->decConfigDescr->decSpecificInfo ) + return 0; + /* no need to update, header.size is already set */ + return mp4sys_get_descriptor_size( esd->decConfigDescr->decSpecificInfo->header.size ); +} + +static uint32_t mp4sys_update_DecoderConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd ) +{ + debug_if( !esd ) + return 0; + if( !esd->decConfigDescr ) + return 0; + uint32_t size = 13; + size += mp4sys_update_DecoderSpecificInfo_size( esd ); + esd->decConfigDescr->header.size = size; + return mp4sys_get_descriptor_size( size ); +} + +static uint32_t mp4sys_update_SLConfigDescriptor_size( mp4sys_ES_Descriptor_t* esd ) +{ + debug_if( !esd ) + return 0; + if( !esd->slConfigDescr ) + return 0; + mp4sys_SLConfigDescriptor_t *slcd = esd->slConfigDescr; + uint32_t size = 1; + if( slcd->predefined == 0x00 ) + size += 15; + if( slcd->durationFlag ) + size += 8; + if( !slcd->useTimeStampsFlag ) + size += (2 * slcd->timeStampLength + 7) / 8; + esd->slConfigDescr->header.size = size; + return mp4sys_get_descriptor_size( size ); +} + +uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd ) +{ + if( !esd ) + return 0; + uint32_t size = 3; + if( esd->streamDependenceFlag ) + size += 2; + if( esd->URL_Flag ) + size += 1 + esd->URLlength; + if( esd->OCRstreamFlag ) + size += 2; + size += mp4sys_update_DecoderConfigDescriptor_size( esd ); + size += mp4sys_update_SLConfigDescriptor_size( esd ); + esd->header.size = size; + return mp4sys_get_descriptor_size( size ); +} + +static uint32_t mp4sys_update_ES_ID_Inc_size( mp4sys_ES_ID_Inc_t* es_id_inc ) +{ + debug_if( !es_id_inc ) + return 0; + es_id_inc->header.size = 4; + return mp4sys_get_descriptor_size( es_id_inc->header.size ); +} + +/* This function works as aggregate of ES_ID_Incs, so this function itself updates no size information */ +static uint32_t mp4sys_update_ES_ID_Incs_size( mp4sys_ObjectDescriptor_t* od ) +{ + debug_if( !od ) + return 0; + if( !od->esDescr ) + return 0; + uint32_t size = 0; + for( lsmash_entry_t *entry = od->esDescr->head; entry; entry = entry->next ) + size += mp4sys_update_ES_ID_Inc_size( (mp4sys_ES_ID_Inc_t*)entry->data ); + return size; +} + +uint32_t mp4sys_update_ObjectDescriptor_size( mp4sys_ObjectDescriptor_t* od ) +{ + if( !od ) + return 0; + uint32_t size = od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ? 7 : 2; + size += mp4sys_update_ES_ID_Incs_size( od ); + od->header.size = size; + return mp4sys_get_descriptor_size( size ); +} + +static int mp4sys_put_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header ) +{ + debug_if( !bs || !header ) + return -1; + lsmash_bs_put_byte( bs, header->tag ); + /* descriptor length will be splitted into 7bits + see 14496-1 Expandable classes and Length encoding of descriptors and commands */ +#if ALWAYS_28BITS_LENGTH_CODING + lsmash_bs_put_byte( bs, ( header->size >> 21 ) | 0x80 ); + lsmash_bs_put_byte( bs, ( header->size >> 14 ) | 0x80 ); + lsmash_bs_put_byte( bs, ( header->size >> 7 ) | 0x80 ); +#else + for( uint32_t i = mp4sys_get_descriptor_size( header->size ) - header->size - 2; i; i-- ){ + lsmash_bs_put_byte( bs, ( header->size >> ( 7 * i ) ) | 0x80 ); + } +#endif + lsmash_bs_put_byte( bs, header->size & 0x7F ); + return 0; +} + +static int mp4sys_write_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_DecoderSpecificInfo_t* dsi ) +{ + debug_if( !bs ) + return -1; + if( !dsi ) + return 0; /* can be NULL */ + debug_if( mp4sys_put_descriptor_header( bs, &dsi->header ) ) + return -1; + if( dsi->data && dsi->header.size != 0 ) + lsmash_bs_put_bytes( bs, dsi->data, dsi->header.size ); + return lsmash_bs_write_data( bs ); +} + +static int mp4sys_write_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_DecoderConfigDescriptor_t* dcd ) +{ + debug_if( !bs ) + return -1; + if( !dcd ) + return -1; /* cannot be NULL */ + debug_if( mp4sys_put_descriptor_header( bs, &dcd->header ) ) + return -1; + lsmash_bs_put_byte( bs, dcd->objectTypeIndication ); + uint8_t temp; + temp = (dcd->streamType << 2) & 0x3F; + temp |= (dcd->upStream << 1) & 0x01; + temp |= dcd->reserved & 0x01; + lsmash_bs_put_byte( bs, temp ); + lsmash_bs_put_be24( bs, dcd->bufferSizeDB ); + lsmash_bs_put_be32( bs, dcd->maxBitrate ); + lsmash_bs_put_be32( bs, dcd->avgBitrate ); + if( lsmash_bs_write_data( bs ) ) + return -1; + return mp4sys_write_DecoderSpecificInfo( bs, dcd->decSpecificInfo ); + /* here, profileLevelIndicationIndexDescriptor is omitted */ +} + +static int mp4sys_write_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_SLConfigDescriptor_t* slcd ) +{ + debug_if( !bs ) + return -1; + if( !slcd ) + return 0; + debug_if( mp4sys_put_descriptor_header( bs, &slcd->header ) ) + return -1; + lsmash_bs_put_byte( bs, slcd->predefined ); + if( slcd->predefined == 0x00 ) + { + uint8_t temp8; + temp8 = slcd->useAccessUnitStartFlag << 7; + temp8 |= slcd->useAccessUnitEndFlag << 6; + temp8 |= slcd->useRandomAccessPointFlag << 5; + temp8 |= slcd->hasRandomAccessUnitsOnlyFlag << 4; + temp8 |= slcd->usePaddingFlag << 3; + temp8 |= slcd->useTimeStampsFlag << 2; + temp8 |= slcd->useIdleFlag << 1; + temp8 |= slcd->durationFlag; + lsmash_bs_put_byte( bs, temp8 ); + lsmash_bs_put_be32( bs, slcd->timeStampResolution ); + lsmash_bs_put_be32( bs, slcd->OCRResolution ); + lsmash_bs_put_byte( bs, slcd->timeStampLength ); + lsmash_bs_put_byte( bs, slcd->OCRLength ); + lsmash_bs_put_byte( bs, slcd->AU_Length ); + lsmash_bs_put_byte( bs, slcd->instantBitrateLength ); + uint16_t temp16; + temp16 = slcd->degradationPriorityLength << 12; + temp16 |= slcd->AU_seqNumLength << 7; + temp16 |= slcd->packetSeqNumLength << 2; + temp16 |= slcd->reserved; + lsmash_bs_put_be32( bs, temp16 ); + } + if( slcd->durationFlag ) + { + lsmash_bs_put_be32( bs, slcd->timeScale ); + lsmash_bs_put_be16( bs, slcd->accessUnitDuration ); + lsmash_bs_put_be16( bs, slcd->compositionUnitDuration ); + } + if( !slcd->useTimeStampsFlag ) + { + lsmash_bits_t *bits = lsmash_bits_create( bs ); + if( !bits ) + return -1; + lsmash_bits_put( bits, slcd->startDecodingTimeStamp, slcd->timeStampLength ); + lsmash_bits_put( bits, slcd->startCompositionTimeStamp, slcd->timeStampLength ); + lsmash_bits_put_align( bits ); + lsmash_bits_cleanup( bits ); + } + return lsmash_bs_write_data( bs ); +} + +int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t* esd ) +{ + if( !bs || !esd ) + return -1; + debug_if( mp4sys_put_descriptor_header( bs, &esd->header ) ) + return -1; + lsmash_bs_put_be16( bs, esd->ES_ID ); + uint8_t temp; + temp = esd->streamDependenceFlag << 7; + temp |= esd->URL_Flag << 6; + temp |= esd->OCRstreamFlag << 5; + temp |= esd->streamPriority; + lsmash_bs_put_byte( bs, temp ); + if( esd->streamDependenceFlag ) + lsmash_bs_put_be16( bs, esd->dependsOn_ES_ID ); + if( esd->URL_Flag ) + { + lsmash_bs_put_byte( bs, esd->URLlength ); + lsmash_bs_put_bytes( bs, esd->URLstring, esd->URLlength ); + } + if( esd->OCRstreamFlag ) + lsmash_bs_put_be16( bs, esd->OCR_ES_Id ); + /* here, some syntax elements are omitted due to previous flags (all 0) */ + if( lsmash_bs_write_data( bs ) ) + return -1; + if( mp4sys_write_DecoderConfigDescriptor( bs, esd->decConfigDescr ) ) + return -1; + return mp4sys_write_SLConfigDescriptor( bs, esd->slConfigDescr ); +} + +static int mp4sys_put_ES_ID_Inc( lsmash_bs_t *bs, mp4sys_ES_ID_Inc_t* es_id_inc ) +{ + debug_if( !es_id_inc ) + return 0; + debug_if( mp4sys_put_descriptor_header( bs, &es_id_inc->header ) ) + return -1; + lsmash_bs_put_be32( bs, es_id_inc->Track_ID ); + return 0; +} + +/* This function works as aggregate of ES_ID_Incs */ +static int mp4sys_write_ES_ID_Incs( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od ) +{ + debug_if( !od ) + return 0; + if( !od->esDescr ) + return 0; /* This may violate the spec, but some muxer do this */ + for( lsmash_entry_t *entry = od->esDescr->head; entry; entry = entry->next ) + mp4sys_put_ES_ID_Inc( bs, (mp4sys_ES_ID_Inc_t*)entry->data ); + return lsmash_bs_write_data( bs ); +} + +int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od ) +{ + if( !bs || !od ) + return -1; + debug_if( mp4sys_put_descriptor_header( bs, &od->header ) ) + return -1; + uint16_t temp = (od->ObjectDescriptorID << 6); + // temp |= (0x0 << 5); /* URL_Flag */ + temp |= (od->includeInlineProfileLevelFlag << 4); /* if MP4_OD, includeInlineProfileLevelFlag is 0x1. */ + temp |= 0xF; /* reserved */ + lsmash_bs_put_be16( bs, temp ); + /* here, since we don't support URL_Flag, we put ProfileLevelIndications */ + if( od->header.tag == MP4SYS_DESCRIPTOR_TAG_MP4_IOD_Tag ) + { + lsmash_bs_put_byte( bs, od->ODProfileLevelIndication ); + lsmash_bs_put_byte( bs, od->sceneProfileLevelIndication ); + lsmash_bs_put_byte( bs, od->audioProfileLevelIndication ); + lsmash_bs_put_byte( bs, od->visualProfileLevelIndication ); + lsmash_bs_put_byte( bs, od->graphicsProfileLevelIndication ); + } + if( lsmash_bs_write_data( bs ) ) + return -1; + return mp4sys_write_ES_ID_Incs( bs, od ); +} + +#ifdef LSMASH_DEMUXER_ENABLED +static int mp4sys_copy_DecoderSpecificInfo( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src ) +{ + if( !src || !src->decConfigDescr || !dst || !dst->decConfigDescr + || mp4sys_remove_DecoderSpecificInfo( dst ) ) + return -1; + mp4sys_DecoderSpecificInfo_t *dsi = src->decConfigDescr->decSpecificInfo; + if( !dsi || !dsi->data || !dsi->header.size ) + return 0; + return mp4sys_add_DecoderSpecificInfo( dst, dsi->data, dsi->header.size ); +} + +static int mp4sys_copy_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src ) +{ + if( !src || !dst + || mp4sys_remove_DecoderConfigDescriptor( dst ) ) + return -1; + if( !src->decConfigDescr ) + return 0; + if( mp4sys_add_DecoderConfigDescriptor( dst, 0, 0, 0, 0, 0 ) ) + return -1; + *dst->decConfigDescr = *src->decConfigDescr; + dst->decConfigDescr->decSpecificInfo = NULL; + return mp4sys_copy_DecoderSpecificInfo( dst, src ); +} + +static int mp4sys_copy_SLConfigDescriptor( mp4sys_ES_Descriptor_t *dst, mp4sys_ES_Descriptor_t *src ) +{ + if( !src || !dst + || mp4sys_remove_SLConfigDescriptor( dst ) ) + return -1; + if( !src->slConfigDescr ) + return 0; + if( mp4sys_add_SLConfigDescriptor( dst ) ) + return -1; + *dst->slConfigDescr = *src->slConfigDescr; + return 0; +} + +mp4sys_ES_Descriptor_t *mp4sys_duplicate_ES_Descriptor( mp4sys_ES_Descriptor_t *src ) +{ + if( !src ) + return NULL; + mp4sys_ES_Descriptor_t *dst = mp4sys_create_ES_Descriptor( 0 ); + if( !dst ) + return NULL; + *dst = *src; + dst->decConfigDescr = NULL; + dst->slConfigDescr = NULL; + if( mp4sys_copy_DecoderConfigDescriptor( dst, src ) + || mp4sys_copy_SLConfigDescriptor( dst, src ) ) + { + mp4sys_remove_ES_Descriptor( dst ); + return NULL; + } + return dst; +} + +static void mp4sys_get_descriptor_header( lsmash_bs_t *bs, mp4sys_descriptor_head_t* header ) +{ + header->tag = lsmash_bs_get_byte( bs ); + uint8_t temp = lsmash_bs_get_byte( bs ); + int nextByte = temp & 0x80; + uint32_t sizeOfInstance = temp & 0x7F; + while( nextByte ) + { + temp = lsmash_bs_get_byte( bs ); + nextByte = temp & 0x80; + sizeOfInstance = (sizeOfInstance << 7) | (temp & 0x7F); + } + header->size = sizeOfInstance; +} + +static int mp4sys_get_DecoderSpecificInfo( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) +{ + mp4sys_DecoderSpecificInfo_t *dsi = (mp4sys_DecoderSpecificInfo_t *)malloc( sizeof(mp4sys_DecoderSpecificInfo_t) ); + if( !dsi ) + return -1; + memset( dsi, 0, sizeof(mp4sys_DecoderSpecificInfo_t) ); + esd->decConfigDescr->decSpecificInfo = dsi; + mp4sys_get_descriptor_header( bs, &dsi->header ); + if( dsi->header.size ) + { + dsi->data = lsmash_bs_get_bytes( bs, dsi->header.size ); + if( !dsi->data ) + { + mp4sys_remove_DecoderSpecificInfo( esd ); + return -1; + } + } + return 0; +} + +static int mp4sys_get_DecoderConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) +{ + mp4sys_DecoderConfigDescriptor_t *dcd = (mp4sys_DecoderConfigDescriptor_t *)malloc( sizeof(mp4sys_DecoderConfigDescriptor_t) ); + if( !dcd ) + return -1; + memset( dcd, 0, sizeof(mp4sys_DecoderConfigDescriptor_t) ); + esd->decConfigDescr = dcd; + mp4sys_get_descriptor_header( bs, &dcd->header ); + dcd->objectTypeIndication = lsmash_bs_get_byte( bs ); + uint8_t temp = lsmash_bs_get_byte( bs ); + dcd->streamType = (temp >> 2) & 0x3F; + dcd->upStream = (temp >> 1) & 0x01; + dcd->reserved = temp & 0x01; + dcd->bufferSizeDB = lsmash_bs_get_be24( bs ); + dcd->maxBitrate = lsmash_bs_get_be32( bs ); + dcd->avgBitrate = lsmash_bs_get_be32( bs ); + if( dcd->header.size > 13 + && mp4sys_get_DecoderSpecificInfo( bs, esd ) ) + { + mp4sys_remove_DecoderConfigDescriptor( esd ); + return -1; + } + return 0; +} + +static int mp4sys_get_SLConfigDescriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t *esd ) +{ + mp4sys_SLConfigDescriptor_t *slcd = (mp4sys_SLConfigDescriptor_t *)malloc( sizeof(mp4sys_SLConfigDescriptor_t) ); + if( !slcd ) + return -1; + memset( slcd, 0, sizeof(mp4sys_SLConfigDescriptor_t) ); + esd->slConfigDescr = slcd; + mp4sys_get_descriptor_header( bs, &slcd->header ); + slcd->predefined = lsmash_bs_get_byte( bs ); + if( slcd->predefined == 0x00 ) + { + uint8_t temp8 = lsmash_bs_get_byte( bs ); + slcd->useAccessUnitStartFlag = (temp8 >> 7) & 0x01; + slcd->useAccessUnitEndFlag = (temp8 >> 6) & 0x01; + slcd->useRandomAccessPointFlag = (temp8 >> 5) & 0x01; + slcd->hasRandomAccessUnitsOnlyFlag = (temp8 >> 4) & 0x01; + slcd->usePaddingFlag = (temp8 >> 3) & 0x01; + slcd->useTimeStampsFlag = (temp8 >> 2) & 0x01; + slcd->useIdleFlag = (temp8 >> 1) & 0x01; + slcd->durationFlag = temp8 & 0x01; + slcd->timeStampResolution = lsmash_bs_get_be32( bs ); + slcd->OCRResolution = lsmash_bs_get_be32( bs ); + slcd->timeStampLength = lsmash_bs_get_byte( bs ); + slcd->OCRLength = lsmash_bs_get_byte( bs ); + slcd->AU_Length = lsmash_bs_get_byte( bs ); + slcd->instantBitrateLength = lsmash_bs_get_byte( bs ); + uint16_t temp16 = lsmash_bs_get_be16( bs ); + slcd->degradationPriorityLength = (temp16 >> 12) & 0x0F; + slcd->AU_seqNumLength = (temp16 >> 7) & 0x1F; + slcd->packetSeqNumLength = (temp16 >> 2) & 0x1F; + slcd->reserved = temp16 & 0x03; + } + else if( slcd->predefined == 0x01 ) + { + slcd->timeStampResolution = 1000; + slcd->timeStampLength = 32; + } + else if( slcd->predefined == 0x02 ) + slcd->useTimeStampsFlag = 1; + if( slcd->durationFlag ) + { + slcd->timeScale = lsmash_bs_get_be32( bs ); + slcd->accessUnitDuration = lsmash_bs_get_be16( bs ); + slcd->compositionUnitDuration = lsmash_bs_get_be16( bs ); + } + if( !slcd->useTimeStampsFlag ) + { + lsmash_bits_t *bits = lsmash_bits_create( bs ); + if( !bits ) + return -1; + slcd->startDecodingTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); + slcd->startCompositionTimeStamp = lsmash_bits_get( bits, slcd->timeStampLength ); + lsmash_bits_cleanup( bits ); + } + return 0; +} + +mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs ) +{ + mp4sys_ES_Descriptor_t *esd = (mp4sys_ES_Descriptor_t *)malloc( sizeof(mp4sys_ES_Descriptor_t) ); + if( !esd ) + return NULL; + memset( esd, 0, sizeof(mp4sys_ES_Descriptor_t) ); + mp4sys_get_descriptor_header( bs, &esd->header ); + esd->ES_ID = lsmash_bs_get_be16( bs ); + uint8_t temp = lsmash_bs_get_byte( bs ); + esd->streamDependenceFlag = (temp >> 7) & 0x01; + esd->URL_Flag = (temp >> 6) & 0x01; + esd->OCRstreamFlag = (temp >> 5) & 0x01; + esd->streamPriority = temp & 0x1F; + if( esd->streamDependenceFlag ) + esd->dependsOn_ES_ID = lsmash_bs_get_be16( bs ); + if( esd->URL_Flag ) + { + esd->URLlength = lsmash_bs_get_byte( bs ); + for( uint8_t i = 0; i < esd->URLlength; i++ ) + esd->URLstring[i] = lsmash_bs_get_byte( bs ); + } + if( esd->OCRstreamFlag ) + esd->OCR_ES_Id = lsmash_bs_get_be16( bs ); + if( mp4sys_get_DecoderConfigDescriptor( bs, esd ) + || mp4sys_get_SLConfigDescriptor( bs, esd ) ) + { + mp4sys_remove_ES_Descriptor( esd ); + return NULL; + } + return esd; +} +#endif /* LSMASH_DEMUXER_ENABLED */ + +/**** following functions are for facilitation purpose ****/ + +mp4sys_ES_Descriptor_t* mp4sys_setup_ES_Descriptor( mp4sys_ES_Descriptor_params_t* params ) +{ + if( !params ) + return NULL; + mp4sys_ES_Descriptor_t* esd = mp4sys_create_ES_Descriptor( params->ES_ID ); + if( !esd ) + return NULL; + if( mp4sys_add_SLConfigDescriptor( esd ) + || mp4sys_add_DecoderConfigDescriptor( esd, params->objectTypeIndication, params->streamType, params->bufferSizeDB, params->maxBitrate, params->avgBitrate ) + || ( params->dsi_payload && params->dsi_payload_length != 0 && mp4sys_add_DecoderSpecificInfo( esd, params->dsi_payload, params->dsi_payload_length ) ) + ){ + mp4sys_remove_ES_Descriptor( esd ); + return NULL; + } + return esd; +} diff --git a/output/mp4/mp4sys.h b/output/mp4/mp4sys.h new file mode 100644 index 0000000..60c67ed --- /dev/null +++ b/output/mp4/mp4sys.h @@ -0,0 +1,201 @@ +/***************************************************************************** + * 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 + +/*************************************************************************** + MPEG-4 Systems +***************************************************************************/ + +/* ODProfileLevelIndication */ +typedef enum { + MP4SYS_OD_PLI_Forbidden = 0x00, /* Forbidden */ + MP4SYS_OD_PLI_NOT_SPECIFIED = 0xFE, /* no OD profile specified */ + MP4SYS_OD_PLI_NONE_REQUIRED = 0xFF, /* no OD capability required */ +} mp4sys_ODProfileLevelIndication; + +/* sceneProfileLevelIndication */ +typedef enum { + MP4SYS_SCENE_PLI_RESERVED = 0x00, /* Reserved for ISO use */ + MP4SYS_SCENE_PLI_Simple2D_L1 = 0x01, /* Simple 2D L1 */ + MP4SYS_SCENE_PLI_Simple2D_L2 = 0x02, /* Simple 2D L2 */ + MP4SYS_SCENE_PLI_Audio_L1 = 0x03, /* Audio L1 */ + MP4SYS_SCENE_PLI_Audio_L2 = 0x04, /* Audio L2 */ + MP4SYS_SCENE_PLI_Audio_L3 = 0x05, /* Audio L3 */ + MP4SYS_SCENE_PLI_Audio_L4 = 0x06, /* Audio L4 */ + MP4SYS_SCENE_PLI_3D_Audio_L1 = 0x07, /* 3D Audio L1 */ + MP4SYS_SCENE_PLI_3D_Audio_L2 = 0x08, /* 3D Audio L2 */ + MP4SYS_SCENE_PLI_3D_Audio_L3 = 0x09, /* 3D Audio L3 */ + MP4SYS_SCENE_PLI_3D_Audio_L4 = 0x0A, /* 3D Audio L4 */ + MP4SYS_SCENE_PLI_Basic2D_L1 = 0x0B, /* Basic 2D L1 */ + MP4SYS_SCENE_PLI_Core2D_L1 = 0x0C, /* Core 2D L1 */ + MP4SYS_SCENE_PLI_Core2D_L2 = 0x0D, /* Core 2D L2 */ + MP4SYS_SCENE_PLI_Advanced2D_L1 = 0x0E, /* Advanced 2D L1 */ + MP4SYS_SCENE_PLI_Advanced2D_L2 = 0x0F, /* Advanced 2D L2 */ + MP4SYS_SCENE_PLI_Advanced2D_L3 = 0x10, /* Advanced 2D L3 */ + MP4SYS_SCENE_PLI_Main2D_L1 = 0x11, /* Main 2D L1 */ + MP4SYS_SCENE_PLI_Main2D_L2 = 0x12, /* Main 2D L2 */ + MP4SYS_SCENE_PLI_Main2D_L3 = 0x13, /* Main 2D L3 */ + MP4SYS_SCENE_PLI_NOT_SPECIFIED = 0xFE, /* no scene profile specified */ + MP4SYS_SCENE_PLI_NONE_REQUIRED = 0xFF, /* no scene capability required */ +} mp4sys_sceneProfileLevelIndication; + +/* 14496-2 Profile and level indication and restrictions */ +typedef enum { + MP4SYS_VISUAL_PLI_Reserved = 0x00, /* 0b00000000, Reserved */ + MP4SYS_VISUAL_PLI_Simple_PL1 = 0x01, /* 0b00000001, Simple Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Simple_PL2 = 0x02, /* 0b00000010, Simple Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Simple_PL3 = 0x03, /* 0b00000011, Simple Profile/Level 3 */ + MP4SYS_VISUAL_PLI_Simple_Scalable_PL1 = 0x11, /* 0b00010001, Simple Scalable Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Simple_Scalable_PL2 = 0x12, /* 0b00010010, Simple Scalable Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Core_PL1 = 0x21, /* 0b00100001, Core Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Core_PL2 = 0x22, /* 0b00100010, Core Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Main_PL2 = 0x32, /* 0b00110010, Main Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Main_PL3 = 0x33, /* 0b00110011, Main Profile/Level 3 */ + MP4SYS_VISUAL_PLI_Main_PL4 = 0x34, /* 0b00110100, Main Profile/Level 4 */ + MP4SYS_VISUAL_PLI_N_bit_PL2 = 0x42, /* 0b01000010, N-bit Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Scalable_Texture_PL1 = 0x51, /* 0b01010001, Scalable Texture Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Simple_Face_Animation_PL1 = 0x61, /* 0b01100001, Simple Face Animation Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Simple_Face_Animation_PL2 = 0x62, /* 0b01100010, Simple Face Animation Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Simple_FBA_PL1 = 0x63, /* 0b01100011, Simple FBA Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Simple_FBA_PL2 = 0x64, /* 0b01100100, Simple FBA Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Basic_Animated_Texture_PL1 = 0x71, /* 0b01110001, Basic Animated Texture Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Basic_Animated_Texture_PL2 = 0x72, /* 0b01110010, Basic Animated Texture Profile/Level 2 */ + MP4SYS_VISUAL_PLI_H264_AVC = 0x7F, /* ISO/IEC 14496-10 Advanced Video Codec / H.264, defined in ISO/IEC 14496-1:2001/Amd.7:2004 */ + /* NOTE: Some other implementations seem to use 0x15(0b00010101) for AVC, but I think that's wrong. */ + MP4SYS_VISUAL_PLI_Hybrid_PL1 = 0x81, /* 0b10000001, Hybrid Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Hybrid_PL2 = 0x82, /* 0b10000010, Hybrid Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL1 = 0x91, /* 0b10010001, Advanced Real Time Simple Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL2 = 0x92, /* 0b10010010, Advanced Real Time Simple Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL3 = 0x93, /* 0b10010011, Advanced Real Time Simple Profile/Level 3 */ + MP4SYS_VISUAL_PLI_Advanced_Real_Time_Simple_PL4 = 0x94, /* 0b10010100, Advanced Real Time Simple Profile/Level 4 */ + MP4SYS_VISUAL_PLI_Core_Scalable_PL1 = 0xA1, /* 0b10100001, Core Scalable Profile/Level1 */ + MP4SYS_VISUAL_PLI_Core_Scalable_PL2 = 0xA2, /* 0b10100010, Core Scalable Profile/Level2 */ + MP4SYS_VISUAL_PLI_Core_Scalable_PL3 = 0xA3, /* 0b10100011, Core Scalable Profile/Level3 */ + MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL1 = 0xB1, /* 0b10110001, Advanced Coding Efficiency Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL2 = 0xB2, /* 0b10110010, Advanced Coding Efficiency Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL3 = 0xB3, /* 0b10110011, Advanced Coding Efficiency Profile/Level 3 */ + MP4SYS_VISUAL_PLI_Advanced_Coding_Efficiency_PL4 = 0xB4, /* 0b10110100, Advanced Coding Efficiency Profile/Level 4 */ + MP4SYS_VISUAL_PLI_Advanced_Core_PL1 = 0xC1, /* 0b11000001, Advanced Core Profile/Level 1 */ + MP4SYS_VISUAL_PLI_Advanced_Core_PL2 = 0xC2, /* 0b11000010, Advanced Core Profile/Level 2 */ + MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L1 = 0xD1, /* 0b11010001, Advanced Scalable Texture/Level1 */ + MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L2 = 0xD2, /* 0b11010010, Advanced Scalable Texture/Level2 */ + MP4SYS_VISUAL_PLI_Advanced_Scalable_Texture_L3 = 0xD3, /* 0b11010011, Advanced Scalable Texture/Level3 */ + MP4SYS_VISUAL_PLI_NOT_SPECIFIED = 0xFE, /* no visual profile specified */ + MP4SYS_VISUAL_PLI_NONE_REQUIRED = 0xFF, /* no visual capability required */ +} mp4sys_visualProfileLevelIndication; + +/* graphicsProfileLevelIndication */ +typedef enum { + MP4SYS_GRAPHICS_PLI_RESERVED = 0x00, /* Reserved for ISO use */ + MP4SYS_GRAPHICS_PLI_Simple2D_L1 = 0x01, /* Simple2D profile L1 */ + MP4SYS_GRAPHICS_PLI_Simple2D_Text_L1 = 0x02, /* Simple 2D + Text profile L1 */ + MP4SYS_GRAPHICS_PLI_Simple2D_Text_L2 = 0x03, /* Simple 2D + Text profile L2 */ + MP4SYS_GRAPHICS_PLI_Core2D_L1 = 0x04, /* Core 2D profile L1 */ + MP4SYS_GRAPHICS_PLI_Core2D_L2 = 0x05, /* Core 2D profile L2 */ + MP4SYS_GRAPHICS_PLI_Advanced2D_L1 = 0x06, /* Advanced 2D profile L1 */ + MP4SYS_GRAPHICS_PLI_Advanced2D_L2 = 0x07, /* Advanced 2D profile L2 */ + MP4SYS_GRAPHICS_PLI_NOT_SPECIFIED = 0xFE, /* no graphics profile specified */ + MP4SYS_GRAPHICS_PLI_NONE_REQUIRED = 0xFF, /* no graphics capability required */ +} mp4sys_graphicsProfileLevelIndication; + +/* Just for mp4sys_setup_ES_Descriptor, to facilitate to make ES_Descriptor */ +typedef struct { + uint16_t ES_ID; /* Maybe 0 in stsd(esds), or alternatively, lower 16 bits of the TrackID */ + lsmash_mp4sys_object_type_indication objectTypeIndication; + lsmash_mp4sys_stream_type streamType; + uint32_t bufferSizeDB; /* byte unit, NOT bit unit. */ + uint32_t maxBitrate; + uint32_t avgBitrate; /* 0 if VBR */ + void* dsi_payload; /* AudioSpecificConfig or so */ + uint32_t dsi_payload_length ; /* size of dsi_payload */ +} mp4sys_ES_Descriptor_params_t; + +#ifndef MP4SYS_INTERNAL + +#include "utils.h" + +typedef void mp4sys_ES_Descriptor_t; +typedef void mp4sys_ObjectDescriptor_t; + +int mp4sys_remove_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd ); +int mp4sys_remove_DecoderConfigDescriptor( mp4sys_ES_Descriptor_t* esd ); +int mp4sys_remove_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd ); +int mp4sys_remove_ES_Descriptor( mp4sys_ES_Descriptor_t* esd ); +int mp4sys_remove_ES_ID_Incs( mp4sys_ObjectDescriptor_t* od ); +int mp4sys_remove_ObjectDescriptor( mp4sys_ObjectDescriptor_t* od ); + +int mp4sys_add_DecoderSpecificInfo( mp4sys_ES_Descriptor_t* esd, void* dsi_payload, uint32_t dsi_payload_length ); +/* + bufferSizeDB is byte unit, NOT bit unit. + avgBitrate is 0 if VBR +*/ +int mp4sys_add_DecoderConfigDescriptor( + mp4sys_ES_Descriptor_t* esd, + lsmash_mp4sys_object_type_indication objectTypeIndication, + lsmash_mp4sys_stream_type streamType, + uint32_t bufferSizeDB, + uint32_t maxBitrate, + uint32_t avgBitrate +); +int mp4sys_add_SLConfigDescriptor( mp4sys_ES_Descriptor_t* esd ); +int mp4sys_add_ES_ID_Inc( mp4sys_ObjectDescriptor_t* od, uint32_t Track_ID); + +/* ES_ID might be usually 0 or lower 16 bits of the TrackID + 14496-14 says, "set to 0 as stored; when built into a stream, the lower 16 bits of the TrackID are used." + I'm not sure about actual meaning of "stored" and "built into a stream", but maybe 0 will do in stsd(esds). */ +mp4sys_ES_Descriptor_t* mp4sys_create_ES_Descriptor( uint16_t ES_ID ); +mp4sys_ObjectDescriptor_t* mp4sys_create_ObjectDescriptor( uint16_t ObjectDescriptorID ); +int mp4sys_to_InitialObjectDescriptor( + mp4sys_ObjectDescriptor_t* od, + uint8_t include_inline_pli, + mp4sys_ODProfileLevelIndication od_pli, + mp4sys_sceneProfileLevelIndication scene_pli, + mp4a_audioProfileLevelIndication audio_pli, + mp4sys_visualProfileLevelIndication visual_pli, + mp4sys_graphicsProfileLevelIndication graph_pli +); + +uint32_t mp4sys_update_ES_Descriptor_size( mp4sys_ES_Descriptor_t* esd ); +uint32_t mp4sys_update_ObjectDescriptor_size( mp4sys_ObjectDescriptor_t* od ); + +int mp4sys_write_ES_Descriptor( lsmash_bs_t *bs, mp4sys_ES_Descriptor_t* esd ); +int mp4sys_write_ObjectDescriptor( lsmash_bs_t *bs, mp4sys_ObjectDescriptor_t* od ); + +int mp4sys_update_DecoderConfigDescriptor( + mp4sys_ES_Descriptor_t* esd, + uint32_t bufferSizeDB, + uint32_t maxBitrate, + uint32_t avgBitrate +); + +mp4sys_ES_Descriptor_t *mp4sys_duplicate_ES_Descriptor( mp4sys_ES_Descriptor_t *src ); + +mp4sys_ES_Descriptor_t *mp4sys_get_ES_Descriptor( lsmash_bs_t *bs ); + +/* 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..64e4d00 --- /dev/null +++ b/output/mp4/summary.c @@ -0,0 +1,105 @@ +/***************************************************************************** + * 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 "internal.h" /* must be placed first */ + +#include +#include + +#include "mp4a.h" + +/*************************************************************************** + summary and AudioSpecificConfig relative tools +***************************************************************************/ + +/* create AudioSpecificConfig as memory block from summary, and set it into that summary itself */ +int lsmash_setup_AudioSpecificConfig( lsmash_audio_summary_t* summary ) +{ + if( !summary ) + return -1; + lsmash_bs_t* bs = lsmash_bs_create( NULL ); /* no file writing */ + if( !bs ) + return -1; + mp4a_AudioSpecificConfig_t* asc = mp4a_create_AudioSpecificConfig( summary->aot, summary->frequency, summary->channels, summary->sbr_mode ); + if( !asc ) + { + lsmash_bs_cleanup( bs ); + return -1; + } + + mp4a_put_AudioSpecificConfig( bs, asc ); + void* new_asc; + uint32_t new_length; + new_asc = lsmash_bs_export_data( bs, &new_length ); + mp4a_remove_AudioSpecificConfig( asc ); + lsmash_bs_cleanup( bs ); + + if( !new_asc ) + return -1; + if( summary->exdata ) + free( summary->exdata ); + summary->exdata = new_asc; + summary->exdata_length = new_length; + return 0 ; +} + +/* Copy exdata into summary from memory block */ +int lsmash_summary_add_exdata( lsmash_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; +} + +lsmash_audio_summary_t* lsmash_create_audio_summary() +{ + 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) ); + return summary; +} + +void lsmash_cleanup_audio_summary( lsmash_audio_summary_t* summary ) +{ + if( !summary ) + return; + if( summary->exdata ) + free( summary->exdata ); + free( summary ); +} diff --git a/output/mp4/utils.c b/output/mp4/utils.c new file mode 100644 index 0000000..98ece81 --- /dev/null +++ b/output/mp4/utils.c @@ -0,0 +1,664 @@ +/***************************************************************************** + * 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 "internal.h" /* must be placed first */ + +#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) ); +} + +void lsmash_bs_put_byte_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_byte( bs, (uint8_t)(value&0xff) ); +} + +void lsmash_bs_put_be16_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be16( bs, (uint16_t)(value&0xffff) ); +} + +void lsmash_bs_put_be24_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be24( bs, (uint32_t)(value&0xffffff) ); +} + +void lsmash_bs_put_be32_from_64( lsmash_bs_t *bs, uint64_t value ) +{ + lsmash_bs_put_be32( bs, (uint32_t)(value&0xffffffff) ); +} + +int lsmash_bs_write_data( lsmash_bs_t *bs ) +{ + if( !bs ) + return -1; + if( !bs->store || !bs->data ) + return 0; + if( bs->error || !bs->stream || fwrite( bs->data, 1, bs->store, bs->stream ) != bs->store ) + { + lsmash_bs_free( bs ); + bs->error = 1; + return -1; + } + bs->written += bs->store; + bs->store = 0; + return 0; +} + +lsmash_bs_t* lsmash_bs_create( char* filename ) +{ + lsmash_bs_t* bs = 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_byte( bs ); + return (value<<16) | lsmash_bs_get_be16( bs ); +} + +uint32_t lsmash_bs_get_be32( lsmash_bs_t *bs ) +{ + uint32_t value = lsmash_bs_get_be16( bs ); + return (value<<16) | lsmash_bs_get_be16( bs ); +} + +uint64_t lsmash_bs_get_be64( lsmash_bs_t *bs ) +{ + uint64_t value = lsmash_bs_get_be32( bs ); + return (value<<32) | lsmash_bs_get_be32( bs ); +} + +uint64_t lsmash_bs_get_byte_to_64( lsmash_bs_t *bs ) +{ + return lsmash_bs_get_byte( bs ); +} + +uint64_t lsmash_bs_get_be16_to_64( lsmash_bs_t *bs ) +{ + return lsmash_bs_get_be16( bs ); +} + +uint64_t lsmash_bs_get_be24_to_64( lsmash_bs_t *bs ) +{ + return lsmash_bs_get_be24( bs ); +} + +uint64_t lsmash_bs_get_be32_to_64( lsmash_bs_t *bs ) +{ + return lsmash_bs_get_be32( bs ); +} + +int lsmash_bs_read_data( lsmash_bs_t *bs, uint32_t size ) +{ + if( !bs ) + return -1; + if( !size ) + return 0; + lsmash_bs_alloc( bs, bs->store + size ); + if( bs->error || !bs->stream ) + { + lsmash_bs_free( bs ); + bs->error = 1; + return -1; + } + uint64_t read_size = fread( bs->data + bs->store, 1, size, bs->stream ); + if( read_size != size && !feof( bs->stream ) ) + { + bs->error = 1; + return -1; + } + bs->store += read_size; + return 0; +} + +int lsmash_bs_import_data( lsmash_bs_t *bs, void* data, uint32_t length ) +{ + if( !bs || bs->error || !data || length == 0 ) + return -1; + lsmash_bs_alloc( bs, bs->store + length ); + if( bs->error || !bs->data ) /* means, failed to alloc. */ + { + lsmash_bs_free( bs ); + return -1; + } + memcpy( bs->data + bs->store, data, length ); + bs->store += length; + return 0; +} +/*---- ----*/ + +/*---- bitstream ----*/ +void lsmash_bits_init( lsmash_bits_t* bits, lsmash_bs_t *bs ) +{ + debug_if( !bits || !bs ) + return; + bits->bs = bs; + bits->store = 0; + bits->cache = 0; +} + +lsmash_bits_t* lsmash_bits_create( lsmash_bs_t *bs ) +{ + debug_if( !bs ) + return NULL; + lsmash_bits_t* bits = (lsmash_bits_t*)malloc( sizeof(lsmash_bits_t) ); + if( !bits ) + return NULL; + lsmash_bits_init( bits, bs ); + return bits; +} + +#define BITS_IN_BYTE 8 +void lsmash_bits_put_align( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + if( !bits->store ) + return; + lsmash_bs_put_byte( bits->bs, bits->cache << ( BITS_IN_BYTE - bits->store ) ); +} + +void lsmash_bits_get_align( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + bits->store = 0; + bits->cache = 0; +} + +/* Must be used ONLY for bits struct created with isom_create_bits. + Otherwise, just free() the bits struct. */ +void lsmash_bits_cleanup( lsmash_bits_t *bits ) +{ + debug_if( !bits ) + return; + free( bits ); +} + +/* we can change value's type to unsigned int for 64-bit operation if needed. */ +static inline uint8_t lsmash_bits_mask_lsb8( uint32_t value, uint32_t width ) +{ + return (uint8_t)( value & ~( ~0U << width ) ); +} + +/* We can change value's type to unsigned int for 64-bit operation if needed. */ +void lsmash_bits_put( lsmash_bits_t *bits, uint32_t value, uint32_t width ) +{ + debug_if( !bits || !width ) + return; + if( bits->store ) + { + if( bits->store + width < BITS_IN_BYTE ) + { + /* cache can contain all of value's bits. */ + bits->cache <<= width; + bits->cache |= lsmash_bits_mask_lsb8( value, width ); + bits->store += width; + return; + } + /* flush cache with value's some leading bits. */ + uint32_t free_bits = BITS_IN_BYTE - bits->store; + bits->cache <<= free_bits; + bits->cache |= lsmash_bits_mask_lsb8( value >> (width -= free_bits), free_bits ); + lsmash_bs_put_byte( bits->bs, bits->cache ); + bits->store = 0; + bits->cache = 0; + } + /* cache is empty here. */ + /* byte unit operation. */ + while( width > BITS_IN_BYTE ) + lsmash_bs_put_byte( bits->bs, (uint8_t)(value >> (width -= BITS_IN_BYTE)) ); + /* bit unit operation for residual. */ + if( width ) + { + bits->cache = lsmash_bits_mask_lsb8( value, width ); + bits->store = width; + } +} + +/* We can change value's type to unsigned int for 64-bit operation if needed. */ +uint32_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width ) +{ + debug_if( !bits || !width ) + return 0; + uint32_t value = 0; + if( bits->store ) + { + if( bits->store >= width ) + { + /* cache contains all of bits required. */ + bits->store -= width; + return lsmash_bits_mask_lsb8( bits->cache >> bits->store, width ); + } + /* fill value's leading bits with cache's residual. */ + value = lsmash_bits_mask_lsb8( bits->cache, bits->store ); + width -= bits->store; + bits->store = 0; + bits->cache = 0; + } + /* cache is empty here. */ + /* byte unit operation. */ + while( width > BITS_IN_BYTE ) + { + value <<= BITS_IN_BYTE; + width -= BITS_IN_BYTE; + value |= lsmash_bs_get_byte( bits->bs ); + } + /* bit unit operation for residual. */ + if( width ) + { + bits->cache = lsmash_bs_get_byte( bits->bs ); + bits->store = BITS_IN_BYTE - width; + value <<= width; + value |= lsmash_bits_mask_lsb8( bits->cache >> bits->store, width ); + } + return value; +} + +/**** + bitstream with bytestream for adhoc operation +****/ + +lsmash_bits_t* lsmash_bits_adhoc_create() +{ + lsmash_bs_t* bs = lsmash_bs_create( NULL ); /* no file writing */ + if( !bs ) + return NULL; + lsmash_bits_t* bits = lsmash_bits_create( bs ); + if( !bits ) + { + lsmash_bs_cleanup( bs ); + return NULL; + } + return bits; +} + +void lsmash_bits_adhoc_cleanup( lsmash_bits_t* bits ) +{ + if( !bits ) + return; + lsmash_bs_cleanup( bits->bs ); + lsmash_bits_cleanup( bits ); +} + +void* lsmash_bits_export_data( lsmash_bits_t* bits, uint32_t* length ) +{ + lsmash_bits_put_align( bits ); + return lsmash_bs_export_data( bits->bs, length ); +} + +int lsmash_bits_import_data( lsmash_bits_t* bits, void* data, uint32_t length ) +{ + return lsmash_bs_import_data( bits->bs, data, length ); +} +/*---- ----*/ + +/*---- list ----*/ +void lsmash_init_entry_list( lsmash_entry_list_t *list ) +{ + list->head = NULL; + list->tail = NULL; + list->last_accessed_entry = NULL; + list->last_accessed_number = 0; + list->entry_count = 0; +} + +lsmash_entry_list_t *lsmash_create_entry_list( void ) +{ + lsmash_entry_list_t *list = malloc( sizeof(lsmash_entry_list_t) ); + if( !list ) + return NULL; + lsmash_init_entry_list( list ); + return list; +} + +int lsmash_add_entry( lsmash_entry_list_t *list, void *data ) +{ + if( !list ) + return -1; + lsmash_entry_t *entry = malloc( sizeof(lsmash_entry_t) ); + if( !entry ) + return -1; + entry->next = NULL; + entry->prev = list->tail; + entry->data = data; + if( list->head ) + list->tail->next = entry; + else + list->head = entry; + list->tail = entry; + list->entry_count += 1; + return 0; +} + +int lsmash_remove_entry_direct( lsmash_entry_list_t *list, lsmash_entry_t *entry, void* eliminator ) +{ + if( !entry ) + return -1; + if( !eliminator ) + eliminator = free; + lsmash_entry_t *next = entry->next; + lsmash_entry_t *prev = entry->prev; + if( entry == list->head ) + list->head = next; + else + prev->next = next; + if( entry == list->tail ) + list->tail = prev; + else + next->prev = prev; + if( entry->data ) + ((lsmash_entry_data_eliminator)eliminator)( entry->data ); + if( entry == list->last_accessed_entry ) + { + if( next ) + list->last_accessed_entry = next; + else if( prev ) + { + list->last_accessed_entry = prev; + list->last_accessed_number -= 1; + } + else + { + list->last_accessed_entry = NULL; + list->last_accessed_number = 0; + } + } + else + { + /* We can't know the current entry number immediately, + * so discard the last accessed entry info because time is wasted to know it. */ + list->last_accessed_entry = NULL; + list->last_accessed_number = 0; + } + free( entry ); + list->entry_count -= 1; + return 0; +} + +int lsmash_remove_entry( lsmash_entry_list_t *list, uint32_t entry_number, void* eliminator ) +{ + lsmash_entry_t *entry = lsmash_get_entry( list, entry_number ); + return lsmash_remove_entry_direct( list, entry, eliminator ); +} + +void lsmash_remove_entries( lsmash_entry_list_t *list, void* eliminator ) +{ + if( !list ) + return; + if( !eliminator ) + eliminator = free; + for( lsmash_entry_t *entry = list->head; entry; ) + { + lsmash_entry_t *next = entry->next; + if( entry->data ) + ((lsmash_entry_data_eliminator)eliminator)( entry->data ); + free( entry ); + entry = next; + } + lsmash_init_entry_list( list ); +} + +void lsmash_remove_list( lsmash_entry_list_t *list, void* eliminator ) +{ + if( !list ) + return; + lsmash_remove_entries( list, eliminator ); + free( list ); +} + +lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number ) +{ + if( !list || !entry_number || entry_number > list->entry_count ) + return NULL; + int shortcut = 1; + lsmash_entry_t *entry = NULL; + if( list->last_accessed_entry ) + { + if( entry_number == list->last_accessed_number ) + entry = list->last_accessed_entry; + else if( entry_number == list->last_accessed_number + 1 ) + entry = list->last_accessed_entry->next; + else if( entry_number == list->last_accessed_number - 1 ) + entry = list->last_accessed_entry->prev; + else + shortcut = 0; + } + else + shortcut = 0; + if( !shortcut ) + { + if( entry_number <= (list->entry_count >> 1) ) + { + /* Look for from the head. */ + uint32_t distance_plus_one = entry_number; + for( entry = list->head; entry && --distance_plus_one; entry = entry->next ); + } + else + { + /* Look for from the tail. */ + uint32_t distance = list->entry_count - entry_number; + for( entry = list->tail; entry && distance--; entry = entry->prev ); + } + } + if( entry ) + { + list->last_accessed_entry = entry; + list->last_accessed_number = entry_number; + } + return entry; +} + +void *lsmash_get_entry_data( lsmash_entry_list_t *list, uint32_t entry_number ) +{ + lsmash_entry_t *entry = lsmash_get_entry( list, entry_number ); + return entry ? entry->data : NULL; +} +/*---- ----*/ + +/*---- type ----*/ +double lsmash_fixed2double( uint64_t value, int frac_width ) +{ + return value / (double)(1ULL << frac_width); +} + +float lsmash_int2float32( uint32_t value ) +{ + return (union {uint32_t i; float f;}){value}.f; +} + +double lsmash_int2float64( uint64_t value ) +{ + return (union {uint64_t i; double d;}){value}.d; +} +/*---- ----*/ + +/*---- allocator ----*/ +void *lsmash_memdup( void *src, size_t size ) +{ + if( !size ) + return NULL; + void *dst = malloc( size ); + if( !dst ) + return NULL; + memcpy( dst, src, size ); + return dst; +} diff --git a/output/mp4/utils.h b/output/mp4/utils.h new file mode 100644 index 0000000..4a4df11 --- /dev/null +++ b/output/mp4/utils.h @@ -0,0 +1,142 @@ +/***************************************************************************** + * 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 + +#define debug_if(x) if(x) + +#define LSMASH_MAX( a, b ) ((a) > (b) ? (a) : (b)) +#define LSMASH_MIN( a, b ) ((a) < (b) ? (a) : (b)) + +/*---- bytestream ----*/ + +typedef struct +{ + FILE *stream; /* I/O stream */ + uint8_t error; + uint8_t *data; /* buffer for reading/writing */ + uint64_t store; /* valid data size on buffer */ + uint64_t alloc; /* total buffer size including invalid area */ + uint64_t pos; /* data position on buffer to be read next */ + uint64_t written; /* data size written into "stream" already */ +} lsmash_bs_t; + +uint64_t lsmash_bs_get_pos( lsmash_bs_t *bs ); +void lsmash_bs_empty( lsmash_bs_t *bs ); +void lsmash_bs_free( lsmash_bs_t *bs ); +void lsmash_bs_alloc( lsmash_bs_t *bs, uint64_t size ); +lsmash_bs_t* lsmash_bs_create( char* filename ); +void lsmash_bs_cleanup( lsmash_bs_t *bs ); + +/*---- bytestream writer ----*/ + +void lsmash_bs_put_byte( lsmash_bs_t *bs, uint8_t value ); +void lsmash_bs_put_bytes( lsmash_bs_t *bs, void *value, uint32_t size ); +void lsmash_bs_put_be16( lsmash_bs_t *bs, uint16_t value ); +void lsmash_bs_put_be24( lsmash_bs_t *bs, uint32_t value ); +void lsmash_bs_put_be32( lsmash_bs_t *bs, uint32_t value ); +void lsmash_bs_put_be64( lsmash_bs_t *bs, uint64_t value ); +void lsmash_bs_put_byte_from_64( lsmash_bs_t *bs, uint64_t value ); +void lsmash_bs_put_be16_from_64( lsmash_bs_t *bs, uint64_t value ); +void lsmash_bs_put_be24_from_64( lsmash_bs_t *bs, uint64_t value ); +void lsmash_bs_put_be32_from_64( lsmash_bs_t *bs, uint64_t value ); +int lsmash_bs_write_data( lsmash_bs_t *bs ); + +void* lsmash_bs_export_data( lsmash_bs_t *bs, uint32_t* length ); + +/*---- bytestream reader ----*/ +uint8_t lsmash_bs_get_byte( lsmash_bs_t *bs ); +uint8_t *lsmash_bs_get_bytes( lsmash_bs_t *bs, uint32_t size ); +uint16_t lsmash_bs_get_be16( lsmash_bs_t *bs ); +uint32_t lsmash_bs_get_be24( lsmash_bs_t *bs ); +uint32_t lsmash_bs_get_be32( lsmash_bs_t *bs ); +uint64_t lsmash_bs_get_be64( lsmash_bs_t *bs ); +uint64_t lsmash_bs_get_byte_to_64( lsmash_bs_t *bs ); +uint64_t lsmash_bs_get_be16_to_64( lsmash_bs_t *bs ); +uint64_t lsmash_bs_get_be24_to_64( lsmash_bs_t *bs ); +uint64_t lsmash_bs_get_be32_to_64( lsmash_bs_t *bs ); +int lsmash_bs_read_data( lsmash_bs_t *bs, uint32_t size ); + +/*---- bitstream ----*/ +typedef struct { + lsmash_bs_t* bs; + uint8_t store; + uint8_t cache; +} lsmash_bits_t; + +void lsmash_bits_init( lsmash_bits_t* bits, lsmash_bs_t *bs ); +lsmash_bits_t* lsmash_bits_create( lsmash_bs_t *bs ); +void lsmash_bits_put_align( lsmash_bits_t *bits ); +void lsmash_bits_get_align( lsmash_bits_t *bits ); +void lsmash_bits_cleanup( lsmash_bits_t *bits ); + +/*---- bitstream writer ----*/ +void lsmash_bits_put( lsmash_bits_t *bits, uint32_t value, uint32_t width ); +uint32_t lsmash_bits_get( lsmash_bits_t *bits, uint32_t width ); +lsmash_bits_t* lsmash_bits_adhoc_create(); +void lsmash_bits_adhoc_cleanup( lsmash_bits_t* bits ); +void* lsmash_bits_export_data( lsmash_bits_t* bits, uint32_t* length ); +int lsmash_bits_import_data( lsmash_bits_t* bits, void* data, uint32_t length ); + +/*---- list ----*/ + +typedef struct lsmash_entry_tag lsmash_entry_t; + +struct lsmash_entry_tag +{ + lsmash_entry_t *next; + lsmash_entry_t *prev; + void *data; +}; + +typedef struct +{ + lsmash_entry_t *head; + lsmash_entry_t *tail; + lsmash_entry_t *last_accessed_entry; + uint32_t last_accessed_number; + uint32_t entry_count; +} lsmash_entry_list_t; + +typedef void (*lsmash_entry_data_eliminator)(void* data); /* very same as free() of standard c lib; void free(void *); */ + +void lsmash_init_entry_list( lsmash_entry_list_t *list ); +lsmash_entry_list_t *lsmash_create_entry_list( void ); +int lsmash_add_entry( lsmash_entry_list_t *list, void *data ); +int lsmash_remove_entry_direct( lsmash_entry_list_t *list, lsmash_entry_t *entry, void* eliminator ); +int lsmash_remove_entry( lsmash_entry_list_t *list, uint32_t entry_number, void* eliminator ); +void lsmash_remove_entries( lsmash_entry_list_t *list, void* eliminator ); +void lsmash_remove_list( lsmash_entry_list_t *list, void* eliminator ); + +lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number ); +void *lsmash_get_entry_data( lsmash_entry_list_t *list, uint32_t entry_number ); + +/*---- type ----*/ +double lsmash_fixed2double( uint64_t value, int frac_width ); +float lsmash_int2float32( uint32_t value ); +double lsmash_int2float64( uint64_t value ); + +/*---- allocator ----*/ +void *lsmash_memdup( void *src, size_t size ); + +#endif diff --git a/x264.c b/x264.c index 41cb853..4e37ebf 100644 --- a/x264.c +++ b/x264.c @@ -113,9 +113,7 @@ static const char * const muxer_names[] = "raw", "mkv", "flv", -#if HAVE_GPAC "mp4", -#endif 0 }; @@ -371,7 +369,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" @@ -396,11 +397,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" ); @@ -980,9 +976,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; @@ -991,10 +987,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