From f8470ba70a2a91fe598002d62fcb6a912b9c60d8 Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Mon, 31 Aug 2015 01:34:57 +0900 Subject: [PATCH] Add support of muxing and demuxing of Compact Sample Size Box ('stz2'). --- cli/muxer.c | 7 +++- cli/remuxer.c | 13 +++++-- common/list.c | 6 +++ common/list.h | 1 + core/box.c | 3 ++ core/box.h | 38 +++++++++++++++--- core/file.c | 2 +- core/fragment.c | 16 +++++--- core/isom.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++-------- core/print.c | 18 +++++++++ core/read.c | 69 ++++++++++++++++++++++++++++++++ core/timeline.c | 12 +++--- core/write.c | 43 ++++++++++++++++++++ lsmash.h | 8 +++- 14 files changed, 313 insertions(+), 42 deletions(-) diff --git a/cli/muxer.c b/cli/muxer.c index 8f06346..9838b7b 100644 --- a/cli/muxer.c +++ b/cli/muxer.c @@ -64,6 +64,7 @@ typedef struct int brand_3gx; int optimize_pd; int timeline_shift; + int compact_size_table; uint32_t interleave; uint32_t num_of_brands; uint32_t brands[MAX_NUM_OF_BRANDS]; @@ -254,6 +255,7 @@ static void display_help( void ) " is or /\n" " --language Specify the default language for all the output tracks.\n" " This option is overridden by the track options.\n" + " --compact-size-table Compress sample size tables if possible.\n" "Output file formats:\n" " mp4, mov, 3gp, 3g2, m4a, m4v\n" "\n" @@ -514,6 +516,8 @@ static int parse_global_options( int argc, char **argv, muxer_t *muxer ) } else if( !strcasecmp( argv[i], "--shift-timeline" ) ) opt->timeline_shift = 1; + else if( !strcasecmp( argv[i], "compact-size-table" ) ) + opt->compact_size_table = 1; else if( !strcasecmp( argv[i], "--chapter" ) ) { CHECK_NEXT_ARG; @@ -896,7 +900,8 @@ static int prepare_output( muxer_t *muxer ) track_param.alternate_group = track_opt->alternate_group; lsmash_media_parameters_t media_param; lsmash_initialize_media_parameters( &media_param ); - media_param.ISO_language = track_opt->ISO_language; + media_param.ISO_language = track_opt->ISO_language; + media_param.compact_sample_size_table = opt->compact_size_table; switch( in_track->summary->summary_type ) { case LSMASH_SUMMARY_TYPE_VIDEO : diff --git a/cli/remuxer.c b/cli/remuxer.c index 1336d44..c1b6f40 100644 --- a/cli/remuxer.c +++ b/cli/remuxer.c @@ -152,6 +152,7 @@ typedef struct uint32_t frag_base_track; uint32_t subseg_per_seg; int dash; + int compact_size_table; } remuxer_t; typedef struct @@ -302,6 +303,7 @@ static void display_help( void ) " The value is the number of subsegments per segment.\n" " If zero, Indexed self-initializing Media Segment.\n" " This option requires --fragment.\n" + " --compact-size-table Compress sample size tables if possible.\n" "Track options:\n" " remove Remove this track\n" " disable Disable this track\n" @@ -725,6 +727,8 @@ static int parse_cli_option( int argc, char **argv, remuxer_t *remuxer ) remuxer->subseg_per_seg = atoi( argv[i] ); remuxer->dash = 1; } + else if( !strcasecmp( argv[i], "--compact-size-table" ) ) + remuxer->compact_size_table = 1; else FAILED_PARSE_CLI_OPTION( "unkown option found: %s\n", argv[i] ); } @@ -1159,10 +1163,11 @@ static int prepare_output( remuxer_t *remuxer ) out_track->track_param = in_track->track_param; out_track->media_param = in_track->media.param; /* Set track and media parameters specified by users */ - out_track->track_param.alternate_group = current_track_opt->alternate_group; - out_track->media_param.ISO_language = current_track_opt->ISO_language; - out_track->media_param.media_handler_name = current_track_opt->handler_name; - out_track->track_param.track_ID = out_track->track_ID; + out_track->track_param.alternate_group = current_track_opt->alternate_group; + out_track->media_param.ISO_language = current_track_opt->ISO_language; + out_track->media_param.media_handler_name = current_track_opt->handler_name; + out_track->media_param.compact_sample_size_table = remuxer->compact_size_table; + out_track->track_param.track_ID = out_track->track_ID; if( current_track_opt->disable ) out_track->track_param.mode &= ~ISOM_TRACK_ENABLED; if( lsmash_set_track_parameters( output->root, out_track->track_ID, &out_track->track_param ) < 0 ) diff --git a/common/list.c b/common/list.c index 76afad3..87622bc 100644 --- a/common/list.c +++ b/common/list.c @@ -142,6 +142,12 @@ void lsmash_remove_list_orig( lsmash_entry_list_t *list, lsmash_entry_data_elimi lsmash_free( list ); } +void lsmash_move_entries( lsmash_entry_list_t *dst, lsmash_entry_list_t *src ) +{ + *dst = *src; + lsmash_init_entry_list( src ); +} + lsmash_entry_t *lsmash_get_entry( lsmash_entry_list_t *list, uint32_t entry_number ) { if( !list || !entry_number || entry_number > list->entry_count ) diff --git a/common/list.h b/common/list.h index 233dd01..de3bc78 100644 --- a/common/list.h +++ b/common/list.h @@ -63,6 +63,7 @@ int lsmash_remove_entry_orig( lsmash_entry_list_t *list, uint32_t entry_number, int lsmash_remove_entry_tail_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ); void lsmash_remove_entries_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ); void lsmash_remove_list_orig( lsmash_entry_list_t *list, lsmash_entry_data_eliminator eliminator ); +void lsmash_move_entries( lsmash_entry_list_t *dst, lsmash_entry_list_t *src ); 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 ); diff --git a/core/box.c b/core/box.c index f1da57f..cab613c 100644 --- a/core/box.c +++ b/core/box.c @@ -193,6 +193,7 @@ int isom_is_fullbox( void *box ) fullbox_type_table[i++] = ISOM_BOX_TYPE_SDTP; fullbox_type_table[i++] = ISOM_BOX_TYPE_STSC; fullbox_type_table[i++] = ISOM_BOX_TYPE_STSZ; + fullbox_type_table[i++] = ISOM_BOX_TYPE_STZ2; fullbox_type_table[i++] = ISOM_BOX_TYPE_STCO; fullbox_type_table[i++] = ISOM_BOX_TYPE_CO64; fullbox_type_table[i++] = ISOM_BOX_TYPE_SGPD; @@ -896,6 +897,7 @@ DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_ctts, ctts, isom_stbl_t ) DEFINE_SIMPLE_BOX_REMOVER( isom_remove_cslg, cslg, isom_stbl_t ) DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stsc, stsc, isom_stbl_t ) DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stsz, stsz, isom_stbl_t ) +DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stz2, stz2, isom_stbl_t ) DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stss, stss, isom_stbl_t ) DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stps, stps, isom_stbl_t ) DEFINE_SIMPLE_LIST_BOX_REMOVER( isom_remove_stco, stco, isom_stbl_t ) @@ -1504,6 +1506,7 @@ DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_ctts, ctts, stbl, ISOM_BOX_TYPE_CTTS, LSM DEFINE_SIMPLE_BOX_ADDER ( isom_add_cslg, cslg, stbl, ISOM_BOX_TYPE_CSLG, LSMASH_BOX_PRECEDENCE_ISOM_CSLG ) DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_stsc, stsc, stbl, ISOM_BOX_TYPE_STSC, LSMASH_BOX_PRECEDENCE_ISOM_STSC ) DEFINE_SIMPLE_BOX_ADDER ( isom_add_stsz, stsz, stbl, ISOM_BOX_TYPE_STSZ, LSMASH_BOX_PRECEDENCE_ISOM_STSZ ) /* We don't create a list here. */ +DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_stz2, stz2, stbl, ISOM_BOX_TYPE_STZ2, LSMASH_BOX_PRECEDENCE_ISOM_STZ2 ) DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_stss, stss, stbl, ISOM_BOX_TYPE_STSS, LSMASH_BOX_PRECEDENCE_ISOM_STSS ) DEFINE_SIMPLE_LIST_BOX_ADDER( isom_add_stps, stps, stbl, QT_BOX_TYPE_STPS, LSMASH_BOX_PRECEDENCE_QTFF_STPS ) diff --git a/core/box.h b/core/box.h index d0350ca..5fd78cc 100644 --- a/core/box.h +++ b/core/box.h @@ -923,9 +923,9 @@ typedef struct 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 +/* Sample Size Box / Compact 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. + * The total number of samples in the media within the initial movie 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 @@ -936,11 +936,24 @@ typedef struct 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 */ + uint32_t sample_size; /* the default 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 media within the initial movie */ lsmash_entry_list_t *list; /* available if sample_size == 0 */ } isom_stsz_t; +typedef struct +{ + ISOM_FULLBOX_COMMON; + unsigned int reserved : 24; /* 0 */ + unsigned int field_size : 8; /* the size in bits of the entries in the following table + * It shall take the value 4, 8 or 16. If the value 4 is used, then each byte contains two values + * entry[i]<<4 + entry[i+1]; if the sizes do not fill an integral number of bytes, the last byte is + * padded with zero. */ + uint32_t sample_count; /* the number of entries in the following table */ + lsmash_entry_list_t *list; /* L-SMASH uses isom_stsz_entry_t for its internal processes. */ +} isom_stz2_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. @@ -1085,7 +1098,9 @@ typedef struct } isom_group_assignment_entry_t; /* Sample Table Box */ -typedef struct +typedef struct isom_stbl_tag isom_stbl_t; + +struct isom_stbl_tag { ISOM_BASEBOX_COMMON; isom_stsd_t *stsd; /* Sample Description Box */ @@ -1097,10 +1112,13 @@ typedef struct 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_stz2_t *stz2; /* Compact Sample Size Box */ isom_stco_t *stco; /* Chunk Offset Box */ lsmash_entry_list_t sgpd_list; /* Sample Group Description Boxes */ lsmash_entry_list_t sbgp_list; /* Sample To Group Boxes */ -} isom_stbl_t; + + int (*compress_sample_size_table)( isom_stbl_t *stbl ); /* Use 'stz2' instead of 'stsz' if possible. (write mode only) */ +}; /* Media Information Box */ typedef struct @@ -2055,6 +2073,7 @@ struct lsmash_root_tag #define ISOM_BOX_TYPE_STSH lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'h' ) ) #define ISOM_BOX_TYPE_STSS lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 's' ) ) #define ISOM_BOX_TYPE_STSZ lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 's', 'z' ) ) +#define ISOM_BOX_TYPE_STZ2 lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'z', '2' ) ) #define ISOM_BOX_TYPE_STTS lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 't', 's' ) ) #define ISOM_BOX_TYPE_STYP lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'y', 'p' ) ) #define ISOM_BOX_TYPE_STZ2 lsmash_form_iso_box_type( LSMASH_4CC( 's', 't', 'z', '2' ) ) @@ -2221,6 +2240,7 @@ struct lsmash_root_tag #define LSMASH_BOX_PRECEDENCE_ISOM_SDTP (LSMASH_BOX_PRECEDENCE_N - 12 * LSMASH_BOX_PRECEDENCE_S) #define LSMASH_BOX_PRECEDENCE_ISOM_STSC (LSMASH_BOX_PRECEDENCE_N - 14 * LSMASH_BOX_PRECEDENCE_S) #define LSMASH_BOX_PRECEDENCE_ISOM_STSZ (LSMASH_BOX_PRECEDENCE_N - 16 * LSMASH_BOX_PRECEDENCE_S) +#define LSMASH_BOX_PRECEDENCE_ISOM_STZ2 (LSMASH_BOX_PRECEDENCE_N - 16 * LSMASH_BOX_PRECEDENCE_S) #define LSMASH_BOX_PRECEDENCE_ISOM_STCO (LSMASH_BOX_PRECEDENCE_N - 18 * LSMASH_BOX_PRECEDENCE_S) #define LSMASH_BOX_PRECEDENCE_ISOM_CO64 (LSMASH_BOX_PRECEDENCE_N - 18 * LSMASH_BOX_PRECEDENCE_S) #define LSMASH_BOX_PRECEDENCE_ISOM_SGPD (LSMASH_BOX_PRECEDENCE_N - 20 * LSMASH_BOX_PRECEDENCE_S) @@ -2536,6 +2556,11 @@ int isom_append_sample_by_type int (*func_append_sample)( void *, lsmash_sample_t *, isom_sample_entry_t * ) ); +uint32_t isom_get_first_sample_size +( + isom_stbl_t *stbl +); + int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type ); int isom_group_random_access( isom_box_t *parent, lsmash_sample_t *sample ); int isom_group_roll_recovery( isom_box_t *parent, lsmash_sample_t *sample ); @@ -2609,6 +2634,7 @@ isom_ctts_t *isom_add_ctts( isom_stbl_t *stbl ); isom_cslg_t *isom_add_cslg( isom_stbl_t *stbl ); isom_stsc_t *isom_add_stsc( isom_stbl_t *stbl ); isom_stsz_t *isom_add_stsz( isom_stbl_t *stbl ); +isom_stz2_t *isom_add_stz2( isom_stbl_t *stbl ); isom_stss_t *isom_add_stss( isom_stbl_t *stbl ); isom_stps_t *isom_add_stps( isom_stbl_t *stbl ); isom_sdtp_t *isom_add_sdtp( isom_box_t *parent ); diff --git a/core/file.c b/core/file.c index cf509a7..c6195f3 100644 --- a/core/file.c +++ b/core/file.c @@ -230,7 +230,7 @@ int isom_check_mandatory_boxes || !trak->mdia->minf->dinf->dref || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd - || !trak->mdia->minf->stbl->stsz + || (!trak->mdia->minf->stbl->stsz && !trak->mdia->minf->stbl->stz2) || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stco ) diff --git a/core/fragment.c b/core/fragment.c index 0afbabc..55c3a9d 100644 --- a/core/fragment.c +++ b/core/fragment.c @@ -416,10 +416,12 @@ static int isom_create_fragment_overall_default_settings( lsmash_file_t *file ) || !trak->mdia->minf->stbl ) return LSMASH_ERR_NAMELESS; isom_stbl_t *stbl = trak->mdia->minf->stbl; - if( !stbl->stts || !stbl->stts->list - || !stbl->stsz + if( !stbl->stts + || !stbl->stts->list || (stbl->stts->list->tail && !stbl->stts->list->tail->data) - || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) ) + || (!stbl->stsz && !stbl->stz2) + || (stbl->stsz && stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) + || (stbl->stz2 && stbl->stz2->list && stbl->stz2->list->head && !stbl->stz2->list->head->data)) return LSMASH_ERR_NAMELESS; isom_trex_t *trex = isom_add_trex( file->moov->mvex ); if( !trex ) @@ -432,9 +434,7 @@ static int isom_create_fragment_overall_default_settings( lsmash_file_t *file ) 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; + trex->default_sample_size = isom_get_first_sample_size( stbl ); if( stbl->sdtp && stbl->sdtp->list ) { @@ -530,6 +530,10 @@ static int isom_finish_fragment_initial_movie( lsmash_file_t *file ) isom_stbl_t *stbl = trak->mdia->minf->stbl; if( isom_get_sample_count( trak ) ) { + /* Compress sample size table. */ + if( stbl->compress_sample_size_table + && (ret = stbl->compress_sample_size_table( stbl )) < 0 ) + return ret; /* Add stss box if any samples aren't sync sample. */ if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) ) return -1; diff --git a/core/isom.c b/core/isom.c index 994cfc9..1c0a5b8 100644 --- a/core/isom.c +++ b/core/isom.c @@ -386,7 +386,7 @@ static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size ) /* retrieve initial sample_size */ if( stsz->sample_count == 0 ) stsz->sample_size = entry_size; - /* if it seems constant access_unit size at present, update sample_count only */ + /* if it seems constant sample size at present, update sample_count only */ if( !stsz->list && stsz->sample_size == entry_size ) { ++ stsz->sample_count; @@ -696,10 +696,14 @@ uint32_t isom_get_sample_count( isom_trak_t *trak ) if( !trak || !trak->mdia || !trak->mdia->minf - || !trak->mdia->minf->stbl - || !trak->mdia->minf->stbl->stsz ) + || !trak->mdia->minf->stbl ) + return 0; + if( trak->mdia->minf->stbl->stsz ) + return trak->mdia->minf->stbl->stsz->sample_count; + else if( trak->mdia->minf->stbl->stz2 ) + return trak->mdia->minf->stbl->stz2->sample_count; + else return 0; - return trak->mdia->minf->stbl->stsz->sample_count; } static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number ) @@ -1067,8 +1071,9 @@ static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *bufferSizeDB, uint32_t *maxBitrate, uint32_t *avgBitrate, uint32_t sample_description_index ) { - isom_stsz_t *stsz = mdia->minf->stbl->stsz; - lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL; + isom_stsz_t *stsz = mdia->minf->stbl->stsz; + lsmash_entry_list_t *stsz_list = stsz ? stsz->list : mdia->minf->stbl->stz2->list; + lsmash_entry_t *stsz_entry = stsz_list ? stsz_list->head : NULL; lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head; lsmash_entry_t *stsc_entry = NULL; lsmash_entry_t *next_stsc_entry = mdia->minf->stbl->stsc->list->head; @@ -1083,6 +1088,7 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff uint32_t chunk_number = 0; uint32_t sample_number_in_stts = 1; uint32_t sample_number_in_chunk = 1; + uint32_t constant_sample_size = stsz ? stsz->sample_size : 0; *bufferSizeDB = 0; *maxBitrate = 0; *avgBitrate = 0; @@ -1140,7 +1146,7 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk; for( uint32_t i = 0; i < number_of_skips; i++ ) { - if( stsz->list ) + if( stsz_list ) { if( !stsz_entry ) break; @@ -1153,7 +1159,7 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff &stts_entry )) < 0 ) return err; } - if( (stsz->list && !stsz_entry) || !stts_entry ) + if( (stsz_list && !stsz_entry) || !stts_entry ) break; chunk_number = stsc_data->first_chunk; } @@ -1163,7 +1169,7 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff ++sample_number_in_chunk; /* Get current sample's size. */ uint32_t size; - if( stsz->list ) + if( stsz_list ) { if( !stsz_entry ) break; @@ -1174,7 +1180,7 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff stsz_entry = stsz_entry->next; } else - size = stsz->sample_size; + size = constant_sample_size; /* Get current sample's DTS. */ if( stts_data ) dts += stts_data->sample_delta; @@ -1206,6 +1212,39 @@ static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *buff return 0; } +static int isom_is_variable_size( isom_stbl_t *stbl ) +{ + if( (stbl->stz2 && stbl->stz2->sample_count > 1) + || (stbl->stsz && stbl->stsz->sample_count > 1 && stbl->stsz->sample_size == 0) ) + return 1; + else + return 0; +} + +uint32_t isom_get_first_sample_size( isom_stbl_t *stbl ) +{ + if( stbl->stsz ) + { + /* 'stsz' */ + if( stbl->stsz->sample_size ) + return stbl->stsz->sample_size; + else if( stbl->stsz->list && stbl->stsz->list->head && stbl->stsz->list->head->data ) + return ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size; + else + return 0; + } + else if( stbl->stz2 ) + { + /* stz2 */ + if( stbl->stz2->list && stbl->stz2->list->head && stbl->stz2->list->head->data ) + return ((isom_stsz_entry_t *)stbl->stz2->list->head->data)->entry_size; + else + return 0; + } + else + return 0; +} + int isom_update_bitrate_description( isom_mdia_t *mdia ) { if( !mdia @@ -1215,7 +1254,7 @@ int isom_update_bitrate_description( isom_mdia_t *mdia ) return LSMASH_ERR_INVALID_DATA; isom_stbl_t *stbl = mdia->minf->stbl; if( !stbl->stsd - || !stbl->stsz + || (!stbl->stsz && !stbl->stz2) || !stbl->stsc || !stbl->stsc->list || !stbl->stts || !stbl->stts->list ) return LSMASH_ERR_INVALID_DATA; @@ -1379,7 +1418,7 @@ int isom_update_bitrate_description( isom_mdia_t *mdia ) return LSMASH_ERR_INVALID_DATA; if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) return err; - if( !stbl->stsz->list ) + if( !isom_is_variable_size( stbl ) ) maxBitrate = avgBitrate; uint8_t *exdata = ext->binary + 12; LSMASH_SET_BE32( &exdata[0], maxBitrate ); @@ -1392,14 +1431,14 @@ int isom_update_bitrate_description( isom_mdia_t *mdia ) if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 10) ) return LSMASH_ERR_INVALID_DATA; uint16_t bitrate; - if( stbl->stsz->list ) + if( isom_is_variable_size( stbl ) ) { if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 ) return err; bitrate = maxBitrate / 1000; /* Use maximum bitrate if VBR. */ } else - bitrate = stbl->stsz->sample_size * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */ + bitrate = isom_get_first_sample_size( stbl ) * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */ uint8_t *exdata = ext->binary + 8; exdata[0] = (bitrate >> 5) & 0xff; exdata[1] = (bitrate & 0x1f) << 3; @@ -2252,6 +2291,44 @@ static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type gr return 0; } +static int isom_compress_sample_size_table( isom_stbl_t *stbl ) +{ + if( stbl->stsz && isom_is_variable_size( stbl ) ) + { + int max_num_bits = 0; + for( lsmash_entry_t *entry = stbl->stsz->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_INVALID_DATA; + int num_bits; + for( num_bits = 1; data->entry_size >> num_bits; num_bits++ ); + if( max_num_bits < num_bits ) + { + max_num_bits = num_bits; + if( max_num_bits > 16 ) + return 0; /* not compressible */ + } + } + if( max_num_bits <= 16 && isom_add_stz2( stbl ) ) + { + /* The sample size table can be compressed by using 'stz2'. */ + isom_stsz_t *stsz = stbl->stsz; + isom_stz2_t *stz2 = stbl->stz2; + stz2->sample_count = stsz->sample_count; + if( max_num_bits <= 4 ) + stz2->field_size = 4; + else if( max_num_bits <= 8 ) + stz2->field_size = 8; + else + stz2->field_size = 16; + lsmash_move_entries( stz2->list, stsz->list ); + isom_remove_box_by_itself( stsz ); + } + } + return 0; +} + void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param ) { memset( param, 0, sizeof(lsmash_media_parameters_t) ); @@ -2286,6 +2363,8 @@ int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_ if( (file->max_isom_version >= 6) && param->rap_grouping && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP )) < 0 ) return err; + if( param->compact_sample_size_table ) + trak->mdia->minf->stbl->compress_sample_size_table = isom_compress_sample_size_table; return 0; } @@ -2391,6 +2470,8 @@ int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_ param->data_handler_name = NULL; memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) ); } + param->compact_sample_size_table = !!stbl->stz2; + param->reserved[0] = param->reserved[1] = param->reserved[2] = 0; return 0; } @@ -2746,8 +2827,12 @@ int lsmash_finish_movie if( (err = lsmash_create_explicit_timeline_map( root, track_ID, edit )) < 0 ) return err; } - /* Add stss box if any samples aren't sync sample. */ isom_stbl_t *stbl = trak->mdia->minf->stbl; + /* Compress sample size table. */ + if( stbl->compress_sample_size_table + && (err = stbl->compress_sample_size_table( stbl )) < 0 ) + return err; + /* Add stss box if any samples aren't sync sample. */ if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) ) return LSMASH_ERR_NAMELESS; if( (err = isom_update_tkhd_duration( trak )) < 0 @@ -2843,7 +2928,7 @@ int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32 || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd - || !trak->mdia->minf->stbl->stsz + || (!trak->mdia->minf->stbl->stsz && !trak->mdia->minf->stbl->stz2) || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list ) return LSMASH_ERR_NAMELESS; @@ -3203,7 +3288,7 @@ static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t ct return err; ctts = stbl->ctts; isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data; - uint32_t sample_count = stbl->stsz->sample_count; + uint32_t sample_count = stbl->stsz ? stbl->stsz->sample_count : stbl->stz2->sample_count; if( sample_count != 1 ) { data->sample_count = sample_count - 1; diff --git a/core/print.c b/core/print.c index 5d2aacf..e8a7d9c 100644 --- a/core/print.c +++ b/core/print.c @@ -1531,6 +1531,23 @@ static int isom_print_stsz( FILE *fp, lsmash_file_t *file, isom_box_t *box, int return 0; } +static int isom_print_stz2( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) +{ + isom_stz2_t *stz2 = (isom_stz2_t *)box; + int indent = level; + uint32_t i = 0; + isom_print_box_common( fp, indent++, box, "Compact Sample Size Box" ); + lsmash_ifprintf( fp, indent, "reserved = 0x%06"PRIx32"\n", stz2->reserved ); + lsmash_ifprintf( fp, indent, "field_size = %"PRIu8"\n", stz2->field_size ); + lsmash_ifprintf( fp, indent, "sample_count = %"PRIu32"\n", stz2->sample_count ); + for( lsmash_entry_t *entry = stz2->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + lsmash_ifprintf( fp, indent, "entry_size[%"PRIu32"] = %"PRIu32"\n", i++, data->entry_size ); + } + return 0; +} + static int isom_print_stco( FILE *fp, lsmash_file_t *file, isom_box_t *box, int level ) { if( !((isom_stco_t *)box)->list ) @@ -2621,6 +2638,7 @@ static isom_print_box_t isom_select_print_func( isom_box_t *box ) ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SDTP, isom_print_sdtp ); ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSC, isom_print_stsc ); ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STSZ, isom_print_stsz ); + ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STZ2, isom_print_stz2 ); ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_STCO, isom_print_stco ); ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_CO64, isom_print_stco ); ADD_PRINT_BOX_TABLE_ELEMENT( ISOM_BOX_TYPE_SGPD, isom_print_sgpd ); diff --git a/core/read.c b/core/read.c index 6575731..cd0b56f 100644 --- a/core/read.c +++ b/core/read.c @@ -1765,6 +1765,74 @@ static int isom_read_stsz( lsmash_file_t *file, isom_box_t *box, isom_box_t *par return isom_read_leaf_box_common_last_process( file, box, level, stsz ); } +static int isom_read_stz2( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) +{ + if( !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) || ((isom_stbl_t *)parent)->stz2 ) + return isom_read_unknown_box( file, box, parent, level ); + ADD_BOX( stz2, isom_stbl_t ); + lsmash_bs_t *bs = file->bs; + uint32_t temp32 = lsmash_bs_get_be32( bs ); + stz2->reserved = temp32 >> 24; + stz2->field_size = temp32 & 0xff; + stz2->sample_count = lsmash_bs_get_be32( bs ); + uint64_t pos = lsmash_bs_count( bs ); + if( pos < box->size ) + { + if( stz2->field_size == 16 || stz2->field_size == 8 ) + { + uint64_t (*bs_get_funcs[2])( lsmash_bs_t * ) = + { + lsmash_bs_get_byte_to_64, + lsmash_bs_get_be16_to_64 + }; + uint64_t (*bs_get_entry_size)( lsmash_bs_t * ) = bs_get_funcs[ stz2->field_size == 16 ? 1 : 0 ]; + for( ; pos < box->size && stz2->list->entry_count < stz2->sample_count; pos = lsmash_bs_count( bs ) ) + { + isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stz2->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + data->entry_size = bs_get_entry_size( bs ); + } + } + else if( stz2->field_size == 4 ) + { + int parity = 1; + uint8_t temp8; + while( pos < box->size && stz2->list->entry_count < stz2->sample_count ) + { + isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) ); + if( !data ) + return LSMASH_ERR_MEMORY_ALLOC; + if( lsmash_add_entry( stz2->list, data ) < 0 ) + { + lsmash_free( data ); + return LSMASH_ERR_MEMORY_ALLOC; + } + /* Read a byte by two entries. */ + if( parity ) + { + temp8 = lsmash_bs_get_byte( bs ); + data->entry_size = (temp8 >> 4) & 0xf; + } + else + { + pos = lsmash_bs_count( bs ); + data->entry_size = temp8 & 0xf; + } + parity ^= 1; + } + } + else + return LSMASH_ERR_INVALID_DATA; + } + return isom_read_leaf_box_common_last_process( file, box, level, stz2 ); +} + static int isom_read_stco( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, int level ) { if( !lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) || ((isom_stbl_t *)parent)->stco ) @@ -2793,6 +2861,7 @@ int isom_read_box( lsmash_file_t *file, isom_box_t *box, isom_box_t *parent, uin ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SDTP, lsmash_form_iso_box_type, isom_read_sdtp ); ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSC, lsmash_form_iso_box_type, isom_read_stsc ); ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSZ, lsmash_form_iso_box_type, isom_read_stsz ); + ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STZ2, lsmash_form_iso_box_type, isom_read_stz2 ); ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_STCO, lsmash_form_iso_box_type, isom_read_stco ); ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_CO64, lsmash_form_iso_box_type, isom_read_stco ); ADD_BOX_READER_TABLE_ELEMENT( ISOM_BOX_TYPE_SGPD, lsmash_form_iso_box_type, isom_read_sgpd ); diff --git a/core/timeline.c b/core/timeline.c index e13259c..6740de4 100644 --- a/core/timeline.c +++ b/core/timeline.c @@ -772,7 +772,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stco || !trak->mdia->minf->stbl->stsd - || !trak->mdia->minf->stbl->stsz ) + || (!trak->mdia->minf->stbl->stsz && !trak->mdia->minf->stbl->stz2) ) return LSMASH_ERR_INVALID_DATA; /* Create a timeline list if it doesn't exist. */ if( !file->timeline ) @@ -802,6 +802,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) isom_sdtp_t *sdtp = stbl->sdtp; isom_stsc_t *stsc = stbl->stsc; isom_stsz_t *stsz = stbl->stsz; + isom_stz2_t *stz2 = stbl->stz2; isom_stco_t *stco = stbl->stco; isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP ); isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP ); @@ -813,7 +814,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL; lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL; lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL; - lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL; + lsmash_entry_t *stsz_entry = stsz ? (stsz->list ? stsz->list->head : NULL) : (stz2->list ? stz2->list->head : NULL); lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL; lsmash_entry_t *stco_entry = stco->list ? stco->list->head : NULL; lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL; @@ -847,6 +848,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset : 0; + uint32_t initial_movie_sample_count = stsz ? stsz->sample_count : stz2->sample_count; uint32_t samples_per_packet; uint32_t constant_sample_size; if( is_qt_fixed_comp_audio ) @@ -854,7 +856,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) else { samples_per_packet = 1; - constant_sample_size = stsz->sample_size; + constant_sample_size = stsz ? stsz->sample_size : 0; } uint32_t sample_number = samples_per_packet; uint32_t sample_number_in_chunk = samples_per_packet; @@ -903,7 +905,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) uint32_t last_duration = UINT32_MAX; uint32_t packet_number = 1; isom_lpcm_bunch_t bunch = { 0 }; - while( sample_number <= stsz->sample_count ) + while( sample_number <= initial_movie_sample_count ) { isom_sample_info_t info = { 0 }; /* Get sample duration and sample offset. */ @@ -1077,7 +1079,7 @@ int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID ) else { samples_per_packet = 1; - constant_sample_size = stsz->sample_size; + constant_sample_size = stsz ? stsz->sample_size : 0; } /* Reference media data. */ dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description ? description->data_reference_index : 0 ); diff --git a/core/write.c b/core/write.c index 33d4e10..a61556e 100644 --- a/core/write.c +++ b/core/write.c @@ -771,6 +771,48 @@ static int isom_write_stsz( lsmash_bs_t *bs, isom_box_t *box ) return 0; } +static int isom_write_stz2( lsmash_bs_t *bs, isom_box_t *box ) +{ + isom_stz2_t *stz2 = (isom_stz2_t *)box; + isom_bs_put_box_common( bs, stz2 ); + lsmash_bs_put_be32( bs, (stz2->reserved << 8) | stz2->field_size ); + lsmash_bs_put_be32( bs, stz2->sample_count ); + if( stz2->field_size == 16 ) + for( lsmash_entry_t *entry = stz2->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_NAMELESS; + assert( data->entry_size <= 0xffff ); + lsmash_bs_put_be16( bs, data->entry_size ); + } + else if( stz2->field_size == 8 ) + for( lsmash_entry_t *entry = stz2->list->head; entry; entry = entry->next ) + { + isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data; + if( !data ) + return LSMASH_ERR_NAMELESS; + assert( data->entry_size <= 0xff ); + lsmash_bs_put_byte( bs, data->entry_size ); + } + else if( stz2->field_size == 4 ) + { + isom_stsz_entry_t zero_padding = { .entry_size = 0 }; + for( lsmash_entry_t *entry = stz2->list->head; entry; entry = entry->next ? entry->next->next : entry->next ) + { + isom_stsz_entry_t *data_o = (isom_stsz_entry_t *)entry->data; + isom_stsz_entry_t *data_e = (isom_stsz_entry_t *)(entry->next ? entry->next->data : &zero_padding); + if( !data_o || !data_e ) + return LSMASH_ERR_NAMELESS; + assert( data_o->entry_size <= 0xf && data_e->entry_size <= 0xf ); + lsmash_bs_put_byte( bs, (data_o->entry_size << 4) | data_e->entry_size ); + } + } + else + return LSMASH_ERR_NAMELESS; + return 0; +} + static int isom_write_stss( lsmash_bs_t *bs, isom_box_t *box ) { isom_stss_t *stss = (isom_stss_t *)box; @@ -1557,6 +1599,7 @@ void isom_set_box_writer( isom_box_t *box ) ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SDTP, isom_write_sdtp ); ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSC, isom_write_stsc ); ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STSZ, isom_write_stsz ); + ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STZ2, isom_write_stz2 ); ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_STCO, isom_write_stco ); ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_CO64, isom_write_stco ); ADD_BOX_WRITER_TABLE_ELEMENT( ISOM_BOX_TYPE_SGPD, isom_write_sgpd ); diff --git a/lsmash.h b/lsmash.h index d3a24af..bfd4116 100644 --- a/lsmash.h +++ b/lsmash.h @@ -47,8 +47,8 @@ extern "C" { * Version ****************************************************************************/ #define LSMASH_VERSION_MAJOR 2 -#define LSMASH_VERSION_MINOR 9 -#define LSMASH_VERSION_MICRO 1 +#define LSMASH_VERSION_MINOR 10 +#define LSMASH_VERSION_MICRO 0 #define LSMASH_VERSION_INT( a, b, c ) (((a) << 16) | ((b) << 8) | (c)) @@ -1764,6 +1764,10 @@ typedef struct /* Any user shouldn't use the following parameters. */ PRIVATE char media_handler_name_shadow[256]; PRIVATE char data_handler_name_shadow[256]; + /* */ + uint8_t compact_sample_size_table; /* 1: Use compact size table if possible. (output/muxing mode) + * Compact sample size table is used. (input/demuxing mode) */ + uint8_t reserved[3]; } lsmash_media_parameters_t; typedef struct -- 2.11.4.GIT